Merge "Enable isolated process to use graphics allocator" into udc-dev
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 2e0c95a..66df0de 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -297,8 +297,18 @@
write /sys/kernel/debug/tracing/synthetic_events "rss_stat_throttled unsigned int mm_id; unsigned int curr; int member; long size"
# allow creating event triggers
- chmod 0666 /sys/kernel/debug/tracing/events/kmem/rss_stat/trigger
chmod 0666 /sys/kernel/tracing/events/kmem/rss_stat/trigger
+ chmod 0666 /sys/kernel/debug/tracing/events/kmem/rss_stat/trigger
+
+ # allow enabling rss_stat_throttled
+ chmod 0666 /sys/kernel/tracing/events/synthetic/rss_stat_throttled/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/synthetic/rss_stat_throttled/enable
+
+on late-init && property:ro.boot.fastboot.boottrace=enabled
+ setprop debug.atrace.tags.enableflags 802922
+ setprop persist.traced.enable 0
+ write /sys/kernel/debug/tracing/tracing_on 1
+ write /sys/kernel/tracing/tracing_on 1
on late-init && property:ro.boot.fastboot.boottrace=enabled
setprop debug.atrace.tags.enableflags 802922
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 1c52792..a1be542 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -38,6 +38,7 @@
#include <binder/IBinder.h>
#include <binder/Parcelable.h>
#include <input/Input.h>
+#include <input/InputVerifier.h>
#include <sys/stat.h>
#include <ui/Transform.h>
#include <utils/BitSet.h>
@@ -444,6 +445,7 @@
private:
std::shared_ptr<InputChannel> mChannel;
+ InputVerifier mInputVerifier;
};
/*
diff --git a/include/input/InputVerifier.h b/include/input/InputVerifier.h
new file mode 100644
index 0000000..d4589f5
--- /dev/null
+++ b/include/input/InputVerifier.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <input/Input.h>
+#include <map>
+
+namespace android {
+
+/*
+ * Crash if the provided touch stream is inconsistent.
+ *
+ * TODO(b/211379801): Add support for hover events:
+ * - No hover move without enter
+ * - No touching pointers when hover enter
+ * - No hovering pointers when touching
+ * - Only 1 hovering pointer max
+ */
+class InputVerifier {
+public:
+ InputVerifier(const std::string& name);
+
+ void processMovement(int32_t deviceId, int32_t action, uint32_t pointerCount,
+ const PointerProperties* pointerProperties,
+ const PointerCoords* pointerCoords, int32_t flags);
+
+private:
+ const std::string mName;
+ std::map<int32_t /*deviceId*/, std::bitset<MAX_POINTER_ID + 1>> mTouchingPointerIdsByDevice;
+ void ensureTouchingPointersMatch(int32_t deviceId, uint32_t pointerCount,
+ const PointerProperties* pointerProperties,
+ const char* action) const;
+};
+
+} // namespace android
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index c4c8ffb..6a354b4 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -74,9 +74,6 @@
name: "libbinder_common_defaults",
host_supported: true,
- // for vndbinder and binderRpcTest
- vendor_available: true,
-
srcs: [
"Binder.cpp",
"BpBinder.cpp",
@@ -200,7 +197,6 @@
cc_library_headers {
name: "trusty_mock_headers",
- vendor_available: true,
host_supported: true,
export_include_dirs: [
@@ -215,7 +211,6 @@
cc_defaults {
name: "trusty_mock_defaults",
- vendor_available: true,
host_supported: true,
header_libs: [
@@ -309,6 +304,8 @@
version_script: "libbinder.map",
+ // for vndbinder
+ vendor_available: true,
vndk: {
enabled: true,
},
@@ -467,7 +464,6 @@
cc_library_static {
name: "libbinder_tls_static",
defaults: ["libbinder_tls_defaults"],
- vendor_available: true,
visibility: [
":__subpackages__",
],
@@ -547,8 +543,10 @@
// Do not expand the visibility.
visibility: [
":__subpackages__",
- "//packages/modules/Virtualization:__subpackages__",
+ "//packages/modules/Virtualization/javalib/jni",
+ "//packages/modules/Virtualization/vm_payload",
"//device/google/cuttlefish/shared/minidroid:__subpackages__",
+ "//system/software_defined_vehicle:__subpackages__",
],
}
diff --git a/libs/binder/OWNERS b/libs/binder/OWNERS
index f954e74..bb17683 100644
--- a/libs/binder/OWNERS
+++ b/libs/binder/OWNERS
@@ -1,6 +1,4 @@
# Bug component: 32456
-ctate@google.com
-hackbod@google.com
maco@google.com
smoreland@google.com
tkjos@google.com
diff --git a/libs/binder/rust/rpcbinder/Android.bp b/libs/binder/rust/rpcbinder/Android.bp
index da9797b..0067a20 100644
--- a/libs/binder/rust/rpcbinder/Android.bp
+++ b/libs/binder/rust/rpcbinder/Android.bp
@@ -26,6 +26,7 @@
visibility: [
"//device/google/cuttlefish/shared/minidroid/sample",
"//packages/modules/Virtualization:__subpackages__",
+ "//system/software_defined_vehicle:__subpackages__",
],
apex_available: [
"//apex_available:platform",
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 2a4fc48..0f0d64a 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -138,7 +138,6 @@
aidl_interface {
name: "binderRpcTestIface",
- vendor_available: true,
host_supported: true,
unstable: true,
srcs: [
@@ -159,7 +158,6 @@
cc_library_static {
name: "libbinder_tls_test_utils",
- vendor_available: true,
host_supported: true,
target: {
darwin: {
@@ -213,7 +211,6 @@
defaults: [
"binderRpcTest_common_defaults",
],
- vendor_available: true,
gtest: false,
auto_gen_config: false,
srcs: [
@@ -224,18 +221,10 @@
cc_defaults {
name: "binderRpcTest_defaults",
- vendor_available: true,
target: {
android: {
test_suites: ["vts"],
},
-
- vendor: {
- shared_libs: [
- "libbinder_trusty",
- "libtrusty",
- ],
- },
},
defaults: [
"binderRpcTest_common_defaults",
@@ -397,11 +386,6 @@
cc_test {
name: "binderRpcTest",
- // TODO(b/269799024)
- test_options: {
- unit_test: false,
- },
-
defaults: [
"binderRpcTest_defaults",
"binderRpcTest_shared_defaults",
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 9d82c14..cf8b13f 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -486,6 +486,17 @@
mSyncedFrameNumbers.erase(callbackId.framenumber);
}
+static ui::Size getBufferSize(const BufferItem& item) {
+ uint32_t bufWidth = item.mGraphicBuffer->getWidth();
+ uint32_t bufHeight = item.mGraphicBuffer->getHeight();
+
+ // Take the buffer's orientation into account
+ if (item.mTransform & ui::Transform::ROT_90) {
+ std::swap(bufWidth, bufHeight);
+ }
+ return ui::Size(bufWidth, bufHeight);
+}
+
status_t BLASTBufferQueue::acquireNextBufferLocked(
const std::optional<SurfaceComposerClient::Transaction*> transaction) {
// Check if we have frames available and we have not acquired the maximum number of buffers.
@@ -563,7 +574,12 @@
// Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback.
incStrong((void*)transactionCallbackThunk);
- mSize = mRequestedSize;
+ // Only update mSize for destination bounds if the incoming buffer matches the requested size.
+ // Otherwise, it could cause stretching since the destination bounds will update before the
+ // buffer with the new size is acquired.
+ if (mRequestedSize == getBufferSize(bufferItem)) {
+ mSize = mRequestedSize;
+ }
Rect crop = computeCrop(bufferItem);
mLastBufferInfo.update(true /* hasBuffer */, bufferItem.mGraphicBuffer->getWidth(),
bufferItem.mGraphicBuffer->getHeight(), bufferItem.mTransform,
@@ -834,14 +850,7 @@
return false;
}
- uint32_t bufWidth = item.mGraphicBuffer->getWidth();
- uint32_t bufHeight = item.mGraphicBuffer->getHeight();
-
- // Take the buffer's orientation into account
- if (item.mTransform & ui::Transform::ROT_90) {
- std::swap(bufWidth, bufHeight);
- }
- ui::Size bufferSize(bufWidth, bufHeight);
+ ui::Size bufferSize = getBufferSize(item);
if (mRequestedSize != mSize && mRequestedSize == bufferSize) {
return false;
}
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 3809b6d..48cb72c 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -47,6 +47,7 @@
"Input.cpp",
"InputDevice.cpp",
"InputEventLabels.cpp",
+ "InputVerifier.cpp",
"Keyboard.cpp",
"KeyCharacterMap.cpp",
"KeyLayoutMap.cpp",
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 9f0a314..bdbd708 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -5,20 +5,6 @@
//
#define LOG_TAG "InputTransport"
-//#define LOG_NDEBUG 0
-
-// Log debug messages about channel messages (send message, receive message)
-#define DEBUG_CHANNEL_MESSAGES 0
-
-// Log debug messages whenever InputChannel objects are created/destroyed
-static constexpr bool DEBUG_CHANNEL_LIFECYCLE = false;
-
-// Log debug messages about transport actions
-static constexpr bool DEBUG_TRANSPORT_ACTIONS = false;
-
-// Log debug messages about touch event resampling
-#define DEBUG_RESAMPLING 0
-
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
@@ -27,6 +13,7 @@
#include <sys/types.h>
#include <unistd.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <binder/Parcel.h>
#include <cutils/properties.h>
@@ -36,6 +23,63 @@
#include <input/InputTransport.h>
+namespace {
+
+/**
+ * Log debug messages about channel messages (send message, receive message).
+ * Enable this via "adb shell setprop log.tag.InputTransportMessages DEBUG"
+ * (requires restart)
+ */
+const bool DEBUG_CHANNEL_MESSAGES =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Messages", ANDROID_LOG_INFO);
+
+/**
+ * Log debug messages whenever InputChannel objects are created/destroyed.
+ * Enable this via "adb shell setprop log.tag.InputTransportLifecycle DEBUG"
+ * (requires restart)
+ */
+const bool DEBUG_CHANNEL_LIFECYCLE =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Lifecycle", ANDROID_LOG_INFO);
+
+/**
+ * Log debug messages relating to the consumer end of the transport channel.
+ * Enable this via "adb shell setprop log.tag.InputTransportConsumer DEBUG" (requires restart)
+ */
+
+const bool DEBUG_TRANSPORT_CONSUMER =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Consumer", ANDROID_LOG_INFO);
+
+const bool IS_DEBUGGABLE_BUILD =
+#if defined(__ANDROID__)
+ android::base::GetBoolProperty("ro.debuggable", false);
+#else
+ true;
+#endif
+
+/**
+ * Log debug messages relating to the producer end of the transport channel.
+ * Enable this via "adb shell setprop log.tag.InputTransportPublisher DEBUG".
+ * This requires a restart on non-debuggable (e.g. user) builds, but should take effect immediately
+ * on debuggable builds (e.g. userdebug).
+ */
+bool debugTransportPublisher() {
+ if (!IS_DEBUGGABLE_BUILD) {
+ static const bool DEBUG_TRANSPORT_PUBLISHER =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Publisher", ANDROID_LOG_INFO);
+ return DEBUG_TRANSPORT_PUBLISHER;
+ }
+ return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Publisher", ANDROID_LOG_INFO);
+}
+
+/**
+ * Log debug messages about touch event resampling.
+ * Enable this via "adb shell setprop log.tag.InputTransportResampling DEBUG" (requires restart)
+ */
+const bool DEBUG_RESAMPLING =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling", ANDROID_LOG_INFO);
+
+} // namespace
+
using android::base::StringPrintf;
namespace android {
@@ -76,6 +120,14 @@
*/
static const char* PROPERTY_RESAMPLING_ENABLED = "ro.input.resampling";
+/**
+ * Crash if the events that are getting sent to the InputPublisher are inconsistent.
+ * Enable this via "adb shell setprop log.tag.InputTransportVerifyEvents DEBUG"
+ */
+static bool verifyEvents() {
+ return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "VerifyEvents", ANDROID_LOG_INFO);
+}
+
template<typename T>
inline static T min(const T& a, const T& b) {
return a < b ? a : b;
@@ -132,7 +184,7 @@
return valid;
}
}
- ALOGE("Invalid message type: %" PRIu32, header.type);
+ ALOGE("Invalid message type: %s", ftl::enum_string(header.type).c_str());
return false;
}
@@ -322,15 +374,13 @@
InputChannel::InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token)
: mName(std::move(name)), mFd(std::move(fd)), mToken(std::move(token)) {
- if (DEBUG_CHANNEL_LIFECYCLE) {
- ALOGD("Input channel constructed: name='%s', fd=%d", getName().c_str(), getFd().get());
- }
+ ALOGD_IF(DEBUG_CHANNEL_LIFECYCLE, "Input channel constructed: name='%s', fd=%d",
+ getName().c_str(), getFd().get());
}
InputChannel::~InputChannel() {
- if (DEBUG_CHANNEL_LIFECYCLE) {
- ALOGD("Input channel destroyed: name='%s', fd=%d", getName().c_str(), getFd().get());
- }
+ ALOGD_IF(DEBUG_CHANNEL_LIFECYCLE, "Input channel destroyed: name='%s', fd=%d",
+ getName().c_str(), getFd().get());
}
status_t InputChannel::openInputChannelPair(const std::string& name,
@@ -375,10 +425,8 @@
if (nWrite < 0) {
int error = errno;
-#if DEBUG_CHANNEL_MESSAGES
- ALOGD("channel '%s' ~ error sending message of type %d, %s", mName.c_str(),
- msg->header.type, strerror(error));
-#endif
+ ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ error sending message of type %s, %s",
+ mName.c_str(), ftl::enum_string(msg->header.type).c_str(), strerror(error));
if (error == EAGAIN || error == EWOULDBLOCK) {
return WOULD_BLOCK;
}
@@ -389,16 +437,14 @@
}
if (size_t(nWrite) != msgLength) {
-#if DEBUG_CHANNEL_MESSAGES
- ALOGD("channel '%s' ~ error sending message type %d, send was incomplete",
- mName.c_str(), msg->header.type);
-#endif
+ ALOGD_IF(DEBUG_CHANNEL_MESSAGES,
+ "channel '%s' ~ error sending message type %s, send was incomplete", mName.c_str(),
+ ftl::enum_string(msg->header.type).c_str());
return DEAD_OBJECT;
}
-#if DEBUG_CHANNEL_MESSAGES
- ALOGD("channel '%s' ~ sent message of type %d", mName.c_str(), msg->header.type);
-#endif
+ ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ sent message of type %s", mName.c_str(),
+ ftl::enum_string(msg->header.type).c_str());
return OK;
}
@@ -410,9 +456,8 @@
if (nRead < 0) {
int error = errno;
-#if DEBUG_CHANNEL_MESSAGES
- ALOGD("channel '%s' ~ receive message failed, errno=%d", mName.c_str(), errno);
-#endif
+ ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ receive message failed, errno=%d",
+ mName.c_str(), errno);
if (error == EAGAIN || error == EWOULDBLOCK) {
return WOULD_BLOCK;
}
@@ -423,9 +468,8 @@
}
if (nRead == 0) { // check for EOF
-#if DEBUG_CHANNEL_MESSAGES
- ALOGD("channel '%s' ~ receive message failed because peer was closed", mName.c_str());
-#endif
+ ALOGD_IF(DEBUG_CHANNEL_MESSAGES,
+ "channel '%s' ~ receive message failed because peer was closed", mName.c_str());
return DEAD_OBJECT;
}
@@ -434,9 +478,8 @@
return BAD_VALUE;
}
-#if DEBUG_CHANNEL_MESSAGES
- ALOGD("channel '%s' ~ received message of type %d", mName.c_str(), msg->header.type);
-#endif
+ ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ received message of type %s", mName.c_str(),
+ ftl::enum_string(msg->header.type).c_str());
return OK;
}
@@ -492,7 +535,8 @@
// --- InputPublisher ---
-InputPublisher::InputPublisher(const std::shared_ptr<InputChannel>& channel) : mChannel(channel) {}
+InputPublisher::InputPublisher(const std::shared_ptr<InputChannel>& channel)
+ : mChannel(channel), mInputVerifier(channel->getName()) {}
InputPublisher::~InputPublisher() {
}
@@ -504,17 +548,19 @@
int32_t metaState, int32_t repeatCount, nsecs_t downTime,
nsecs_t eventTime) {
if (ATRACE_ENABLED()) {
- std::string message = StringPrintf("publishKeyEvent(inputChannel=%s, keyCode=%" PRId32 ")",
- mChannel->getName().c_str(), keyCode);
+ std::string message =
+ StringPrintf("publishKeyEvent(inputChannel=%s, action=%s, keyCode=%s)",
+ mChannel->getName().c_str(), KeyEvent::actionToString(action),
+ KeyEvent::getLabel(keyCode));
ATRACE_NAME(message.c_str());
}
- if (DEBUG_TRANSPORT_ACTIONS) {
- ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, "
- "action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d,"
- "downTime=%" PRId64 ", eventTime=%" PRId64,
- mChannel->getName().c_str(), seq, deviceId, source, action, flags, keyCode, scanCode,
- metaState, repeatCount, downTime, eventTime);
- }
+ ALOGD_IF(debugTransportPublisher(),
+ "channel '%s' publisher ~ %s: seq=%u, deviceId=%d, source=%s, "
+ "action=%s, flags=0x%x, keyCode=%s, scanCode=%d, metaState=0x%x, repeatCount=%d,"
+ "downTime=%" PRId64 ", eventTime=%" PRId64,
+ mChannel->getName().c_str(), __func__, seq, deviceId,
+ inputEventSourceToString(source).c_str(), KeyEvent::actionToString(action), flags,
+ KeyEvent::getLabel(keyCode), scanCode, metaState, repeatCount, downTime, eventTime);
if (!seq) {
ALOGE("Attempted to publish a key event with sequence number 0.");
@@ -550,24 +596,29 @@
uint32_t pointerCount, const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords) {
if (ATRACE_ENABLED()) {
- std::string message = StringPrintf(
- "publishMotionEvent(inputChannel=%s, action=%" PRId32 ")",
- mChannel->getName().c_str(), action);
+ std::string message = StringPrintf("publishMotionEvent(inputChannel=%s, action=%s)",
+ mChannel->getName().c_str(),
+ MotionEvent::actionToString(action).c_str());
ATRACE_NAME(message.c_str());
}
- if (DEBUG_TRANSPORT_ACTIONS) {
+ if (verifyEvents()) {
+ mInputVerifier.processMovement(deviceId, action, pointerCount, pointerProperties,
+ pointerCoords, flags);
+ }
+ if (debugTransportPublisher()) {
std::string transformString;
transform.dump(transformString, "transform", " ");
- ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, "
+ ALOGD("channel '%s' publisher ~ %s: seq=%u, deviceId=%d, source=%s, "
"displayId=%" PRId32 ", "
- "action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, "
+ "action=%s, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, "
"metaState=0x%x, buttonState=0x%x, classification=%s,"
"xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", "
"pointerCount=%" PRIu32 " \n%s",
- mChannel->getName().c_str(), seq, deviceId, source, displayId, action, actionButton,
- flags, edgeFlags, metaState, buttonState,
- motionClassificationToString(classification), xPrecision, yPrecision, downTime,
- eventTime, pointerCount, transformString.c_str());
+ mChannel->getName().c_str(), __func__, seq, deviceId,
+ inputEventSourceToString(source).c_str(), displayId,
+ MotionEvent::actionToString(action).c_str(), actionButton, flags, edgeFlags,
+ metaState, buttonState, motionClassificationToString(classification), xPrecision,
+ yPrecision, downTime, eventTime, pointerCount, transformString.c_str());
}
if (!seq) {
@@ -629,6 +680,8 @@
mChannel->getName().c_str(), toString(hasFocus));
ATRACE_NAME(message.c_str());
}
+ ALOGD_IF(debugTransportPublisher(), "channel '%s' publisher ~ %s: seq=%u, hasFocus=%s",
+ mChannel->getName().c_str(), __func__, seq, toString(hasFocus));
InputMessage msg;
msg.header.type = InputMessage::Type::FOCUS;
@@ -646,6 +699,9 @@
mChannel->getName().c_str(), toString(pointerCaptureEnabled));
ATRACE_NAME(message.c_str());
}
+ ALOGD_IF(debugTransportPublisher(),
+ "channel '%s' publisher ~ %s: seq=%u, pointerCaptureEnabled=%s",
+ mChannel->getName().c_str(), __func__, seq, toString(pointerCaptureEnabled));
InputMessage msg;
msg.header.type = InputMessage::Type::CAPTURE;
@@ -663,6 +719,9 @@
mChannel->getName().c_str(), x, y, toString(isExiting));
ATRACE_NAME(message.c_str());
}
+ ALOGD_IF(debugTransportPublisher(),
+ "channel '%s' publisher ~ %s: seq=%u, x=%f, y=%f, isExiting=%s",
+ mChannel->getName().c_str(), __func__, seq, x, y, toString(isExiting));
InputMessage msg;
msg.header.type = InputMessage::Type::DRAG;
@@ -681,6 +740,8 @@
mChannel->getName().c_str(), toString(isInTouchMode));
ATRACE_NAME(message.c_str());
}
+ ALOGD_IF(debugTransportPublisher(), "channel '%s' publisher ~ %s: seq=%u, isInTouchMode=%s",
+ mChannel->getName().c_str(), __func__, seq, toString(isInTouchMode));
InputMessage msg;
msg.header.type = InputMessage::Type::TOUCH_MODE;
@@ -691,9 +752,8 @@
}
android::base::Result<InputPublisher::ConsumerResponse> InputPublisher::receiveConsumerResponse() {
- if (DEBUG_TRANSPORT_ACTIONS) {
- ALOGD("channel '%s' publisher ~ %s", mChannel->getName().c_str(), __func__);
- }
+ ALOGD_IF(debugTransportPublisher(), "channel '%s' publisher ~ %s", mChannel->getName().c_str(),
+ __func__);
InputMessage msg;
status_t result = mChannel->receiveMessage(&msg);
@@ -738,10 +798,9 @@
status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consumeBatches,
nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
- if (DEBUG_TRANSPORT_ACTIONS) {
- ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%" PRId64,
- mChannel->getName().c_str(), toString(consumeBatches), frameTime);
- }
+ ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
+ "channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%" PRId64,
+ mChannel->getName().c_str(), toString(consumeBatches), frameTime);
*outSeq = 0;
*outEvent = nullptr;
@@ -767,10 +826,9 @@
if (consumeBatches || result != WOULD_BLOCK) {
result = consumeBatch(factory, frameTime, outSeq, outEvent);
if (*outEvent) {
- if (DEBUG_TRANSPORT_ACTIONS) {
- ALOGD("channel '%s' consumer ~ consumed batch event, seq=%u",
- mChannel->getName().c_str(), *outSeq);
- }
+ ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
+ "channel '%s' consumer ~ consumed batch event, seq=%u",
+ mChannel->getName().c_str(), *outSeq);
break;
}
}
@@ -786,11 +844,10 @@
initializeKeyEvent(keyEvent, &mMsg);
*outSeq = mMsg.header.seq;
*outEvent = keyEvent;
- if (DEBUG_TRANSPORT_ACTIONS) {
- ALOGD("channel '%s' consumer ~ consumed key event, seq=%u",
- mChannel->getName().c_str(), *outSeq);
- }
- break;
+ ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
+ "channel '%s' consumer ~ consumed key event, seq=%u",
+ mChannel->getName().c_str(), *outSeq);
+ break;
}
case InputMessage::Type::MOTION: {
@@ -799,11 +856,10 @@
Batch& batch = mBatches[batchIndex];
if (canAddSample(batch, &mMsg)) {
batch.samples.push_back(mMsg);
- if (DEBUG_TRANSPORT_ACTIONS) {
- ALOGD("channel '%s' consumer ~ appended to batch event",
- mChannel->getName().c_str());
- }
- break;
+ ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
+ "channel '%s' consumer ~ appended to batch event",
+ mChannel->getName().c_str());
+ break;
} else if (isPointerEvent(mMsg.body.motion.source) &&
mMsg.body.motion.action == AMOTION_EVENT_ACTION_CANCEL) {
// No need to process events that we are going to cancel anyways
@@ -824,12 +880,11 @@
if (result) {
return result;
}
- if (DEBUG_TRANSPORT_ACTIONS) {
- ALOGD("channel '%s' consumer ~ consumed batch event and "
- "deferred current event, seq=%u",
- mChannel->getName().c_str(), *outSeq);
- }
- break;
+ ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
+ "channel '%s' consumer ~ consumed batch event and "
+ "deferred current event, seq=%u",
+ mChannel->getName().c_str(), *outSeq);
+ break;
}
}
@@ -839,10 +894,9 @@
Batch batch;
batch.samples.push_back(mMsg);
mBatches.push_back(batch);
- if (DEBUG_TRANSPORT_ACTIONS) {
- ALOGD("channel '%s' consumer ~ started batch event",
- mChannel->getName().c_str());
- }
+ ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
+ "channel '%s' consumer ~ started batch event",
+ mChannel->getName().c_str());
break;
}
@@ -854,10 +908,9 @@
*outSeq = mMsg.header.seq;
*outEvent = motionEvent;
- if (DEBUG_TRANSPORT_ACTIONS) {
- ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u",
- mChannel->getName().c_str(), *outSeq);
- }
+ ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
+ "channel '%s' consumer ~ consumed motion event, seq=%u",
+ mChannel->getName().c_str(), *outSeq);
break;
}
@@ -1074,11 +1127,9 @@
state.recentCoordinatesAreIdentical(id)) {
PointerCoords& msgCoords = msg.body.motion.pointers[i].coords;
const PointerCoords& resampleCoords = state.lastResample.getPointerById(id);
-#if DEBUG_RESAMPLING
- ALOGD("[%d] - rewrite (%0.3f, %0.3f), old (%0.3f, %0.3f)", id,
- resampleCoords.getX(), resampleCoords.getY(),
- msgCoords.getX(), msgCoords.getY());
-#endif
+ ALOGD_IF(DEBUG_RESAMPLING, "[%d] - rewrite (%0.3f, %0.3f), old (%0.3f, %0.3f)", id,
+ resampleCoords.getX(), resampleCoords.getY(), msgCoords.getX(),
+ msgCoords.getY());
msgCoords.setAxisValue(AMOTION_EVENT_AXIS_X, resampleCoords.getX());
msgCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, resampleCoords.getY());
msgCoords.isResampled = true;
@@ -1099,17 +1150,13 @@
ssize_t index = findTouchState(event->getDeviceId(), event->getSource());
if (index < 0) {
-#if DEBUG_RESAMPLING
- ALOGD("Not resampled, no touch state for device.");
-#endif
+ ALOGD_IF(DEBUG_RESAMPLING, "Not resampled, no touch state for device.");
return;
}
TouchState& touchState = mTouchStates[index];
if (touchState.historySize < 1) {
-#if DEBUG_RESAMPLING
- ALOGD("Not resampled, no history for device.");
-#endif
+ ALOGD_IF(DEBUG_RESAMPLING, "Not resampled, no history for device.");
return;
}
@@ -1119,9 +1166,7 @@
for (size_t i = 0; i < pointerCount; i++) {
uint32_t id = event->getPointerId(i);
if (!current->idBits.hasBit(id)) {
-#if DEBUG_RESAMPLING
- ALOGD("Not resampled, missing id %d", id);
-#endif
+ ALOGD_IF(DEBUG_RESAMPLING, "Not resampled, missing id %d", id);
return;
}
}
@@ -1137,9 +1182,8 @@
other = &future;
nsecs_t delta = future.eventTime - current->eventTime;
if (delta < RESAMPLE_MIN_DELTA) {
-#if DEBUG_RESAMPLING
- ALOGD("Not resampled, delta time is too small: %" PRId64 " ns.", delta);
-#endif
+ ALOGD_IF(DEBUG_RESAMPLING, "Not resampled, delta time is too small: %" PRId64 " ns.",
+ delta);
return;
}
alpha = float(sampleTime - current->eventTime) / delta;
@@ -1149,30 +1193,25 @@
other = touchState.getHistory(1);
nsecs_t delta = current->eventTime - other->eventTime;
if (delta < RESAMPLE_MIN_DELTA) {
-#if DEBUG_RESAMPLING
- ALOGD("Not resampled, delta time is too small: %" PRId64 " ns.", delta);
-#endif
+ ALOGD_IF(DEBUG_RESAMPLING, "Not resampled, delta time is too small: %" PRId64 " ns.",
+ delta);
return;
} else if (delta > RESAMPLE_MAX_DELTA) {
-#if DEBUG_RESAMPLING
- ALOGD("Not resampled, delta time is too large: %" PRId64 " ns.", delta);
-#endif
+ ALOGD_IF(DEBUG_RESAMPLING, "Not resampled, delta time is too large: %" PRId64 " ns.",
+ delta);
return;
}
nsecs_t maxPredict = current->eventTime + min(delta / 2, RESAMPLE_MAX_PREDICTION);
if (sampleTime > maxPredict) {
-#if DEBUG_RESAMPLING
- ALOGD("Sample time is too far in the future, adjusting prediction "
- "from %" PRId64 " to %" PRId64 " ns.",
- sampleTime - current->eventTime, maxPredict - current->eventTime);
-#endif
+ ALOGD_IF(DEBUG_RESAMPLING,
+ "Sample time is too far in the future, adjusting prediction "
+ "from %" PRId64 " to %" PRId64 " ns.",
+ sampleTime - current->eventTime, maxPredict - current->eventTime);
sampleTime = maxPredict;
}
alpha = float(current->eventTime - sampleTime) / delta;
} else {
-#if DEBUG_RESAMPLING
- ALOGD("Not resampled, insufficient data.");
-#endif
+ ALOGD_IF(DEBUG_RESAMPLING, "Not resampled, insufficient data.");
return;
}
@@ -1207,28 +1246,22 @@
PointerCoords& resampledCoords = touchState.lastResample.pointers[i];
const PointerCoords& currentCoords = current->getPointerById(id);
resampledCoords.copyFrom(currentCoords);
- if (other->idBits.hasBit(id)
- && shouldResampleTool(event->getToolType(i))) {
+ if (other->idBits.hasBit(id) && shouldResampleTool(event->getToolType(i))) {
const PointerCoords& otherCoords = other->getPointerById(id);
resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X,
- lerp(currentCoords.getX(), otherCoords.getX(), alpha));
+ lerp(currentCoords.getX(), otherCoords.getX(), alpha));
resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y,
- lerp(currentCoords.getY(), otherCoords.getY(), alpha));
+ lerp(currentCoords.getY(), otherCoords.getY(), alpha));
resampledCoords.isResampled = true;
-#if DEBUG_RESAMPLING
- ALOGD("[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f), "
- "other (%0.3f, %0.3f), alpha %0.3f",
- id, resampledCoords.getX(), resampledCoords.getY(),
- currentCoords.getX(), currentCoords.getY(),
- otherCoords.getX(), otherCoords.getY(),
- alpha);
-#endif
+ ALOGD_IF(DEBUG_RESAMPLING,
+ "[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f), "
+ "other (%0.3f, %0.3f), alpha %0.3f",
+ id, resampledCoords.getX(), resampledCoords.getY(), currentCoords.getX(),
+ currentCoords.getY(), otherCoords.getX(), otherCoords.getY(), alpha);
} else {
-#if DEBUG_RESAMPLING
- ALOGD("[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f)",
- id, resampledCoords.getX(), resampledCoords.getY(),
- currentCoords.getX(), currentCoords.getY());
-#endif
+ ALOGD_IF(DEBUG_RESAMPLING, "[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f)", id,
+ resampledCoords.getX(), resampledCoords.getY(), currentCoords.getX(),
+ currentCoords.getY());
}
}
@@ -1241,10 +1274,9 @@
}
status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) {
- if (DEBUG_TRANSPORT_ACTIONS) {
- ALOGD("channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s",
- mChannel->getName().c_str(), seq, toString(handled));
- }
+ ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
+ "channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s",
+ mChannel->getName().c_str(), seq, toString(handled));
if (!seq) {
ALOGE("Attempted to send a finished signal with sequence number 0.");
@@ -1291,13 +1323,12 @@
status_t InputConsumer::sendTimeline(int32_t inputEventId,
std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline) {
- if (DEBUG_TRANSPORT_ACTIONS) {
- ALOGD("channel '%s' consumer ~ sendTimeline: inputEventId=%" PRId32
- ", gpuCompletedTime=%" PRId64 ", presentTime=%" PRId64,
- mChannel->getName().c_str(), inputEventId,
- graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME],
- graphicsTimeline[GraphicsTimeline::PRESENT_TIME]);
- }
+ ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
+ "channel '%s' consumer ~ sendTimeline: inputEventId=%" PRId32
+ ", gpuCompletedTime=%" PRId64 ", presentTime=%" PRId64,
+ mChannel->getName().c_str(), inputEventId,
+ graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME],
+ graphicsTimeline[GraphicsTimeline::PRESENT_TIME]);
InputMessage msg;
msg.header.type = InputMessage::Type::TIMELINE;
diff --git a/libs/input/InputVerifier.cpp b/libs/input/InputVerifier.cpp
new file mode 100644
index 0000000..eb75804
--- /dev/null
+++ b/libs/input/InputVerifier.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "InputVerifier"
+
+#include <android-base/logging.h>
+#include <input/InputVerifier.h>
+
+namespace android {
+
+/**
+ * Log all of the movements that are sent to this verifier. Helps to identify the streams that lead
+ * to inconsistent events.
+ * Enable this via "adb shell setprop log.tag.InputVerifierLogEvents DEBUG"
+ */
+static bool logEvents() {
+ return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "LogEvents", ANDROID_LOG_INFO);
+}
+
+// --- InputVerifier ---
+
+InputVerifier::InputVerifier(const std::string& name) : mName(name){};
+
+void InputVerifier::processMovement(int32_t deviceId, int32_t action, uint32_t pointerCount,
+ const PointerProperties* pointerProperties,
+ const PointerCoords* pointerCoords, int32_t flags) {
+ if (logEvents()) {
+ LOG(ERROR) << "Processing " << MotionEvent::actionToString(action) << " for device "
+ << deviceId << " (" << pointerCount << " pointer"
+ << (pointerCount == 1 ? "" : "s") << ") on " << mName;
+ }
+
+ switch (MotionEvent::getActionMasked(action)) {
+ case AMOTION_EVENT_ACTION_DOWN: {
+ auto [it, inserted] = mTouchingPointerIdsByDevice.insert({deviceId, {}});
+ if (!inserted) {
+ LOG(FATAL) << "Got ACTION_DOWN, but already have touching pointers " << it->second
+ << " for device " << deviceId << " on " << mName;
+ }
+ it->second.set(pointerProperties[0].id);
+ break;
+ }
+ case AMOTION_EVENT_ACTION_POINTER_DOWN: {
+ auto it = mTouchingPointerIdsByDevice.find(deviceId);
+ if (it == mTouchingPointerIdsByDevice.end()) {
+ LOG(FATAL) << "Got POINTER_DOWN, but no touching pointers for device " << deviceId
+ << " on " << mName;
+ }
+ it->second.set(pointerProperties[MotionEvent::getActionIndex(action)].id);
+ break;
+ }
+ case AMOTION_EVENT_ACTION_MOVE: {
+ ensureTouchingPointersMatch(deviceId, pointerCount, pointerProperties, "MOVE");
+ break;
+ }
+ case AMOTION_EVENT_ACTION_POINTER_UP: {
+ auto it = mTouchingPointerIdsByDevice.find(deviceId);
+ if (it == mTouchingPointerIdsByDevice.end()) {
+ LOG(FATAL) << "Got POINTER_UP, but no touching pointers for device " << deviceId
+ << " on " << mName;
+ }
+ it->second.reset(pointerProperties[MotionEvent::getActionIndex(action)].id);
+ break;
+ }
+ case AMOTION_EVENT_ACTION_UP: {
+ auto it = mTouchingPointerIdsByDevice.find(deviceId);
+ if (it == mTouchingPointerIdsByDevice.end()) {
+ LOG(FATAL) << "Got ACTION_UP, but no record for deviceId " << deviceId << " on "
+ << mName;
+ }
+ const auto& [_, touchingPointerIds] = *it;
+ if (touchingPointerIds.count() != 1) {
+ LOG(FATAL) << "Got ACTION_UP, but we have pointers: " << touchingPointerIds
+ << " for deviceId " << deviceId << " on " << mName;
+ }
+ const int32_t pointerId = pointerProperties[0].id;
+ if (!touchingPointerIds.test(pointerId)) {
+ LOG(FATAL) << "Got ACTION_UP, but pointerId " << pointerId
+ << " is not touching. Touching pointers: " << touchingPointerIds
+ << " for deviceId " << deviceId << " on " << mName;
+ }
+ mTouchingPointerIdsByDevice.erase(it);
+ break;
+ }
+ case AMOTION_EVENT_ACTION_CANCEL: {
+ if ((flags & AMOTION_EVENT_FLAG_CANCELED) != AMOTION_EVENT_FLAG_CANCELED) {
+ LOG(FATAL) << "For ACTION_CANCEL, must set FLAG_CANCELED";
+ }
+ ensureTouchingPointersMatch(deviceId, pointerCount, pointerProperties, "CANCEL");
+ mTouchingPointerIdsByDevice.erase(deviceId);
+ break;
+ }
+ }
+}
+
+void InputVerifier::ensureTouchingPointersMatch(int32_t deviceId, uint32_t pointerCount,
+ const PointerProperties* pointerProperties,
+ const char* action) const {
+ auto it = mTouchingPointerIdsByDevice.find(deviceId);
+ if (it == mTouchingPointerIdsByDevice.end()) {
+ LOG(FATAL) << "Got " << action << ", but no touching pointers for device " << deviceId
+ << " on " << mName;
+ }
+ const auto& [_, touchingPointerIds] = *it;
+ for (size_t i = 0; i < pointerCount; i++) {
+ const int32_t pointerId = pointerProperties[i].id;
+ if (!touchingPointerIds.test(pointerId)) {
+ LOG(FATAL) << "Got " << action << " for pointerId " << pointerId
+ << " but the touching pointers are " << touchingPointerIds << " on "
+ << mName;
+ }
+ }
+};
+
+} // namespace android
diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp
index 2278d39..e2aac8c 100644
--- a/libs/sensor/ISensorServer.cpp
+++ b/libs/sensor/ISensorServer.cpp
@@ -67,7 +67,11 @@
v.setCapacity(n);
while (n) {
n--;
- reply.read(s);
+ if(reply.read(s) != OK) {
+ ALOGE("Failed to read reply from getSensorList");
+ v.clear();
+ break;
+ }
v.add(s);
}
return v;
@@ -85,7 +89,11 @@
v.setCapacity(n);
while (n) {
n--;
- reply.read(s);
+ if(reply.read(s) != OK) {
+ ALOGE("Failed to read reply from getDynamicSensorList");
+ v.clear();
+ break;
+ }
v.add(s);
}
return v;
diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp
index fb895f5..b6ea77d 100644
--- a/libs/sensor/Sensor.cpp
+++ b/libs/sensor/Sensor.cpp
@@ -628,7 +628,13 @@
return false;
}
outputString8.setTo(static_cast<char const*>(buffer), len);
+
+ if (size < FlattenableUtils::align<4>(len)) {
+ ALOGE("Malformed Sensor String8 field. Should be in a 4-byte aligned buffer but is not.");
+ return false;
+ }
FlattenableUtils::advance(buffer, size, FlattenableUtils::align<4>(len));
+
return true;
}
diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp
index 2748276..44a208d 100644
--- a/libs/sensor/SensorManager.cpp
+++ b/libs/sensor/SensorManager.cpp
@@ -92,6 +92,16 @@
return *sensorManager;
}
+void SensorManager::removeInstanceForPackage(const String16& packageName) {
+ Mutex::Autolock _l(sLock);
+ auto iterator = sPackageInstances.find(packageName);
+ if (iterator != sPackageInstances.end()) {
+ SensorManager* sensorManager = iterator->second;
+ delete sensorManager;
+ sPackageInstances.erase(iterator);
+ }
+}
+
SensorManager::SensorManager(const String16& opPackageName)
: mSensorList(nullptr), mOpPackageName(opPackageName), mDirectConnectionHandle(1) {
Mutex::Autolock _l(mLock);
@@ -166,6 +176,11 @@
mSensors = mSensorServer->getSensorList(mOpPackageName);
size_t count = mSensors.size();
+ if (count == 0) {
+ ALOGE("Failed to get Sensor list");
+ mSensorServer.clear();
+ return UNKNOWN_ERROR;
+ }
mSensorList =
static_cast<Sensor const**>(malloc(count * sizeof(Sensor*)));
LOG_ALWAYS_FATAL_IF(mSensorList == nullptr, "mSensorList NULL");
diff --git a/libs/sensor/include/sensor/SensorManager.h b/libs/sensor/include/sensor/SensorManager.h
index 0798da2..c31f648 100644
--- a/libs/sensor/include/sensor/SensorManager.h
+++ b/libs/sensor/include/sensor/SensorManager.h
@@ -54,6 +54,7 @@
{
public:
static SensorManager& getInstanceForPackage(const String16& packageName);
+ static void removeInstanceForPackage(const String16& packageName);
~SensorManager();
ssize_t getSensorList(Sensor const* const** list);
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index ab5c5ef..da4e42f 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -34,6 +34,7 @@
srcs: [
"AnrTracker.cpp",
"Connection.cpp",
+ "DebugConfig.cpp",
"DragState.cpp",
"Entry.cpp",
"FocusResolver.cpp",
diff --git a/services/inputflinger/dispatcher/DebugConfig.cpp b/services/inputflinger/dispatcher/DebugConfig.cpp
new file mode 100644
index 0000000..764194d
--- /dev/null
+++ b/services/inputflinger/dispatcher/DebugConfig.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "DebugConfig.h"
+
+#include <android-base/properties.h>
+
+namespace android::inputdispatcher {
+
+const bool IS_DEBUGGABLE_BUILD =
+#if defined(__ANDROID__)
+ android::base::GetBoolProperty("ro.debuggable", false);
+#else
+ true;
+#endif
+
+bool debugInboundEventDetails() {
+ if (!IS_DEBUGGABLE_BUILD) {
+ static const bool DEBUG_INBOUND_EVENT_DETAILS =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "InboundEvent",
+ ANDROID_LOG_INFO);
+ return DEBUG_INBOUND_EVENT_DETAILS;
+ }
+ return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "InboundEvent", ANDROID_LOG_INFO);
+}
+
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/DebugConfig.h b/services/inputflinger/dispatcher/DebugConfig.h
index d2ad407..0e260a7 100644
--- a/services/inputflinger/dispatcher/DebugConfig.h
+++ b/services/inputflinger/dispatcher/DebugConfig.h
@@ -22,12 +22,20 @@
#include <log/log_event_list.h>
namespace android::inputdispatcher {
+
+/**
+ * Signals whether this is a debuggable Android build.
+ * This is populated by reading the value of the "ro.debuggable" property.
+ */
+extern const bool IS_DEBUGGABLE_BUILD;
+
/**
* Log detailed debug messages about each inbound event notification to the dispatcher.
- * Enable this via "adb shell setprop log.tag.InputDispatcherInboundEvent DEBUG" (requires restart)
+ * Enable this via "adb shell setprop log.tag.InputDispatcherInboundEvent DEBUG".
+ * This requires a restart on non-debuggable (e.g. user) builds, but should take effect immediately
+ * on debuggable builds (e.g. userdebug).
*/
-const bool DEBUG_INBOUND_EVENT_DETAILS =
- __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "InboundEvent", ANDROID_LOG_INFO);
+bool debugInboundEventDetails();
/**
* Log detailed debug messages about each outbound event processed by the dispatcher.
@@ -90,4 +98,5 @@
*/
const bool DEBUG_HOVER =
__android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Hover", ANDROID_LOG_INFO);
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 621543a..b625a1b 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -14,11 +14,13 @@
* limitations under the License.
*/
+#define LOG_TAG "InputDispatcher"
+
#include "Entry.h"
#include "Connection.h"
+#include "DebugConfig.h"
-#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <cutils/atomic.h>
#include <inttypes.h>
@@ -27,13 +29,6 @@
namespace android::inputdispatcher {
-static const bool DEBUGGABLE =
-#if defined(__ANDROID__)
- android::base::GetBoolProperty("ro.debuggable", false);
-#else
- true;
-#endif
-
VerifiedKeyEvent verifiedKeyEventFromKeyEntry(const KeyEntry& entry) {
return {{VerifiedInputEvent::Type::KEY, entry.deviceId, entry.eventTime, entry.source,
entry.displayId},
@@ -178,7 +173,7 @@
KeyEntry::~KeyEntry() {}
std::string KeyEntry::getDescription() const {
- if (!DEBUGGABLE) {
+ if (!IS_DEBUGGABLE_BUILD) {
return "KeyEvent";
}
return StringPrintf("KeyEvent(deviceId=%d, eventTime=%" PRIu64 ", source=%s, displayId=%" PRId32
@@ -248,7 +243,7 @@
MotionEntry::~MotionEntry() {}
std::string MotionEntry::getDescription() const {
- if (!DEBUGGABLE) {
+ if (!IS_DEBUGGABLE_BUILD) {
return "MotionEvent";
}
std::string msg;
@@ -298,7 +293,7 @@
deviceId, inputEventSourceToString(source).c_str(),
ftl::enum_string(sensorType).c_str(), accuracy, hwTimestamp);
- if (DEBUGGABLE) {
+ if (IS_DEBUGGABLE_BUILD) {
for (size_t i = 0; i < values.size(); i++) {
if (i > 0) {
msg += ", ";
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 08ae376..ab6a116 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -1207,7 +1207,7 @@
const char* reason;
switch (dropReason) {
case DropReason::POLICY:
- if (DEBUG_INBOUND_EVENT_DETAILS) {
+ if (debugInboundEventDetails()) {
ALOGD("Dropped event because policy consumed it.");
}
reason = "inbound event was dropped because the policy consumed it";
@@ -1596,7 +1596,7 @@
} else if (entry->action == AKEY_EVENT_ACTION_UP && mKeyRepeatState.lastKeyEntry &&
mKeyRepeatState.lastKeyEntry->deviceId != entry->deviceId) {
// The key on device 'deviceId' is still down, do not stop key repeat
- if (DEBUG_INBOUND_EVENT_DETAILS) {
+ if (debugInboundEventDetails()) {
ALOGD("deviceId=%d got KEY_UP as stale", entry->deviceId);
}
} else if (!entry->syntheticRepeat) {
@@ -2745,7 +2745,8 @@
if (displayInfoIt != mDisplayInfos.end()) {
inputTarget.displayTransform = displayInfoIt->second.transform;
} else {
- ALOGE("DisplayInfo not found for window on display: %d", windowInfo->displayId);
+ // DisplayInfo not found for this window on display windowInfo->displayId.
+ // TODO(b/198444055): Make this an error message after 'setInputWindows' API is removed.
}
inputTargets.push_back(inputTarget);
it = inputTargets.end() - 1;
@@ -4015,7 +4016,7 @@
}
void InputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
- if (DEBUG_INBOUND_EVENT_DETAILS) {
+ if (debugInboundEventDetails()) {
ALOGD("notifyConfigurationChanged - eventTime=%" PRId64, args->eventTime);
}
@@ -4071,7 +4072,7 @@
}
void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
- if (DEBUG_INBOUND_EVENT_DETAILS) {
+ if (debugInboundEventDetails()) {
ALOGD("notifyKey - eventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
"policyFlags=0x%x, action=0x%x, "
"flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%" PRId64,
@@ -4149,7 +4150,7 @@
}
void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
- if (DEBUG_INBOUND_EVENT_DETAILS) {
+ if (debugInboundEventDetails()) {
ALOGD("notifyMotion - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, "
"displayId=%" PRId32 ", policyFlags=0x%x, "
"action=%s, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, "
@@ -4260,7 +4261,7 @@
}
void InputDispatcher::notifySensor(const NotifySensorArgs* args) {
- if (DEBUG_INBOUND_EVENT_DETAILS) {
+ if (debugInboundEventDetails()) {
ALOGD("notifySensor - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, "
" sensorType=%s",
args->id, args->eventTime, args->deviceId, args->source,
@@ -4288,7 +4289,7 @@
}
void InputDispatcher::notifyVibratorState(const NotifyVibratorStateArgs* args) {
- if (DEBUG_INBOUND_EVENT_DETAILS) {
+ if (debugInboundEventDetails()) {
ALOGD("notifyVibratorState - eventTime=%" PRId64 ", device=%d, isOn=%d", args->eventTime,
args->deviceId, args->isOn);
}
@@ -4300,7 +4301,7 @@
}
void InputDispatcher::notifySwitch(const NotifySwitchArgs* args) {
- if (DEBUG_INBOUND_EVENT_DETAILS) {
+ if (debugInboundEventDetails()) {
ALOGD("notifySwitch - eventTime=%" PRId64 ", policyFlags=0x%x, switchValues=0x%08x, "
"switchMask=0x%08x",
args->eventTime, args->policyFlags, args->switchValues, args->switchMask);
@@ -4312,7 +4313,7 @@
}
void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
- if (DEBUG_INBOUND_EVENT_DETAILS) {
+ if (debugInboundEventDetails()) {
ALOGD("notifyDeviceReset - eventTime=%" PRId64 ", deviceId=%d", args->eventTime,
args->deviceId);
}
@@ -4332,7 +4333,7 @@
}
void InputDispatcher::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) {
- if (DEBUG_INBOUND_EVENT_DETAILS) {
+ if (debugInboundEventDetails()) {
ALOGD("notifyPointerCaptureChanged - eventTime=%" PRId64 ", enabled=%s", args->eventTime,
args->request.enable ? "true" : "false");
}
@@ -4355,7 +4356,7 @@
InputEventInjectionSync syncMode,
std::chrono::milliseconds timeout,
uint32_t policyFlags) {
- if (DEBUG_INBOUND_EVENT_DETAILS) {
+ if (debugInboundEventDetails()) {
ALOGD("injectInputEvent - eventType=%d, targetUid=%s, syncMode=%d, timeout=%lld, "
"policyFlags=0x%08x",
event->getType(), targetUid ? std::to_string(*targetUid).c_str() : "none", syncMode,
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index cc9cc4e..94f3813 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -247,10 +247,19 @@
}
void InputState::MotionMemento::setPointers(const MotionEntry& entry) {
- pointerCount = entry.pointerCount;
+ pointerCount = 0;
for (uint32_t i = 0; i < entry.pointerCount; i++) {
- pointerProperties[i].copyFrom(entry.pointerProperties[i]);
- pointerCoords[i].copyFrom(entry.pointerCoords[i]);
+ if (MotionEvent::getActionMasked(entry.action) == AMOTION_EVENT_ACTION_POINTER_UP) {
+ // In POINTER_UP events, the pointer is leaving. Since the action is not stored,
+ // this departing pointer should not be recorded.
+ const uint8_t actionIndex = MotionEvent::getActionIndex(entry.action);
+ if (i == actionIndex) {
+ continue;
+ }
+ }
+ pointerProperties[pointerCount].copyFrom(entry.pointerProperties[i]);
+ pointerCoords[pointerCount].copyFrom(entry.pointerCoords[i]);
+ pointerCount++;
}
}
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index d29692c..132c3a1 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -39,6 +39,7 @@
"EventHub.cpp",
"InputDevice.cpp",
"InputReader.cpp",
+ "Macros.cpp",
"TouchVideoDevice.cpp",
"controller/PeripheralController.cpp",
"mapper/CursorInputMapper.cpp",
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index c598c0a..002de29 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -412,7 +412,7 @@
// in the order received.
std::list<NotifyArgs> out;
for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
- if (DEBUG_RAW_EVENTS) {
+ if (debugRawEvents()) {
ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%" PRId64,
rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value,
rawEvent->when);
@@ -421,11 +421,11 @@
if (mDropUntilNextSync) {
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
mDropUntilNextSync = false;
- if (DEBUG_RAW_EVENTS) {
+ if (debugRawEvents()) {
ALOGD("Recovered from input event buffer overrun.");
}
} else {
- if (DEBUG_RAW_EVENTS) {
+ if (debugRawEvents()) {
ALOGD("Dropped input event while waiting for next input sync.");
}
}
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 57f679c..9080cc1 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -146,7 +146,7 @@
if (mNextTimeout != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
if (now >= mNextTimeout) {
- if (DEBUG_RAW_EVENTS) {
+ if (debugRawEvents()) {
ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
}
mNextTimeout = LLONG_MAX;
@@ -199,7 +199,7 @@
}
batchSize += 1;
}
- if (DEBUG_RAW_EVENTS) {
+ if (debugRawEvents()) {
ALOGD("BatchSize: %zu Count: %zu", batchSize, count);
}
out += processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
diff --git a/services/inputflinger/reader/Macros.cpp b/services/inputflinger/reader/Macros.cpp
new file mode 100644
index 0000000..8841d0f
--- /dev/null
+++ b/services/inputflinger/reader/Macros.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Macros.h"
+
+#include <android-base/properties.h>
+
+namespace {
+
+const bool IS_DEBUGGABLE_BUILD =
+#if defined(__ANDROID__)
+ android::base::GetBoolProperty("ro.debuggable", false);
+#else
+ true;
+#endif
+
+} // namespace
+
+namespace android {
+
+bool debugRawEvents() {
+ if (!IS_DEBUGGABLE_BUILD) {
+ static const bool DEBUG_RAW_EVENTS =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "RawEvents", ANDROID_LOG_INFO);
+ return DEBUG_RAW_EVENTS;
+ }
+ return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "RawEvents", ANDROID_LOG_INFO);
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/Macros.h b/services/inputflinger/reader/Macros.h
index d2a7ced..2bce215 100644
--- a/services/inputflinger/reader/Macros.h
+++ b/services/inputflinger/reader/Macros.h
@@ -25,12 +25,14 @@
#include <unordered_map>
namespace android {
+
/**
* Log debug messages for each raw event received from the EventHub.
- * Enable this via "adb shell setprop log.tag.InputReaderRawEvents DEBUG" (requires restart)
+ * Enable this via "adb shell setprop log.tag.InputReaderRawEvents DEBUG".
+ * This requires a restart on non-debuggable (e.g. user) builds, but should take effect immediately
+ * on debuggable builds (e.g. userdebug).
*/
-const bool DEBUG_RAW_EVENTS =
- __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "RawEvents", ANDROID_LOG_INFO);
+bool debugRawEvents();
/**
* Log debug messages about virtual key processing.
@@ -52,6 +54,7 @@
*/
const bool DEBUG_POINTER_ASSIGNMENT =
__android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "PointerAssignment", ANDROID_LOG_INFO);
+
/**
* Log debug messages about gesture detection.
* Enable this via "adb shell setprop log.tag.InputReaderGestures DEBUG" (requires restart)
@@ -79,6 +82,7 @@
*/
const bool DEBUG_LIGHT_DETAILS =
__android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "LightDetails", ANDROID_LOG_INFO);
+
} // namespace android
#define INDENT " "
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 31fdac9..3ba6cd0 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -1446,7 +1446,7 @@
assignPointerIds(last, next);
}
- ALOGD_IF(DEBUG_RAW_EVENTS,
+ ALOGD_IF(debugRawEvents(),
"syncTouch: pointerCount %d -> %d, touching ids 0x%08x -> 0x%08x, "
"hovering ids 0x%08x -> 0x%08x, canceled ids 0x%08x",
last.rawPointerData.pointerCount, next.rawPointerData.pointerCount,
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index 9f32311..8ab6748 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -16,6 +16,7 @@
#include "../Macros.h"
+#include <limits>
#include <optional>
#include <android/input.h>
@@ -30,6 +31,76 @@
namespace {
+// Describes a segment of the acceleration curve.
+struct CurveSegment {
+ // The maximum pointer speed which this segment should apply. The last segment in a curve should
+ // always set this to infinity.
+ double maxPointerSpeedMmPerS;
+ double slope;
+ double intercept;
+};
+
+const std::vector<CurveSegment> segments = {
+ {10.922, 3.19, 0},
+ {31.750, 4.79, -17.526},
+ {98.044, 7.28, -96.52},
+ {std::numeric_limits<double>::infinity(), 15.04, -857.758},
+};
+
+const std::vector<double> sensitivityFactors = {1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 18, 20};
+
+std::vector<double> createAccelerationCurveForSensitivity(int32_t sensitivity,
+ size_t propertySize) {
+ LOG_ALWAYS_FATAL_IF(propertySize < 4 * segments.size());
+ std::vector<double> output(propertySize, 0);
+
+ // The Gestures library uses functions of the following form to define curve segments, where a,
+ // b, and c can be specified by us:
+ // output_speed(input_speed_mm) = a * input_speed_mm ^ 2 + b * input_speed_mm + c
+ //
+ // (a, b, and c are also called sqr_, mul_, and int_ in the Gestures library code.)
+ //
+ // We are trying to implement the following function, where slope and intercept are the
+ // parameters specified in the `segments` array above:
+ // gain(input_speed_mm) =
+ // 0.64 * (sensitivityFactor / 10) * (slope + intercept / input_speed_mm)
+ // Where "gain" is a multiplier applied to the input speed to produce the output speed:
+ // output_speed(input_speed_mm) = input_speed_mm * gain(input_speed_mm)
+ //
+ // To put our function in the library's form, we substitute it into the function above:
+ // output_speed(input_speed_mm) =
+ // input_speed_mm * (0.64 * (sensitivityFactor / 10) *
+ // (slope + 25.4 * intercept / input_speed_mm))
+ // then expand the brackets so that input_speed_mm cancels out for the intercept term:
+ // gain(input_speed_mm) =
+ // 0.64 * (sensitivityFactor / 10) * slope * input_speed_mm +
+ // 0.64 * (sensitivityFactor / 10) * intercept
+ //
+ // This gives us the following parameters for the Gestures library function form:
+ // a = 0
+ // b = 0.64 * (sensitivityFactor / 10) * slope
+ // c = 0.64 * (sensitivityFactor / 10) * intercept
+
+ double commonFactor = 0.64 * sensitivityFactors[sensitivity + 7] / 10;
+
+ size_t i = 0;
+ for (CurveSegment seg : segments) {
+ // The library's curve format consists of four doubles per segment:
+ // * maximum pointer speed for the segment (mm/s)
+ // * multiplier for the x² term (a.k.a. "a" or "sqr")
+ // * multiplier for the x term (a.k.a. "b" or "mul")
+ // * the intercept (a.k.a. "c" or "int")
+ // (see struct CurveSegment in the library's AccelFilterInterpreter)
+ output[i + 0] = seg.maxPointerSpeedMmPerS;
+ output[i + 1] = 0;
+ output[i + 2] = commonFactor * seg.slope;
+ output[i + 3] = commonFactor * seg.intercept;
+ i += 4;
+ }
+
+ return output;
+}
+
short getMaxTouchCount(const InputDeviceContext& context) {
if (context.hasScanCode(BTN_TOOL_QUINTTAP)) return 5;
if (context.hasScanCode(BTN_TOOL_QUADTAP)) return 4;
@@ -147,10 +218,12 @@
mGestureConverter.setOrientation(orientation);
}
if (!changes || (changes & InputReaderConfiguration::CHANGE_TOUCHPAD_SETTINGS)) {
- // TODO(b/265798483): load an Android-specific acceleration curve instead of mapping to one
- // of five ChromeOS curves.
- const int pointerSensitivity = (config->touchpadPointerSpeed + 7) / 3 + 1;
- mPropertyProvider.getProperty("Pointer Sensitivity").setIntValues({pointerSensitivity});
+ mPropertyProvider.getProperty("Use Custom Touchpad Pointer Accel Curve")
+ .setBoolValues({true});
+ GesturesProp accelCurveProp = mPropertyProvider.getProperty("Pointer Accel Curve");
+ accelCurveProp.setRealValues(
+ createAccelerationCurveForSensitivity(config->touchpadPointerSpeed,
+ accelCurveProp.getCount()));
mPropertyProvider.getProperty("Invert Scrolling")
.setBoolValues({config->touchpadNaturalScrollingEnabled});
mPropertyProvider.getProperty("Tap Enable")
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index bd9038d..314233e 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -105,6 +105,8 @@
static constexpr int expectedWallpaperFlags =
AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+using ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID;
+
struct PointF {
float x;
float y;
@@ -1945,6 +1947,48 @@
}
/**
+ * Two fingers down on the window, and lift off the first finger.
+ * Next, cancel the gesture to the window by removing the window. Make sure that the CANCEL event
+ * contains a single pointer.
+ */
+TEST_F(InputDispatcherTest, CancelAfterPointer0Up) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ NotifyMotionArgs args;
+ // First touch pointer down on right window
+ mDispatcher->notifyMotion(&(
+ args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(100).y(100))
+ .build()));
+ // Second touch pointer down
+ mDispatcher->notifyMotion(&(
+ args = MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(100).y(100))
+ .pointer(PointerBuilder(1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(110).y(100))
+ .build()));
+ // First touch pointer lifts. The second one remains down
+ mDispatcher->notifyMotion(&(
+ args = MotionArgsBuilder(POINTER_0_UP, AINPUT_SOURCE_TOUCHSCREEN)
+
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(100).y(100))
+ .pointer(PointerBuilder(1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(110).y(100))
+ .build()));
+ window->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+ window->consumeMotionEvent(WithMotionAction(POINTER_1_DOWN));
+ window->consumeMotionEvent(WithMotionAction(POINTER_0_UP));
+
+ // Remove the window. The gesture should be canceled
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {}}});
+ const std::map<int32_t, PointF> expectedPointers{{1, PointF{110, 100}}};
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithPointers(expectedPointers)));
+}
+
+/**
* Same test as WhenForegroundWindowDisappears_WallpaperTouchIsCanceled above,
* with the following differences:
* After ACTION_DOWN, Wallpaper window hangs up its channel, which forces the dispatcher to
@@ -2052,8 +2096,17 @@
wallpaperWindow->consumeMotionPointerUp(0, ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {100, 100}))
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_UP,
+ AINPUT_SOURCE_TOUCHSCREEN)
+ .displayId(ADISPLAY_ID_DEFAULT)
+ .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+ .pointer(PointerBuilder(/* id */ 1,
+ AMOTION_EVENT_TOOL_TYPE_FINGER)
+ .x(100)
+ .y(100))
+ .build(),
+ INJECT_EVENT_TIMEOUT, InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
foregroundWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
wallpaperWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
@@ -2490,7 +2543,7 @@
const int32_t mouseDeviceId = 6;
NotifyMotionArgs args;
- // First touch pointer down on right window
+ // First touch pointer down
mDispatcher->notifyMotion(&(
args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
@@ -2523,7 +2576,7 @@
.build()));
window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(touchDeviceId),
- WithPointerCount(2u)));
+ WithPointerCount(1u)));
window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(mouseDeviceId)));
mDispatcher->notifyMotion(&(
@@ -2547,6 +2600,46 @@
}
/**
+ * Inject a touch down and then send a new event via 'notifyMotion'. Ensure the new event cancels
+ * the injected event.
+ */
+TEST_F(InputDispatcherTest, UnfinishedInjectedEvent) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 400, 400));
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+ const int32_t touchDeviceId = 4;
+ NotifyMotionArgs args;
+ // Pretend a test injects an ACTION_DOWN mouse event, but forgets to lift up the touch after
+ // completion.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+ .deviceId(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(50)
+ .y(50))
+ .build()));
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(VIRTUAL_KEYBOARD_ID)));
+
+ // Now a real touch comes. Rather than crashing or dropping the real event, the injected pointer
+ // should be canceled and the new gesture should take over.
+ mDispatcher->notifyMotion(&(
+ args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(300).y(100))
+ .build()));
+
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(VIRTUAL_KEYBOARD_ID)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+}
+
+/**
* This test is similar to the test above, but the sequence of injected events is different.
*
* Two windows: a window on the left and a window on the right.
@@ -3007,8 +3100,8 @@
window1->assertNoEvents();
// Now move the pointer on the first window
- mDispatcher->notifyMotion(
- &(args = generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{51, 51}, {151, 51}})));
+ mDispatcher->notifyMotion(&(
+ args = generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{51, 51}, {151, 51}, {150, 50}})));
mDispatcher->waitForIdle();
window1->consumeMotionEvent(WithDownTime(downTimeForWindow1));
@@ -3440,6 +3533,42 @@
}
/**
+ * If mouse is hovering when the touch goes down, the hovering should be stopped via HOVER_EXIT.
+ */
+TEST_F(InputDispatcherTest, TouchDownAfterMouseHover) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 100, 100));
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+ const int32_t mouseDeviceId = 7;
+ const int32_t touchDeviceId = 4;
+ NotifyMotionArgs args;
+
+ // Start hovering with the mouse
+ mDispatcher->notifyMotion(
+ &(args = MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE).x(10).y(10))
+ .build()));
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(mouseDeviceId)));
+
+ // Touch goes down
+ mDispatcher->notifyMotion(
+ &(args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50))
+ .build()));
+
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(mouseDeviceId)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+}
+
+/**
* Inject a mouse hover event followed by a tap from touchscreen.
* The tap causes a HOVER_EXIT event to be generated because the current event
* stream's source has been switched.
@@ -5465,6 +5594,13 @@
windowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID);
monitorInSecondary.consumeMotionDown(SECOND_DISPLAY_ID);
+ // Lift up the touch from the second display
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ windowInSecondary->consumeMotionUp(SECOND_DISPLAY_ID);
+ monitorInSecondary.consumeMotionUp(SECOND_DISPLAY_ID);
+
// Test inject a non-pointer motion event.
// If specific a display, it will dispatch to the focused window of particular display,
// or it will dispatch to the focused window of focused display.
diff --git a/services/sensorservice/aidl/SensorManager.cpp b/services/sensorservice/aidl/SensorManager.cpp
index b7aecdf..08e00b4 100644
--- a/services/sensorservice/aidl/SensorManager.cpp
+++ b/services/sensorservice/aidl/SensorManager.cpp
@@ -59,6 +59,9 @@
if (mPollThread.joinable()) {
mPollThread.join();
}
+
+ ::android::SensorManager::removeInstanceForPackage(
+ String16(ISensorManager::descriptor));
}
ndk::ScopedAStatus createDirectChannel(::android::SensorManager& manager, size_t size, int type,
diff --git a/services/sensorservice/hidl/SensorManager.cpp b/services/sensorservice/hidl/SensorManager.cpp
index f04712c..3d148e1 100644
--- a/services/sensorservice/hidl/SensorManager.cpp
+++ b/services/sensorservice/hidl/SensorManager.cpp
@@ -60,6 +60,9 @@
if (mPollThread.joinable()) {
mPollThread.join();
}
+
+ ::android::SensorManager::removeInstanceForPackage(
+ String16(ISensorManager::descriptor));
}
// Methods from ::android::frameworks::sensorservice::V1_0::ISensorManager follow.
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index dac0916..a29be22 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -790,7 +790,7 @@
// transaction
// ----------------------------------------------------------------------------
-uint32_t Layer::doTransaction(uint32_t flags, nsecs_t latchTime) {
+uint32_t Layer::doTransaction(uint32_t flags) {
ATRACE_CALL();
// TODO: This is unfortunate.
@@ -818,12 +818,12 @@
mFlinger->mUpdateInputInfo = true;
}
- commitTransaction(mDrawingState, latchTime);
+ commitTransaction(mDrawingState);
return flags;
}
-void Layer::commitTransaction(State&, nsecs_t currentLatchTime) {
+void Layer::commitTransaction(State&) {
// Set the present state for all bufferlessSurfaceFramesTX to Presented. The
// bufferSurfaceFrameTX will be presented in latchBuffer.
for (auto& [token, surfaceFrame] : mDrawingState.bufferlessSurfaceFramesTX) {
@@ -835,7 +835,6 @@
}
}
mDrawingState.bufferlessSurfaceFramesTX.clear();
- mLastLatchTime = currentLatchTime;
}
uint32_t Layer::clearTransactionFlags(uint32_t mask) {
@@ -1381,7 +1380,7 @@
surfaceFrame->setAcquireFenceTime(acquireFenceTime);
surfaceFrame->setPresentState(PresentState::Presented, mLastLatchTime);
mFlinger->mFrameTimeline->addSurfaceFrame(surfaceFrame);
- mLastLatchTime = currentLatchTime;
+ updateLastLatchTime(currentLatchTime);
}
std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForTransaction(
@@ -4077,6 +4076,10 @@
}
}
+void Layer::updateLastLatchTime(nsecs_t latchTime) {
+ mLastLatchTime = latchTime;
+}
+
// ---------------------------------------------------------------------------
std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 7d40774..326e1f6 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -347,6 +347,7 @@
void useSurfaceDamage();
void useEmptyDamage();
Region getVisibleRegion(const DisplayDevice*) const;
+ void updateLastLatchTime(nsecs_t latchtime);
/*
* isOpaque - true if this surface is opaque
@@ -623,7 +624,7 @@
* doTransaction - process the transaction. This is a good place to figure
* out which attributes of the surface have changed.
*/
- virtual uint32_t doTransaction(uint32_t transactionFlags, nsecs_t currentLatchTime);
+ virtual uint32_t doTransaction(uint32_t transactionFlags);
/*
* Remove relative z for the layer if its relative parent is not part of the
@@ -860,7 +861,7 @@
void preparePerFrameCompositionState();
void preparePerFrameBufferCompositionState();
void preparePerFrameEffectsCompositionState();
- virtual void commitTransaction(State& stateToCommit, nsecs_t currentLatchTime = 0);
+ virtual void commitTransaction(State& stateToCommit);
void gatherBufferInfo();
void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>&);
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index eb6d7e4..b78b92b 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -384,23 +384,13 @@
return vsyncEventData;
}
-void EventThread::onScreenReleased() {
+void EventThread::enableSyntheticVsync(bool enable) {
std::lock_guard<std::mutex> lock(mMutex);
- if (!mVSyncState || mVSyncState->synthetic) {
+ if (!mVSyncState || mVSyncState->synthetic == enable) {
return;
}
- mVSyncState->synthetic = true;
- mCondition.notify_all();
-}
-
-void EventThread::onScreenAcquired() {
- std::lock_guard<std::mutex> lock(mMutex);
- if (!mVSyncState || !mVSyncState->synthetic) {
- return;
- }
-
- mVSyncState->synthetic = false;
+ mVSyncState->synthetic = enable;
mCondition.notify_all();
}
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 347dc4a..5037860 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -106,11 +106,8 @@
virtual sp<EventThreadConnection> createEventConnection(
ResyncCallback, EventRegistrationFlags eventRegistration = {}) const = 0;
- // called before the screen is turned off from main thread
- virtual void onScreenReleased() = 0;
-
- // called after the screen is turned on from main thread
- virtual void onScreenAcquired() = 0;
+ // Feed clients with fake VSYNC, e.g. while the display is off.
+ virtual void enableSyntheticVsync(bool) = 0;
virtual void onHotplugReceived(PhysicalDisplayId displayId, bool connected) = 0;
@@ -159,11 +156,7 @@
VsyncEventData getLatestVsyncEventData(
const sp<EventThreadConnection>& connection) const override;
- // called before the screen is turned off from main thread
- void onScreenReleased() override;
-
- // called after the screen is turned on from main thread
- void onScreenAcquired() override;
+ void enableSyntheticVsync(bool) override;
void onHotplugReceived(PhysicalDisplayId displayId, bool connected) override;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index dab01ba..0daabe2 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -265,24 +265,16 @@
thread->onHotplugReceived(displayId, connected);
}
-void Scheduler::onScreenAcquired(ConnectionHandle handle) {
+void Scheduler::enableSyntheticVsync(bool enable) {
+ // TODO(b/241285945): Remove connection handles.
+ const ConnectionHandle handle = mAppConnectionHandle;
android::EventThread* thread;
{
std::lock_guard<std::mutex> lock(mConnectionsLock);
RETURN_IF_INVALID_HANDLE(handle);
thread = mConnections[handle].thread.get();
}
- thread->onScreenAcquired();
-}
-
-void Scheduler::onScreenReleased(ConnectionHandle handle) {
- android::EventThread* thread;
- {
- std::lock_guard<std::mutex> lock(mConnectionsLock);
- RETURN_IF_INVALID_HANDLE(handle);
- thread = mConnections[handle].thread.get();
- }
- thread->onScreenReleased();
+ thread->enableSyntheticVsync(enable);
}
void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index a340919..67f4daa 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -157,8 +157,8 @@
void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected);
void onPrimaryDisplayModeChanged(ConnectionHandle, const FrameRateMode&) EXCLUDES(mPolicyLock);
void onNonPrimaryDisplayModeChanged(ConnectionHandle, const FrameRateMode&);
- void onScreenAcquired(ConnectionHandle);
- void onScreenReleased(ConnectionHandle);
+
+ void enableSyntheticVsync(bool = true) REQUIRES(kMainThreadContext);
void onFrameRateOverridesChanged(ConnectionHandle, PhysicalDisplayId)
EXCLUDES(mConnectionsLock);
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h
index d88f1d1..a32acc7 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.h
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h
@@ -20,6 +20,7 @@
#include <string>
#include <ThreadContext.h>
+#include <android-base/thread_annotations.h>
#include <ftl/enum.h>
#include <ftl/optional.h>
#include <scheduler/Features.h>
@@ -27,6 +28,7 @@
namespace android {
class EventThreadTest;
+class VsyncScheduleTest;
}
namespace android::fuzz {
@@ -96,6 +98,7 @@
private:
friend class TestableScheduler;
friend class android::EventThreadTest;
+ friend class android::VsyncScheduleTest;
friend class android::fuzz::SchedulerFuzzer;
using TrackerPtr = std::unique_ptr<VsyncTracker>;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 5c8579c..86018d6 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2031,15 +2031,9 @@
void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp,
std::optional<hal::VsyncPeriodNanos> vsyncPeriod) {
- const std::string tracePeriod = [vsyncPeriod]() {
- if (ATRACE_ENABLED() && vsyncPeriod) {
- std::stringstream ss;
- ss << "(" << *vsyncPeriod << ")";
- return ss.str();
- }
- return std::string();
- }();
- ATRACE_FORMAT("onComposerHalVsync%s", tracePeriod.c_str());
+ ATRACE_NAME(vsyncPeriod
+ ? ftl::Concat(__func__, ' ', hwcDisplayId, ' ', *vsyncPeriod, "ns").c_str()
+ : ftl::Concat(__func__, ' ', hwcDisplayId).c_str());
Mutex::Autolock lock(mStateLock);
@@ -3576,6 +3570,10 @@
});
}
+ if (transactionFlags & eInputInfoUpdateNeeded) {
+ mUpdateInputInfo = true;
+ }
+
doCommitTransactions();
}
@@ -3863,7 +3861,7 @@
for (Layer* offscreenLayer : mOffscreenLayers) {
offscreenLayer->traverse(LayerVector::StateSet::Drawing, [](Layer* layer) {
if (layer->clearTransactionFlags(eTransactionNeeded)) {
- layer->doTransaction(0, 0);
+ layer->doTransaction(0);
layer->commitChildList();
}
});
@@ -3899,7 +3897,7 @@
// second frame. But layer 0's second frame could be waiting on display.
mDrawingState.traverse([&](Layer* layer) {
if (layer->clearTransactionFlags(eTransactionNeeded) || mForceTransactionDisplayChange) {
- const uint32_t flags = layer->doTransaction(0, latchTime);
+ const uint32_t flags = layer->doTransaction(0);
if (flags & Layer::eVisibleRegion) {
mVisibleRegionsDirty = true;
}
@@ -3910,6 +3908,8 @@
mLayersWithQueuedFrames.emplace(sp<Layer>::fromExisting(layer));
} else {
layer->useEmptyDamage();
+ // If the layer has frames we will update the latch time when latching the buffer.
+ layer->updateLastLatchTime(latchTime);
}
});
mForceTransactionDisplayChange = false;
@@ -5210,7 +5210,6 @@
return;
}
- const bool isActiveDisplay = displayId == mActiveDisplayId;
const bool isInternalDisplay = mPhysicalDisplays.get(displayId)
.transform(&PhysicalDisplay::isInternal)
.value_or(false);
@@ -5247,10 +5246,10 @@
ALOGW("Couldn't set SCHED_FIFO on display on: %s\n", strerror(errno));
}
getHwComposer().setPowerMode(displayId, mode);
- if (isActiveDisplay && mode != hal::PowerMode::DOZE_SUSPEND) {
+ if (displayId == mActiveDisplayId && mode != hal::PowerMode::DOZE_SUSPEND) {
setHWCVsyncEnabled(displayId,
mScheduler->getVsyncSchedule().getPendingHardwareVsyncState());
- mScheduler->onScreenAcquired(mAppConnectionHandle);
+ mScheduler->enableSyntheticVsync(false);
mScheduler->resyncToHardwareVsync(true, refreshRate);
}
@@ -5264,9 +5263,9 @@
if (SurfaceFlinger::setSchedAttr(false) != NO_ERROR) {
ALOGW("Couldn't set uclamp.min on display off: %s\n", strerror(errno));
}
- if (isActiveDisplay && *currentModeOpt != hal::PowerMode::DOZE_SUSPEND) {
+ if (displayId == mActiveDisplayId && *currentModeOpt != hal::PowerMode::DOZE_SUSPEND) {
mScheduler->disableHardwareVsync(true);
- mScheduler->onScreenReleased(mAppConnectionHandle);
+ mScheduler->enableSyntheticVsync();
}
// Make sure HWVsync is disabled before turning off the display
@@ -5278,18 +5277,18 @@
} else if (mode == hal::PowerMode::DOZE || mode == hal::PowerMode::ON) {
// Update display while dozing
getHwComposer().setPowerMode(displayId, mode);
- if (isActiveDisplay && *currentModeOpt == hal::PowerMode::DOZE_SUSPEND) {
+ if (displayId == mActiveDisplayId && *currentModeOpt == hal::PowerMode::DOZE_SUSPEND) {
ALOGI("Force repainting for DOZE_SUSPEND -> DOZE or ON.");
mVisibleRegionsDirty = true;
scheduleRepaint();
- mScheduler->onScreenAcquired(mAppConnectionHandle);
+ mScheduler->enableSyntheticVsync(false);
mScheduler->resyncToHardwareVsync(true, refreshRate);
}
} else if (mode == hal::PowerMode::DOZE_SUSPEND) {
// Leave display going to doze
- if (isActiveDisplay) {
+ if (displayId == mActiveDisplayId) {
mScheduler->disableHardwareVsync(true);
- mScheduler->onScreenReleased(mAppConnectionHandle);
+ mScheduler->enableSyntheticVsync();
}
getHwComposer().setPowerMode(displayId, mode);
} else {
@@ -5297,7 +5296,7 @@
getHwComposer().setPowerMode(displayId, mode);
}
- if (isActiveDisplay) {
+ if (displayId == mActiveDisplayId) {
mTimeStats->setPowerMode(mode);
mRefreshRateStats->setPowerMode(mode);
mScheduler->setDisplayPowerMode(mode);
@@ -7566,8 +7565,9 @@
}
status_t SurfaceFlinger::addWindowInfosListener(
- const sp<IWindowInfosListener>& windowInfosListener) const {
+ const sp<IWindowInfosListener>& windowInfosListener) {
mWindowInfosListenerInvoker->addWindowInfosListener(windowInfosListener);
+ setTransactionFlags(eInputInfoUpdateNeeded);
return NO_ERROR;
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index b41f414..03c31bb 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -162,7 +162,8 @@
eDisplayTransactionNeeded = 0x04,
eTransformHintUpdateNeeded = 0x08,
eTransactionFlushNeeded = 0x10,
- eTransactionMask = 0x1f,
+ eInputInfoUpdateNeeded = 0x20,
+ eTransactionMask = 0x3f,
};
// Latch Unsignaled buffer behaviours
@@ -618,7 +619,7 @@
status_t getMaxAcquiredBufferCount(int* buffers) const;
- status_t addWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener) const;
+ status_t addWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener);
status_t removeWindowInfosListener(
const sp<gui::IWindowInfosListener>& windowInfosListener) const;
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
index 2f46487..475c76b 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -15,6 +15,7 @@
*/
#include <gui/SurfaceComposerClient.h>
+#include <renderengine/mock/FakeExternalTexture.h>
#include <ui/Fence.h>
#include <ui/Rect.h>
@@ -313,6 +314,14 @@
ResolvedComposerState s;
s.state.what = 0;
fromProto(proto.layer_changes(i), s.state);
+ if (s.state.bufferData) {
+ s.externalTexture = std::make_shared<
+ renderengine::mock::FakeExternalTexture>(s.state.bufferData->getWidth(),
+ s.state.bufferData->getHeight(),
+ s.state.bufferData->getId(),
+ s.state.bufferData->getPixelFormat(),
+ s.state.bufferData->getUsage());
+ }
t.states.emplace_back(s);
}
diff --git a/services/surfaceflinger/Tracing/tools/run.sh b/services/surfaceflinger/Tracing/tools/run.sh
index baa93f1..307a4d8 100644
--- a/services/surfaceflinger/Tracing/tools/run.sh
+++ b/services/surfaceflinger/Tracing/tools/run.sh
@@ -5,7 +5,15 @@
# Build, push and run layertracegenerator
$ANDROID_BUILD_TOP/build/soong/soong_ui.bash --make-mode layertracegenerator
adb wait-for-device && adb push $OUT/system/bin/layertracegenerator /data/layertracegenerator
-echo "Writing transaction trace to file"
-adb shell service call SurfaceFlinger 1041 i32 0
-adb shell /data/layertracegenerator
+
+if [ -z "$1" ]
+ then
+ echo "Writing transaction trace to file"
+ adb shell service call SurfaceFlinger 1041 i32 0
+ adb shell /data/layertracegenerator
+ else
+ echo "Pushing transaction trace to device"
+ adb push $1 /data/transaction_trace.winscope
+ adb shell /data/layertracegenerator /data/transaction_trace.winscope
+fi
adb pull /data/misc/wmtrace/layers_trace.winscope
\ No newline at end of file
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.cpp b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
index a1313e3..292083b 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.cpp
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
@@ -17,7 +17,6 @@
#include <ftl/small_vector.h>
#include <gui/ISurfaceComposer.h>
-#include "SurfaceFlinger.h"
#include "WindowInfosListenerInvoker.h"
namespace android {
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.h b/services/surfaceflinger/WindowInfosListenerInvoker.h
index a1d66a1..d60a9c4 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.h
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.h
@@ -16,6 +16,8 @@
#pragma once
+#include <unordered_set>
+
#include <android/gui/BnWindowInfosReportedListener.h>
#include <android/gui/IWindowInfosListener.h>
#include <android/gui/IWindowInfosReportedListener.h>
@@ -49,8 +51,6 @@
static constexpr size_t kStaticCapacity = 3;
ftl::SmallMap<wp<IBinder>, const sp<gui::IWindowInfosListener>, kStaticCapacity>
mWindowInfosListeners GUARDED_BY(mListenersMutex);
-
- sp<gui::IWindowInfosReportedListener> mWindowInfosReportedListener;
};
} // namespace android
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
index 61fb29a..80486a2 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -109,8 +109,7 @@
thread->setDuration((std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>(),
(std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>());
thread->registerDisplayEventConnection(connection);
- thread->onScreenAcquired();
- thread->onScreenReleased();
+ thread->enableSyntheticVsync(mFdp.ConsumeBool());
dump<android::impl::EventThread>(thread.get(), &mFdp);
}
diff --git a/services/surfaceflinger/tests/WindowInfosListener_test.cpp b/services/surfaceflinger/tests/WindowInfosListener_test.cpp
index 53c3c39..d71486f 100644
--- a/services/surfaceflinger/tests/WindowInfosListener_test.cpp
+++ b/services/surfaceflinger/tests/WindowInfosListener_test.cpp
@@ -18,61 +18,61 @@
#include <gui/SurfaceComposerClient.h>
#include <private/android_filesystem_config.h>
#include <future>
-#include "utils/TransactionUtils.h"
namespace android {
using Transaction = SurfaceComposerClient::Transaction;
using gui::DisplayInfo;
using gui::WindowInfo;
+using WindowInfosPredicate = std::function<bool(const std::vector<WindowInfo>&)>;
+
class WindowInfosListenerTest : public ::testing::Test {
protected:
void SetUp() override {
seteuid(AID_SYSTEM);
mClient = sp<SurfaceComposerClient>::make();
- mWindowInfosListener = sp<SyncWindowInfosListener>::make();
- mClient->addWindowInfosListener(mWindowInfosListener);
}
- void TearDown() override {
- mClient->removeWindowInfosListener(mWindowInfosListener);
- seteuid(AID_ROOT);
- }
+ void TearDown() override { seteuid(AID_ROOT); }
- struct SyncWindowInfosListener : public gui::WindowInfosListener {
+ struct WindowInfosListener : public gui::WindowInfosListener {
public:
+ WindowInfosListener(WindowInfosPredicate predicate, std::promise<void>& promise)
+ : mPredicate(std::move(predicate)), mPromise(promise) {}
+
void onWindowInfosChanged(const std::vector<WindowInfo>& windowInfos,
const std::vector<DisplayInfo>&) override {
- windowInfosPromise.set_value(windowInfos);
- }
-
- std::vector<WindowInfo> waitForWindowInfos() {
- std::future<std::vector<WindowInfo>> windowInfosFuture =
- windowInfosPromise.get_future();
- std::vector<WindowInfo> windowInfos = windowInfosFuture.get();
- windowInfosPromise = std::promise<std::vector<WindowInfo>>();
- return windowInfos;
+ if (mPredicate(windowInfos)) {
+ mPromise.set_value();
+ }
}
private:
- std::promise<std::vector<WindowInfo>> windowInfosPromise;
+ WindowInfosPredicate mPredicate;
+ std::promise<void>& mPromise;
};
sp<SurfaceComposerClient> mClient;
- sp<SyncWindowInfosListener> mWindowInfosListener;
+
+ bool waitForWindowInfosPredicate(WindowInfosPredicate predicate) {
+ std::promise<void> promise;
+ auto listener = sp<WindowInfosListener>::make(std::move(predicate), promise);
+ mClient->addWindowInfosListener(listener);
+ auto future = promise.get_future();
+ bool satisfied = future.wait_for(std::chrono::seconds{1}) == std::future_status::ready;
+ mClient->removeWindowInfosListener(listener);
+ return satisfied;
+ }
};
std::optional<WindowInfo> findMatchingWindowInfo(WindowInfo targetWindowInfo,
std::vector<WindowInfo> windowInfos) {
- std::optional<WindowInfo> foundWindowInfo = std::nullopt;
for (WindowInfo windowInfo : windowInfos) {
if (windowInfo.token == targetWindowInfo.token) {
- foundWindowInfo = std::make_optional<>(windowInfo);
- break;
+ return windowInfo;
}
}
-
- return foundWindowInfo;
+ return std::nullopt;
}
TEST_F(WindowInfosListenerTest, WindowInfoAddedAndRemoved) {
@@ -92,15 +92,17 @@
.setInputWindowInfo(surfaceControl, windowInfo)
.apply();
- std::vector<WindowInfo> windowInfos = mWindowInfosListener->waitForWindowInfos();
- std::optional<WindowInfo> foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos);
- ASSERT_NE(std::nullopt, foundWindowInfo);
+ auto windowPresent = [&](const std::vector<WindowInfo>& windowInfos) {
+ return findMatchingWindowInfo(windowInfo, windowInfos).has_value();
+ };
+ ASSERT_TRUE(waitForWindowInfosPredicate(windowPresent));
Transaction().reparent(surfaceControl, nullptr).apply();
- windowInfos = mWindowInfosListener->waitForWindowInfos();
- foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos);
- ASSERT_EQ(std::nullopt, foundWindowInfo);
+ auto windowNotPresent = [&](const std::vector<WindowInfo>& windowInfos) {
+ return !findMatchingWindowInfo(windowInfo, windowInfos).has_value();
+ };
+ ASSERT_TRUE(waitForWindowInfosPredicate(windowNotPresent));
}
TEST_F(WindowInfosListenerTest, WindowInfoChanged) {
@@ -121,19 +123,28 @@
.setInputWindowInfo(surfaceControl, windowInfo)
.apply();
- std::vector<WindowInfo> windowInfos = mWindowInfosListener->waitForWindowInfos();
- std::optional<WindowInfo> foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos);
- ASSERT_NE(std::nullopt, foundWindowInfo);
- ASSERT_TRUE(foundWindowInfo->touchableRegion.isEmpty());
+ auto windowIsPresentAndTouchableRegionEmpty = [&](const std::vector<WindowInfo>& windowInfos) {
+ auto foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos);
+ if (!foundWindowInfo) {
+ return false;
+ }
+ return foundWindowInfo->touchableRegion.isEmpty();
+ };
+ ASSERT_TRUE(waitForWindowInfosPredicate(windowIsPresentAndTouchableRegionEmpty));
Rect touchableRegions(0, 0, 50, 50);
windowInfo.addTouchableRegion(Rect(0, 0, 50, 50));
Transaction().setInputWindowInfo(surfaceControl, windowInfo).apply();
- windowInfos = mWindowInfosListener->waitForWindowInfos();
- foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos);
- ASSERT_NE(std::nullopt, foundWindowInfo);
- ASSERT_TRUE(foundWindowInfo->touchableRegion.hasSameRects(windowInfo.touchableRegion));
+ auto windowIsPresentAndTouchableRegionMatches =
+ [&](const std::vector<WindowInfo>& windowInfos) {
+ auto foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos);
+ if (!foundWindowInfo) {
+ return false;
+ }
+ return foundWindowInfo->touchableRegion.hasSameRects(windowInfo.touchableRegion);
+ };
+ ASSERT_TRUE(waitForWindowInfosPredicate(windowIsPresentAndTouchableRegionMatches));
}
} // namespace android
diff --git a/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp b/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
index 0e214af..5f9214c 100644
--- a/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
+++ b/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
@@ -91,6 +91,8 @@
uint64_t curr_frame;
float x;
float y;
+ uint32_t bufferWidth;
+ uint32_t bufferHeight;
};
bool operator==(const LayerInfo& lh, const LayerInfo& rh) {
@@ -105,7 +107,8 @@
inline void PrintTo(const LayerInfo& info, ::std::ostream* os) {
*os << "Layer [" << info.id << "] name=" << info.name << " parent=" << info.parent
<< " z=" << info.z << " curr_frame=" << info.curr_frame << " x=" << info.x
- << " y=" << info.y;
+ << " y=" << info.y << " bufferWidth=" << info.bufferWidth
+ << " bufferHeight=" << info.bufferHeight;
}
struct find_id : std::unary_function<LayerInfo, bool> {
@@ -114,6 +117,18 @@
bool operator()(LayerInfo const& m) const { return m.id == id; }
};
+static LayerInfo getLayerInfoFromProto(::android::surfaceflinger::LayerProto& proto) {
+ return {proto.id(),
+ proto.name(),
+ proto.parent(),
+ proto.z(),
+ proto.curr_frame(),
+ proto.has_position() ? proto.position().x() : -1,
+ proto.has_position() ? proto.position().y() : -1,
+ proto.has_active_buffer() ? proto.active_buffer().width() : 0,
+ proto.has_active_buffer() ? proto.active_buffer().height() : 0};
+}
+
TEST_P(TransactionTraceTestSuite, validateEndState) {
ASSERT_GT(mActualLayersTraceProto.entry_size(), 0);
ASSERT_GT(mExpectedLayersTraceProto.entry_size(), 0);
@@ -128,10 +143,7 @@
expectedLayers.reserve(static_cast<size_t>(expectedLastEntry.layers().layers_size()));
for (int i = 0; i < expectedLastEntry.layers().layers_size(); i++) {
auto layer = expectedLastEntry.layers().layers(i);
- expectedLayers.push_back({layer.id(), layer.name(), layer.parent(), layer.z(),
- layer.curr_frame(),
- layer.has_position() ? layer.position().x() : -1,
- layer.has_position() ? layer.position().y() : -1});
+ expectedLayers.push_back(getLayerInfoFromProto(layer));
}
std::sort(expectedLayers.begin(), expectedLayers.end(), compareById);
@@ -139,10 +151,7 @@
actualLayers.reserve(static_cast<size_t>(actualLastEntry.layers().layers_size()));
for (int i = 0; i < actualLastEntry.layers().layers_size(); i++) {
auto layer = actualLastEntry.layers().layers(i);
- actualLayers.push_back({layer.id(), layer.name(), layer.parent(), layer.z(),
- layer.curr_frame(),
- layer.has_position() ? layer.position().x() : -1,
- layer.has_position() ? layer.position().y() : -1});
+ actualLayers.push_back(getLayerInfoFromProto(layer));
}
std::sort(actualLayers.begin(), actualLayers.end(), compareById);
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 87c3c65..eac9edc 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -105,6 +105,7 @@
"SurfaceFlinger_DisplayTransactionCommitTest.cpp",
"SurfaceFlinger_GetDisplayNativePrimariesTest.cpp",
"SurfaceFlinger_HotplugTest.cpp",
+ "SurfaceFlinger_MultiDisplayLeaderTest.cpp",
"SurfaceFlinger_NotifyPowerBoostTest.cpp",
"SurfaceFlinger_OnInitializeDisplaysTest.cpp",
"SurfaceFlinger_PowerHintTest.cpp",
@@ -133,6 +134,7 @@
"VSyncPredictorTest.cpp",
"VSyncReactorTest.cpp",
"VsyncConfigurationTest.cpp",
+ "VsyncScheduleTest.cpp",
],
}
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 17ef6ff..19a93e1 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -284,13 +284,16 @@
constexpr auto kDisplayConnectionType = ui::DisplayConnectionType::Internal;
constexpr bool kIsPrimary = true;
- test->mDisplay = FakeDisplayDeviceInjector(test->mFlinger, compositionDisplay,
- kDisplayConnectionType, HWC_DISPLAY, kIsPrimary)
- .setDisplaySurface(test->mDisplaySurface)
- .setNativeWindow(test->mNativeWindow)
- .setSecure(Derived::IS_SECURE)
- .setPowerMode(Derived::INIT_POWER_MODE)
- .inject();
+ test->mDisplay =
+ FakeDisplayDeviceInjector(test->mFlinger, compositionDisplay,
+ kDisplayConnectionType, HWC_DISPLAY, kIsPrimary)
+ .setDisplaySurface(test->mDisplaySurface)
+ .setNativeWindow(test->mNativeWindow)
+ .setSecure(Derived::IS_SECURE)
+ .setPowerMode(Derived::INIT_POWER_MODE)
+ .setRefreshRateSelector(test->mFlinger.scheduler()->refreshRateSelector())
+ .skipRegisterDisplay()
+ .inject();
Mock::VerifyAndClear(test->mNativeWindow.get());
constexpr bool kIsInternal = kDisplayConnectionType == ui::DisplayConnectionType::Internal;
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index 23a8bd1..57d1d9c 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -503,14 +503,16 @@
static constexpr auto GET_IDENTIFICATION_DATA = getInternalEdid;
};
-template <bool hasIdentificationData>
-struct ExternalDisplay {
- static constexpr auto CONNECTION_TYPE = ui::DisplayConnectionType::External;
+template <ui::DisplayConnectionType connectionType, bool hasIdentificationData>
+struct SecondaryDisplay {
+ static constexpr auto CONNECTION_TYPE = connectionType;
static constexpr Primary PRIMARY = Primary::FALSE;
static constexpr uint8_t PORT = 254;
static constexpr HWDisplayId HWC_DISPLAY_ID = 1002;
static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData;
- static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid;
+ static constexpr auto GET_IDENTIFICATION_DATA =
+ connectionType == ui::DisplayConnectionType::Internal ? getInternalEdid
+ : getExternalEdid;
};
struct TertiaryDisplay {
@@ -521,7 +523,16 @@
};
using PrimaryDisplayVariant = PhysicalDisplayVariant<PrimaryDisplay<false>, 3840, 2160>;
-using ExternalDisplayVariant = PhysicalDisplayVariant<ExternalDisplay<false>, 1920, 1280>;
+
+using InnerDisplayVariant = PhysicalDisplayVariant<PrimaryDisplay<true>, 1840, 2208>;
+using OuterDisplayVariant =
+ PhysicalDisplayVariant<SecondaryDisplay<ui::DisplayConnectionType::Internal, true>, 1080,
+ 2092>;
+
+using ExternalDisplayVariant =
+ PhysicalDisplayVariant<SecondaryDisplay<ui::DisplayConnectionType::External, false>, 1920,
+ 1280>;
+
using TertiaryDisplayVariant = PhysicalDisplayVariant<TertiaryDisplay, 1600, 1200>;
// A virtual display not supported by the HWC.
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 4b15385..422fa1c 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -122,12 +122,6 @@
EXPECT_CALL(*mEventThread, onHotplugReceived(_, _)).Times(0);
mScheduler->onHotplugReceived(handle, kDisplayId1, false);
- EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(0);
- mScheduler->onScreenAcquired(handle);
-
- EXPECT_CALL(*mEventThread, onScreenReleased()).Times(0);
- mScheduler->onScreenReleased(handle);
-
std::string output;
EXPECT_CALL(*mEventThread, dump(_)).Times(0);
mScheduler->dump(handle, output);
@@ -147,12 +141,6 @@
EXPECT_CALL(*mEventThread, onHotplugReceived(kDisplayId1, false)).Times(1);
mScheduler->onHotplugReceived(mConnectionHandle, kDisplayId1, false);
- EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(1);
- mScheduler->onScreenAcquired(mConnectionHandle);
-
- EXPECT_CALL(*mEventThread, onScreenReleased()).Times(1);
- mScheduler->onScreenReleased(mConnectionHandle);
-
std::string output("dump");
EXPECT_CALL(*mEventThread, dump(output)).Times(1);
mScheduler->dump(mConnectionHandle, output);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_MultiDisplayLeaderTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_MultiDisplayLeaderTest.cpp
new file mode 100644
index 0000000..9c58943
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_MultiDisplayLeaderTest.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace {
+
+struct MultiDisplayLeaderTest : DisplayTransactionTest {
+ static constexpr bool kWithMockScheduler = false;
+ MultiDisplayLeaderTest() : DisplayTransactionTest(kWithMockScheduler) {}
+};
+
+TEST_F(MultiDisplayLeaderTest, foldable) {
+ injectMockScheduler(InnerDisplayVariant::DISPLAY_ID::get());
+
+ // Inject inner and outer displays with uninitialized power modes.
+ sp<DisplayDevice> innerDisplay, outerDisplay;
+ constexpr bool kInitPowerMode = false;
+ {
+ InnerDisplayVariant::injectHwcDisplay<kInitPowerMode>(this);
+ auto injector = InnerDisplayVariant::makeFakeExistingDisplayInjector(this);
+ injector.setPowerMode(std::nullopt);
+ injector.setRefreshRateSelector(mFlinger.scheduler()->refreshRateSelector());
+ innerDisplay = injector.inject();
+ }
+ {
+ OuterDisplayVariant::injectHwcDisplay<kInitPowerMode>(this);
+ auto injector = OuterDisplayVariant::makeFakeExistingDisplayInjector(this);
+ injector.setPowerMode(std::nullopt);
+ outerDisplay = injector.inject();
+ }
+
+ // When the device boots, the inner display should be the leader.
+ ASSERT_EQ(mFlinger.scheduler()->leaderDisplayId(), innerDisplay->getPhysicalId());
+
+ // ...and should still be after powering on.
+ mFlinger.setPowerModeInternal(innerDisplay, PowerMode::ON);
+ ASSERT_EQ(mFlinger.scheduler()->leaderDisplayId(), innerDisplay->getPhysicalId());
+
+ // The outer display should become the leader after folding.
+ mFlinger.setPowerModeInternal(innerDisplay, PowerMode::OFF);
+ mFlinger.setPowerModeInternal(outerDisplay, PowerMode::ON);
+ ASSERT_EQ(mFlinger.scheduler()->leaderDisplayId(), outerDisplay->getPhysicalId());
+
+ // The inner display should become the leader after unfolding.
+ mFlinger.setPowerModeInternal(outerDisplay, PowerMode::OFF);
+ mFlinger.setPowerModeInternal(innerDisplay, PowerMode::ON);
+ ASSERT_EQ(mFlinger.scheduler()->leaderDisplayId(), innerDisplay->getPhysicalId());
+
+ // The inner display should stay the leader if both are powered on.
+ // TODO(b/256196556): The leader should depend on the displays' VSYNC phases.
+ mFlinger.setPowerModeInternal(outerDisplay, PowerMode::ON);
+ ASSERT_EQ(mFlinger.scheduler()->leaderDisplayId(), innerDisplay->getPhysicalId());
+
+ // The outer display should become the leader if designated.
+ mFlinger.scheduler()->setLeaderDisplay(outerDisplay->getPhysicalId());
+ ASSERT_EQ(mFlinger.scheduler()->leaderDisplayId(), outerDisplay->getPhysicalId());
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
index 9699fc2..7839ef0 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
@@ -92,6 +92,8 @@
.setDisplaySurface(mDisplaySurface)
.setNativeWindow(mNativeWindow)
.setPowerMode(hal::PowerMode::ON)
+ .setRefreshRateSelector(mFlinger.scheduler()->refreshRateSelector())
+ .skipRegisterDisplay()
.inject();
}
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
index a0aaa68..17b4714 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
@@ -59,47 +59,34 @@
};
struct EventThreadBaseSupportedVariant {
- static void setupVsyncAndEventThreadNoCallExpectations(DisplayTransactionTest* test) {
- // The callback should not be notified to toggle VSYNC.
+ static void setupVsyncNoCallExpectations(DisplayTransactionTest* test) {
+ // Expect no change to hardware nor synthetic VSYNC.
EXPECT_CALL(test->mFlinger.mockSchedulerCallback(), setVsyncEnabled(_)).Times(0);
-
- // The event thread should not be notified.
- EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(0);
- EXPECT_CALL(*test->mEventThread, onScreenAcquired()).Times(0);
+ EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(_)).Times(0);
}
};
struct EventThreadNotSupportedVariant : public EventThreadBaseSupportedVariant {
- static void setupAcquireAndEnableVsyncCallExpectations(DisplayTransactionTest* test) {
- // These calls are only expected for the primary display.
-
- // Instead expect no calls.
- setupVsyncAndEventThreadNoCallExpectations(test);
+ static void setupEnableVsyncCallExpectations(DisplayTransactionTest* test) {
+ setupVsyncNoCallExpectations(test);
}
- static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) {
- // These calls are only expected for the primary display.
-
- // Instead expect no calls.
- setupVsyncAndEventThreadNoCallExpectations(test);
+ static void setupDisableVsyncCallExpectations(DisplayTransactionTest* test) {
+ setupVsyncNoCallExpectations(test);
}
};
struct EventThreadIsSupportedVariant : public EventThreadBaseSupportedVariant {
- static void setupAcquireAndEnableVsyncCallExpectations(DisplayTransactionTest* test) {
- // The callback should be notified to enable VSYNC.
+ static void setupEnableVsyncCallExpectations(DisplayTransactionTest* test) {
+ // Expect to enable hardware VSYNC and disable synthetic VSYNC.
EXPECT_CALL(test->mFlinger.mockSchedulerCallback(), setVsyncEnabled(true)).Times(1);
-
- // The event thread should be notified that the screen was acquired.
- EXPECT_CALL(*test->mEventThread, onScreenAcquired()).Times(1);
+ EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(false)).Times(1);
}
- static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) {
- // The callback should be notified to disable VSYNC.
+ static void setupDisableVsyncCallExpectations(DisplayTransactionTest* test) {
+ // Expect to disable hardware VSYNC and enable synthetic VSYNC.
EXPECT_CALL(test->mFlinger.mockSchedulerCallback(), setVsyncEnabled(false)).Times(1);
-
- // The event thread should not be notified that the screen was released.
- EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(1);
+ EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(true)).Times(1);
}
};
@@ -133,7 +120,7 @@
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
- Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
+ Case::EventThread::setupEnableVsyncCallExpectations(test);
Case::DispSync::setupResetModelCallExpectations(test);
Case::setupRepaintEverythingCallExpectations(test);
}
@@ -148,7 +135,7 @@
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND);
- Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
+ Case::EventThread::setupVsyncNoCallExpectations(test);
Case::setupRepaintEverythingCallExpectations(test);
}
@@ -160,7 +147,7 @@
struct TransitionOnToOffVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::OFF> {
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test);
+ Case::EventThread::setupDisableVsyncCallExpectations(test);
Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF);
}
@@ -173,7 +160,7 @@
: public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::OFF> {
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
+ Case::EventThread::setupVsyncNoCallExpectations(test);
Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF);
}
@@ -185,7 +172,7 @@
struct TransitionOnToDozeVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE> {
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
+ Case::EventThread::setupVsyncNoCallExpectations(test);
Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
}
};
@@ -194,7 +181,7 @@
: public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::DOZE> {
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
+ Case::EventThread::setupEnableVsyncCallExpectations(test);
Case::DispSync::setupResetModelCallExpectations(test);
Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
}
@@ -203,7 +190,7 @@
struct TransitionDozeToOnVariant : public TransitionVariantCommon<PowerMode::DOZE, PowerMode::ON> {
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
+ Case::EventThread::setupVsyncNoCallExpectations(test);
Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
}
};
@@ -212,7 +199,7 @@
: public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::ON> {
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
+ Case::EventThread::setupEnableVsyncCallExpectations(test);
Case::DispSync::setupResetModelCallExpectations(test);
Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
}
@@ -222,7 +209,7 @@
: public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE_SUSPEND> {
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test);
+ Case::EventThread::setupDisableVsyncCallExpectations(test);
Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND);
}
};
@@ -231,7 +218,7 @@
: public TransitionVariantCommon<PowerMode::ON, static_cast<PowerMode>(POWER_MODE_LEET)> {
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
+ Case::EventThread::setupVsyncNoCallExpectations(test);
Case::setupNoComposerPowerModeCallExpectations(test);
}
};
@@ -484,38 +471,5 @@
transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToUnknownVariant>>();
}
-// TODO(b/262417075)
-TEST_F(SetPowerModeInternalTest, DISABLED_designatesLeaderDisplay) {
- using Case = SimplePrimaryDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // Inject a primary display with uninitialized power mode.
- constexpr bool kInitPowerMode = false;
- Case::Display::injectHwcDisplay<kInitPowerMode>(this);
- auto injector = Case::Display::makeFakeExistingDisplayInjector(this);
- injector.setPowerMode(std::nullopt);
- const auto display = injector.inject();
-
- // --------------------------------------------------------------------
- // Invocation
-
- // FakeDisplayDeviceInjector registers the display with Scheduler, so it has already been
- // designated as the leader. Set an arbitrary leader to verify that `setPowerModeInternal`
- // designates a leader regardless of any preceding `Scheduler::registerDisplay` call(s).
- constexpr PhysicalDisplayId kPlaceholderId = PhysicalDisplayId::fromPort(42);
- ASSERT_NE(display->getPhysicalId(), kPlaceholderId);
- mFlinger.scheduler()->setLeaderDisplay(kPlaceholderId);
-
- mFlinger.setPowerModeInternal(display, PowerMode::ON);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The primary display should be designated as the leader.
- EXPECT_EQ(mFlinger.scheduler()->leaderDisplayId(), display->getPhysicalId());
-}
-
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index bd3f3ca..48c5d48 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -91,6 +91,7 @@
Scheduler::setLeaderDisplay(displayId);
}
+ auto& mutableAppConnectionHandle() { return mAppConnectionHandle; }
auto& mutableVsyncModulator() { return *mVsyncModulator; }
auto& mutableLayerHistory() { return mLayerHistory; }
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 8703359..0649333 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -252,7 +252,10 @@
mScheduler->initVsync(mScheduler->getVsyncSchedule().getDispatch(), *mTokenManager, 0ms);
- mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
+ mScheduler->mutableAppConnectionHandle() =
+ mScheduler->createConnection(std::move(appEventThread));
+
+ mFlinger->mAppConnectionHandle = mScheduler->mutableAppConnectionHandle();
mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));
resetScheduler(mScheduler);
}
@@ -852,6 +855,11 @@
return *this;
}
+ auto& skipRegisterDisplay() {
+ mRegisterDisplay = false;
+ return *this;
+ }
+
sp<DisplayDevice> inject() NO_THREAD_SAFETY_ANALYSIS {
const auto displayId = mCreationArgs.compositionDisplay->getDisplayId();
@@ -915,7 +923,7 @@
ui::ColorModes(),
std::nullopt);
- if (mFlinger.scheduler()) {
+ if (mFlinger.scheduler() && mRegisterDisplay) {
mFlinger.scheduler()->registerDisplay(physicalId,
display->holdRefreshRateSelector());
}
@@ -934,6 +942,7 @@
sp<BBinder> mDisplayToken = sp<BBinder>::make();
DisplayDeviceCreationArgs mCreationArgs;
DisplayModes mDisplayModes;
+ bool mRegisterDisplay = true;
const std::optional<ui::DisplayConnectionType> mConnectionType;
const std::optional<hal::HWDisplayId> mHwcDisplayId;
};
diff --git a/services/surfaceflinger/tests/unittests/VsyncScheduleTest.cpp b/services/surfaceflinger/tests/unittests/VsyncScheduleTest.cpp
new file mode 100644
index 0000000..652d313
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VsyncScheduleTest.cpp
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <ftl/fake_guard.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+
+#include <scheduler/Fps.h>
+#include "Scheduler/VsyncSchedule.h"
+#include "ThreadContext.h"
+#include "mock/MockSchedulerCallback.h"
+#include "mock/MockVSyncDispatch.h"
+#include "mock/MockVSyncTracker.h"
+#include "mock/MockVsyncController.h"
+
+using testing::_;
+
+namespace android {
+
+class VsyncScheduleTest : public testing::Test {
+protected:
+ VsyncScheduleTest();
+ ~VsyncScheduleTest() override;
+
+ scheduler::mock::SchedulerCallback mCallback;
+ const std::unique_ptr<scheduler::VsyncSchedule> mVsyncSchedule =
+ std::unique_ptr<scheduler::VsyncSchedule>(
+ new scheduler::VsyncSchedule(std::make_unique<mock::VSyncTracker>(),
+ std::make_unique<mock::VSyncDispatch>(),
+ std::make_unique<mock::VsyncController>()));
+
+ mock::VsyncController& getController() {
+ return *static_cast<mock::VsyncController*>(&mVsyncSchedule->getController());
+ }
+};
+
+VsyncScheduleTest::VsyncScheduleTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+}
+
+VsyncScheduleTest::~VsyncScheduleTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+}
+
+namespace {
+
+using namespace testing;
+
+TEST_F(VsyncScheduleTest, InitiallyDisallowed) {
+ ASSERT_FALSE(mVsyncSchedule->isHardwareVsyncAllowed(false /* makeAllowed */));
+}
+
+TEST_F(VsyncScheduleTest, EnableDoesNothingWhenDisallowed) {
+ EXPECT_CALL(mCallback, setVsyncEnabled(_)).Times(0);
+
+ mVsyncSchedule->enableHardwareVsync(mCallback);
+}
+
+TEST_F(VsyncScheduleTest, DisableDoesNothingWhenDisallowed) {
+ EXPECT_CALL(mCallback, setVsyncEnabled(_)).Times(0);
+
+ mVsyncSchedule->disableHardwareVsync(mCallback, false /* disallow */);
+}
+
+TEST_F(VsyncScheduleTest, MakeAllowed) {
+ ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+}
+
+TEST_F(VsyncScheduleTest, DisableDoesNothingWhenDisabled) {
+ ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+ EXPECT_CALL(mCallback, setVsyncEnabled(_)).Times(0);
+
+ mVsyncSchedule->disableHardwareVsync(mCallback, false /* disallow */);
+}
+
+TEST_F(VsyncScheduleTest, EnableWorksWhenDisabled) {
+ ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+ EXPECT_CALL(mCallback, setVsyncEnabled(true));
+
+ mVsyncSchedule->enableHardwareVsync(mCallback);
+}
+
+TEST_F(VsyncScheduleTest, EnableWorksOnce) {
+ ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+ EXPECT_CALL(mCallback, setVsyncEnabled(true));
+
+ mVsyncSchedule->enableHardwareVsync(mCallback);
+
+ EXPECT_CALL(mCallback, setVsyncEnabled(_)).Times(0);
+ mVsyncSchedule->enableHardwareVsync(mCallback);
+}
+
+TEST_F(VsyncScheduleTest, AllowedIsSticky) {
+ ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+ ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(false /* makeAllowed */));
+}
+
+TEST_F(VsyncScheduleTest, EnableDisable) {
+ ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+ EXPECT_CALL(mCallback, setVsyncEnabled(true));
+
+ mVsyncSchedule->enableHardwareVsync(mCallback);
+
+ EXPECT_CALL(mCallback, setVsyncEnabled(false));
+ mVsyncSchedule->disableHardwareVsync(mCallback, false /* disallow */);
+}
+
+TEST_F(VsyncScheduleTest, StartPeriodTransition) {
+ // Note: startPeriodTransition is only called when hardware vsyncs are
+ // allowed.
+ ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+
+ const Period period = (60_Hz).getPeriod();
+
+ EXPECT_CALL(mCallback, setVsyncEnabled(true));
+ EXPECT_CALL(getController(), startPeriodTransition(period.ns()));
+
+ mVsyncSchedule->startPeriodTransition(mCallback, period);
+}
+
+TEST_F(VsyncScheduleTest, StartPeriodTransitionAlreadyEnabled) {
+ ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+ mVsyncSchedule->enableHardwareVsync(mCallback);
+
+ const Period period = (60_Hz).getPeriod();
+
+ EXPECT_CALL(mCallback, setVsyncEnabled(_)).Times(0);
+ EXPECT_CALL(getController(), startPeriodTransition(period.ns()));
+
+ mVsyncSchedule->startPeriodTransition(mCallback, period);
+}
+
+TEST_F(VsyncScheduleTest, AddResyncSampleDisallowed) {
+ const Period period = (60_Hz).getPeriod();
+ const auto timestamp = TimePoint::now();
+
+ EXPECT_CALL(mCallback, setVsyncEnabled(_)).Times(0);
+ EXPECT_CALL(getController(), addHwVsyncTimestamp(_, _, _)).Times(0);
+
+ mVsyncSchedule->addResyncSample(mCallback, timestamp, period);
+}
+
+TEST_F(VsyncScheduleTest, AddResyncSampleDisabled) {
+ ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+ const Period period = (60_Hz).getPeriod();
+ const auto timestamp = TimePoint::now();
+
+ EXPECT_CALL(mCallback, setVsyncEnabled(_)).Times(0);
+ EXPECT_CALL(getController(), addHwVsyncTimestamp(_, _, _)).Times(0);
+
+ mVsyncSchedule->addResyncSample(mCallback, timestamp, period);
+}
+
+TEST_F(VsyncScheduleTest, AddResyncSampleReturnsTrue) {
+ ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+ mVsyncSchedule->enableHardwareVsync(mCallback);
+
+ const Period period = (60_Hz).getPeriod();
+ const auto timestamp = TimePoint::now();
+
+ EXPECT_CALL(mCallback, setVsyncEnabled(_)).Times(0);
+ EXPECT_CALL(getController(),
+ addHwVsyncTimestamp(timestamp.ns(), std::optional<nsecs_t>(period.ns()), _))
+ .WillOnce(Return(true));
+
+ mVsyncSchedule->addResyncSample(mCallback, timestamp, period);
+}
+
+TEST_F(VsyncScheduleTest, AddResyncSampleReturnsFalse) {
+ ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+ mVsyncSchedule->enableHardwareVsync(mCallback);
+
+ const Period period = (60_Hz).getPeriod();
+ const auto timestamp = TimePoint::now();
+
+ EXPECT_CALL(mCallback, setVsyncEnabled(false));
+ EXPECT_CALL(getController(),
+ addHwVsyncTimestamp(timestamp.ns(), std::optional<nsecs_t>(period.ns()), _))
+ .WillOnce(Return(false));
+
+ mVsyncSchedule->addResyncSample(mCallback, timestamp, period);
+}
+
+TEST_F(VsyncScheduleTest, PendingState) FTL_FAKE_GUARD(kMainThreadContext) {
+ ASSERT_FALSE(mVsyncSchedule->getPendingHardwareVsyncState());
+ mVsyncSchedule->setPendingHardwareVsyncState(true);
+ ASSERT_TRUE(mVsyncSchedule->getPendingHardwareVsyncState());
+
+ mVsyncSchedule->setPendingHardwareVsyncState(false);
+ ASSERT_FALSE(mVsyncSchedule->getPendingHardwareVsyncState());
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index f8567bd..8026a7a 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -31,8 +31,7 @@
MOCK_CONST_METHOD2(createEventConnection,
sp<EventThreadConnection>(ResyncCallback, EventRegistrationFlags));
- MOCK_METHOD0(onScreenReleased, void());
- MOCK_METHOD0(onScreenAcquired, void());
+ MOCK_METHOD(void, enableSyntheticVsync, (bool), (override));
MOCK_METHOD2(onHotplugReceived, void(PhysicalDisplayId, bool));
MOCK_METHOD1(onModeChanged, void(const scheduler::FrameRateMode &));
MOCK_METHOD2(onFrameRateOverridesChanged,
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
index 7d4b159..103beb5 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
@@ -18,7 +18,7 @@
#include <gmock/gmock.h>
-#include "Scheduler/Scheduler.h"
+#include "Scheduler/ISchedulerCallback.h"
namespace android::scheduler::mock {