Merge "Don't overdim SDR content in an HLG screenshot." into udc-dev
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 8ca927e..5dbf7ac 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -3362,23 +3362,25 @@
return;
}
- // Include the proto logging from WMShell.
- RunCommand(
- // Empty name because it's not intended to be classified as a bugreport section.
- // Actual logging files can be found as "/data/misc/wmtrace/shell_log.winscope"
- // in the bugreport.
- "", {"dumpsys", "activity", "service", "SystemUIService",
- "WMShell", "protolog", "save-for-bugreport"},
- CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build());
+ const std::vector<std::vector<std::string>> dumpTracesForBugReportCommands = {
+ {"dumpsys", "activity", "service", "SystemUIService", "WMShell", "protolog",
+ "save-for-bugreport"},
+ {"dumpsys", "activity", "service", "SystemUIService", "WMShell", "transitions", "tracing",
+ "save-for-bugreport"},
+ {"cmd", "input_method", "tracing", "save-for-bugreport"},
+ {"cmd", "window", "tracing", "save-for-bugreport"},
+ {"cmd", "window", "shell", "tracing", "save-for-bugreport"},
+ };
- for (const auto& service : {"input_method", "window", "window shell"}) {
+ for (const auto& command : dumpTracesForBugReportCommands) {
RunCommand(
// Empty name because it's not intended to be classified as a bugreport section.
// Actual tracing files can be found in "/data/misc/wmtrace/" in the bugreport.
- "", {"cmd", service, "tracing", "save-for-bugreport"},
+ "", command,
CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build());
}
+ // This command needs to be run as root
static const auto SURFACEFLINGER_COMMAND_SAVE_ALL_TRACES = std::vector<std::string> {
"service", "call", "SurfaceFlinger", "1042"
};
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index 93d8cdf..5cbcf9f 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -997,17 +997,22 @@
EXPECT_FALSE(ds.dump_pool_);
}
-TEST_F(DumpstateBaseTest, PreDumpUiData) {
- // SurfaceFlinger's transactions trace is always enabled, i.e. it is always pre-dumped
- static const auto kTransactionsTrace =
- std::filesystem::path {"/data/misc/wmtrace/transactions_trace.winscope"};
+TEST_F(DumpstateTest, PreDumpUiData) {
+ // These traces are always enabled, i.e. they are always pre-dumped
+ const std::vector<std::filesystem::path> uiTraces = {
+ std::filesystem::path{"/data/misc/wmtrace/transactions_trace.winscope"},
+ std::filesystem::path{"/data/misc/wmtrace/transition_trace.winscope"},
+ std::filesystem::path{"/data/misc/wmtrace/shell_transition_trace.winscope"},
+ };
- std::system(("rm " + kTransactionsTrace.string()).c_str());
- EXPECT_FALSE(std::filesystem::exists(kTransactionsTrace));
+ for (const auto traceFile : uiTraces) {
+ std::system(("rm -f " + traceFile.string()).c_str());
+ EXPECT_FALSE(std::filesystem::exists(traceFile)) << traceFile << " was not deleted.";
- Dumpstate& ds_ = Dumpstate::GetInstance();
- ds_.PreDumpUiData();
- EXPECT_TRUE(std::filesystem::exists(kTransactionsTrace));
+ Dumpstate& ds_ = Dumpstate::GetInstance();
+ ds_.PreDumpUiData();
+ EXPECT_TRUE(std::filesystem::exists(traceFile)) << traceFile << " was not created.";
+ }
}
class ZippedBugReportStreamTest : public DumpstateBaseTest {
diff --git a/cmds/installd/utils_default.cpp b/cmds/installd/utils_default.cpp
index a6025e6..85ce450 100644
--- a/cmds/installd/utils_default.cpp
+++ b/cmds/installd/utils_default.cpp
@@ -23,7 +23,7 @@
// platform dependent logic.
int rm_package_dir(const std::string& package_dir) {
- return delete_dir_contents_and_dir(package_dir);
+ return rename_delete_dir_contents_and_dir(package_dir);
}
} // namespace installd
diff --git a/headers/media_plugin/media/hardware/VideoAPI.h b/headers/media_plugin/media/hardware/VideoAPI.h
index a090876..5466680 100644
--- a/headers/media_plugin/media/hardware/VideoAPI.h
+++ b/headers/media_plugin/media/hardware/VideoAPI.h
@@ -127,6 +127,8 @@
PrimariesBT601_6_525, // Rec.ITU-R BT.601-6 525 or equivalent
PrimariesGenericFilm, // Generic Film
PrimariesBT2020, // Rec.ITU-R BT.2020 or equivalent
+ PrimariesRP431, // SMPTE RP 431-2 (DCI P3)
+ PrimariesEG432, // SMPTE EG 432-1 (Display P3)
PrimariesOther = 0xff,
};
@@ -173,6 +175,8 @@
StandardBT2020Constant, // PrimariesBT2020 and MatrixBT2020Constant
StandardBT470M, // PrimariesBT470_6M and MatrixBT470_6M
StandardFilm, // PrimariesGenericFilm and KR=0.253, KB=0.068
+ StandardDisplayP3, // PrimariesEG432 and MatrixBT601_6
+ // StandardAdobeRGB, // for placeholder only (not used by media)
StandardOther = 0xff,
};
@@ -282,6 +286,8 @@
case ColorAspects::PrimariesBT601_6_525: return "BT601_6_525";
case ColorAspects::PrimariesGenericFilm: return "GenericFilm";
case ColorAspects::PrimariesBT2020: return "BT2020";
+ case ColorAspects::PrimariesRP431: return "RP431";
+ case ColorAspects::PrimariesEG432: return "EG432";
case ColorAspects::PrimariesOther: return "Other";
default: return def;
}
@@ -332,6 +338,8 @@
case ColorAspects::StandardBT2020Constant: return "BT2020Constant";
case ColorAspects::StandardBT470M: return "BT470M";
case ColorAspects::StandardFilm: return "Film";
+ case ColorAspects::StandardDisplayP3: return "DisplayP3";
+ // case ColorAspects::StandardAdobeRGB: return "AdobeRGB";
case ColorAspects::StandardOther: return "Other";
default: return def;
}
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index e4ba58a..cce2e46 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -54,6 +54,12 @@
* The caller takes ownership of the ASurfaceControl returned and must release it
* using ASurfaceControl_release below.
*
+ * By default the \a ASurfaceControl will be visible and display any buffer submitted. In
+ * addition, the default buffer submission control may release and not display all buffers
+ * that are submitted before receiving a callback for the previous buffer. See
+ * \a ASurfaceTransaction_setVisibility and \a ASurfaceTransaction_setEnableBackPressure to
+ * change the default behaviors after creation.
+ *
* Available since API level 29.
*/
ASurfaceControl* ASurfaceControl_createFromWindow(ANativeWindow* parent, const char* debug_name)
diff --git a/libs/binder/IUidObserver.cpp b/libs/binder/IUidObserver.cpp
index d952dc7..1c35f53 100644
--- a/libs/binder/IUidObserver.cpp
+++ b/libs/binder/IUidObserver.cpp
@@ -67,9 +67,10 @@
remote()->transact(ON_UID_STATE_CHANGED_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY);
}
- virtual void onUidProcAdjChanged(uid_t uid) {
+ virtual void onUidProcAdjChanged(uid_t uid, int32_t adj) {
Parcel data, reply;
data.writeInt32((int32_t)uid);
+ data.writeInt32((int32_t)adj);
remote()->transact(ON_UID_PROC_ADJ_CHANGED_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY);
}
};
@@ -121,7 +122,8 @@
case ON_UID_PROC_ADJ_CHANGED_TRANSACTION: {
CHECK_INTERFACE(IUidObserver, data, reply);
uid_t uid = data.readInt32();
- onUidProcAdjChanged(uid);
+ int32_t adj = data.readInt32();
+ onUidProcAdjChanged(uid, adj);
return NO_ERROR;
} break;
diff --git a/libs/binder/include_activitymanager/binder/IUidObserver.h b/libs/binder/include_activitymanager/binder/IUidObserver.h
index 17f03a9..5ea7447 100644
--- a/libs/binder/include_activitymanager/binder/IUidObserver.h
+++ b/libs/binder/include_activitymanager/binder/IUidObserver.h
@@ -34,7 +34,7 @@
virtual void onUidIdle(uid_t uid, bool disabled) = 0;
virtual void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq,
int32_t capability) = 0;
- virtual void onUidProcAdjChanged(uid_t uid) = 0;
+ virtual void onUidProcAdjChanged(uid_t uid, int32_t adj) = 0;
enum {
ON_UID_GONE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
diff --git a/libs/gui/VsyncEventData.cpp b/libs/gui/VsyncEventData.cpp
index 76c60c2..8e00c2f 100644
--- a/libs/gui/VsyncEventData.cpp
+++ b/libs/gui/VsyncEventData.cpp
@@ -23,8 +23,8 @@
namespace android::gui {
-static_assert(VsyncEventData::kFrameTimelinesLength == 7,
- "Must update value in DisplayEventReceiver.java#FRAME_TIMELINES_LENGTH (and here)");
+static_assert(VsyncEventData::kFrameTimelinesCapacity == 7,
+ "Must update value in DisplayEventReceiver.java#FRAME_TIMELINES_CAPACITY (and here)");
int64_t VsyncEventData::preferredVsyncId() const {
return frameTimelines[preferredFrameTimelineIndex].vsyncId;
@@ -46,11 +46,15 @@
SAFE_PARCEL(parcel->readInt64, &vsync.frameInterval);
- uint64_t uintPreferredFrameTimelineIndex;
- SAFE_PARCEL(parcel->readUint64, &uintPreferredFrameTimelineIndex);
+ uint32_t uintPreferredFrameTimelineIndex;
+ SAFE_PARCEL(parcel->readUint32, &uintPreferredFrameTimelineIndex);
vsync.preferredFrameTimelineIndex = static_cast<size_t>(uintPreferredFrameTimelineIndex);
- for (int i = 0; i < VsyncEventData::kFrameTimelinesLength; i++) {
+ uint32_t uintFrameTimelinesLength;
+ SAFE_PARCEL(parcel->readUint32, &uintFrameTimelinesLength);
+ vsync.frameTimelinesLength = static_cast<size_t>(uintFrameTimelinesLength);
+
+ for (size_t i = 0; i < vsync.frameTimelinesLength; i++) {
SAFE_PARCEL(parcel->readInt64, &vsync.frameTimelines[i].vsyncId);
SAFE_PARCEL(parcel->readInt64, &vsync.frameTimelines[i].deadlineTimestamp);
SAFE_PARCEL(parcel->readInt64, &vsync.frameTimelines[i].expectedPresentationTime);
@@ -60,8 +64,9 @@
}
status_t ParcelableVsyncEventData::writeToParcel(Parcel* parcel) const {
SAFE_PARCEL(parcel->writeInt64, vsync.frameInterval);
- SAFE_PARCEL(parcel->writeUint64, vsync.preferredFrameTimelineIndex);
- for (int i = 0; i < VsyncEventData::kFrameTimelinesLength; i++) {
+ SAFE_PARCEL(parcel->writeUint32, vsync.preferredFrameTimelineIndex);
+ SAFE_PARCEL(parcel->writeUint32, vsync.frameTimelinesLength);
+ for (size_t i = 0; i < vsync.frameTimelinesLength; i++) {
SAFE_PARCEL(parcel->writeInt64, vsync.frameTimelines[i].vsyncId);
SAFE_PARCEL(parcel->writeInt64, vsync.frameTimelines[i].deadlineTimestamp);
SAFE_PARCEL(parcel->writeInt64, vsync.frameTimelines[i].expectedPresentationTime);
diff --git a/libs/gui/fuzzer/libgui_displayEvent_fuzzer.cpp b/libs/gui/fuzzer/libgui_displayEvent_fuzzer.cpp
index 6d5ae49..6e4f074 100644
--- a/libs/gui/fuzzer/libgui_displayEvent_fuzzer.cpp
+++ b/libs/gui/fuzzer/libgui_displayEvent_fuzzer.cpp
@@ -51,7 +51,7 @@
event.vsync.count = fdp->ConsumeIntegral<uint32_t>();
event.vsync.vsyncData.frameInterval = fdp->ConsumeIntegral<uint64_t>();
event.vsync.vsyncData.preferredFrameTimelineIndex = fdp->ConsumeIntegral<uint32_t>();
- for (size_t idx = 0; idx < gui::VsyncEventData::kFrameTimelinesLength; ++idx) {
+ for (size_t idx = 0; idx < gui::VsyncEventData::kFrameTimelinesCapacity; ++idx) {
event.vsync.vsyncData.frameTimelines[idx].vsyncId = fdp->ConsumeIntegral<int64_t>();
event.vsync.vsyncData.frameTimelines[idx].deadlineTimestamp =
fdp->ConsumeIntegral<uint64_t>();
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 5c88a07..a6f503e 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -233,9 +233,10 @@
// Geometry updates.
static constexpr uint64_t GEOMETRY_CHANGES = layer_state_t::eBufferCropChanged |
- layer_state_t::eBufferTransformChanged | layer_state_t::eCropChanged |
- layer_state_t::eDestinationFrameChanged | layer_state_t::eMatrixChanged |
- layer_state_t::ePositionChanged | layer_state_t::eTransformToDisplayInverseChanged |
+ layer_state_t::eBufferTransformChanged | layer_state_t::eCornerRadiusChanged |
+ layer_state_t::eCropChanged | layer_state_t::eDestinationFrameChanged |
+ layer_state_t::eMatrixChanged | layer_state_t::ePositionChanged |
+ layer_state_t::eTransformToDisplayInverseChanged |
layer_state_t::eTransparentRegionChanged;
// Buffer and related updates.
diff --git a/libs/gui/include/gui/VsyncEventData.h b/libs/gui/include/gui/VsyncEventData.h
index dfdae21..b40a840 100644
--- a/libs/gui/include/gui/VsyncEventData.h
+++ b/libs/gui/include/gui/VsyncEventData.h
@@ -24,8 +24,8 @@
// Plain Old Data (POD) vsync data structure. For example, it can be easily used in the
// DisplayEventReceiver::Event union.
struct VsyncEventData {
- // Max amount of frame timelines is arbitrarily set to be reasonable.
- static constexpr int64_t kFrameTimelinesLength = 7;
+ // Max capacity of frame timelines is arbitrarily set to be reasonable.
+ static constexpr int64_t kFrameTimelinesCapacity = 7;
// The current frame interval in ns when this frame was scheduled.
int64_t frameInterval;
@@ -33,6 +33,9 @@
// Index into the frameTimelines that represents the platform's preferred frame timeline.
uint32_t preferredFrameTimelineIndex;
+ // Size of frame timelines provided by the platform; max is kFrameTimelinesCapacity.
+ uint32_t frameTimelinesLength;
+
struct alignas(8) FrameTimeline {
// The Vsync Id corresponsing to this vsync event. This will be used to
// populate ISurfaceComposer::setFrameTimelineVsync and
@@ -45,7 +48,7 @@
// The anticipated Vsync presentation time in nanos.
int64_t expectedPresentationTime;
- } frameTimelines[kFrameTimelinesLength]; // Sorted possible frame timelines.
+ } frameTimelines[kFrameTimelinesCapacity]; // Sorted possible frame timelines.
// Gets the preferred frame timeline's vsync ID.
int64_t preferredVsyncId() const;
diff --git a/libs/gui/tests/DisplayEventStructLayout_test.cpp b/libs/gui/tests/DisplayEventStructLayout_test.cpp
index da88463..3949d70 100644
--- a/libs/gui/tests/DisplayEventStructLayout_test.cpp
+++ b/libs/gui/tests/DisplayEventStructLayout_test.cpp
@@ -35,6 +35,7 @@
CHECK_OFFSET(DisplayEventReceiver::Event::VSync, count, 0);
CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameInterval, 8);
CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.preferredFrameTimelineIndex, 16);
+ CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelinesLength, 20);
CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelines, 24);
CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelines[0].vsyncId, 24);
CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelines[0].deadlineTimestamp,
@@ -44,16 +45,16 @@
// Also test the offsets of the last frame timeline. A loop is not used because the non-const
// index cannot be used in static_assert.
const int lastFrameTimelineOffset = /* Start of array */ 24 +
- (VsyncEventData::kFrameTimelinesLength - 1) * /* Size of FrameTimeline */ 24;
+ (VsyncEventData::kFrameTimelinesCapacity - 1) * /* Size of FrameTimeline */ 24;
CHECK_OFFSET(DisplayEventReceiver::Event::VSync,
- vsyncData.frameTimelines[VsyncEventData::kFrameTimelinesLength - 1].vsyncId,
+ vsyncData.frameTimelines[VsyncEventData::kFrameTimelinesCapacity - 1].vsyncId,
lastFrameTimelineOffset);
CHECK_OFFSET(DisplayEventReceiver::Event::VSync,
- vsyncData.frameTimelines[VsyncEventData::kFrameTimelinesLength - 1]
+ vsyncData.frameTimelines[VsyncEventData::kFrameTimelinesCapacity - 1]
.deadlineTimestamp,
lastFrameTimelineOffset + 8);
CHECK_OFFSET(DisplayEventReceiver::Event::VSync,
- vsyncData.frameTimelines[VsyncEventData::kFrameTimelinesLength - 1]
+ vsyncData.frameTimelines[VsyncEventData::kFrameTimelinesCapacity - 1]
.expectedPresentationTime,
lastFrameTimelineOffset + 16);
diff --git a/libs/gui/tests/VsyncEventData_test.cpp b/libs/gui/tests/VsyncEventData_test.cpp
index f114522..a2138f2 100644
--- a/libs/gui/tests/VsyncEventData_test.cpp
+++ b/libs/gui/tests/VsyncEventData_test.cpp
@@ -36,6 +36,7 @@
FrameTimeline timeline1 = FrameTimeline{4, 5, 6};
data.vsync.frameTimelines[0] = timeline0;
data.vsync.frameTimelines[1] = timeline1;
+ data.vsync.frameTimelinesLength = 2;
Parcel p;
data.writeToParcel(&p);
@@ -45,7 +46,8 @@
data2.readFromParcel(&p);
ASSERT_EQ(data.vsync.frameInterval, data2.vsync.frameInterval);
ASSERT_EQ(data.vsync.preferredFrameTimelineIndex, data2.vsync.preferredFrameTimelineIndex);
- for (int i = 0; i < VsyncEventData::kFrameTimelinesLength; i++) {
+ ASSERT_EQ(data.vsync.frameTimelinesLength, data2.vsync.frameTimelinesLength);
+ for (int i = 0; i < VsyncEventData::kFrameTimelinesCapacity; i++) {
ASSERT_EQ(data.vsync.frameTimelines[i].vsyncId, data2.vsync.frameTimelines[i].vsyncId);
ASSERT_EQ(data.vsync.frameTimelines[i].deadlineTimestamp,
data2.vsync.frameTimelines[i].deadlineTimestamp);
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 66a40f1..8f005a5 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -197,7 +197,7 @@
AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
"Data is only valid in callback");
- return VsyncEventData::kFrameTimelinesLength;
+ return frameCallbackData->vsyncEventData.frameTimelinesLength;
}
size_t AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex(
const AChoreographerFrameCallbackData* data) {
@@ -213,7 +213,7 @@
AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
"Data is only valid in callback");
- LOG_ALWAYS_FATAL_IF(index >= VsyncEventData::kFrameTimelinesLength, "Index out of bounds");
+ LOG_ALWAYS_FATAL_IF(index >= VsyncEventData::kFrameTimelinesCapacity, "Index out of bounds");
return frameCallbackData->vsyncEventData.frameTimelines[index].vsyncId;
}
int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentationTimeNanos(
@@ -222,7 +222,7 @@
AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
"Data is only valid in callback");
- LOG_ALWAYS_FATAL_IF(index >= VsyncEventData::kFrameTimelinesLength, "Index out of bounds");
+ LOG_ALWAYS_FATAL_IF(index >= VsyncEventData::kFrameTimelinesCapacity, "Index out of bounds");
return frameCallbackData->vsyncEventData.frameTimelines[index].expectedPresentationTime;
}
int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos(
@@ -231,7 +231,7 @@
AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
"Data is only valid in callback");
- LOG_ALWAYS_FATAL_IF(index >= VsyncEventData::kFrameTimelinesLength, "Index out of bounds");
+ LOG_ALWAYS_FATAL_IF(index >= VsyncEventData::kFrameTimelinesCapacity, "Index out of bounds");
return frameCallbackData->vsyncEventData.frameTimelines[index].deadlineTimestamp;
}
diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp
index 932be56..c412c9c 100644
--- a/libs/renderengine/skia/AutoBackendTexture.cpp
+++ b/libs/renderengine/skia/AutoBackendTexture.cpp
@@ -43,10 +43,12 @@
createProtectedImage, backendFormat,
isOutputBuffer);
mColorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(desc.format);
- ALOGE_IF(!mBackendTexture.isValid(),
- "Failed to create a valid texture. [%p]:[%d,%d] isProtected:%d isWriteable:%d "
- "format:%d",
- this, desc.width, desc.height, createProtectedImage, isOutputBuffer, desc.format);
+ if (!mBackendTexture.isValid() || !desc.width || !desc.height) {
+ LOG_ALWAYS_FATAL("Failed to create a valid texture. [%p]:[%d,%d] isProtected:%d "
+ "isWriteable:%d format:%d",
+ this, desc.width, desc.height, createProtectedImage, isOutputBuffer,
+ desc.format);
+ }
}
AutoBackendTexture::~AutoBackendTexture() {
diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp
index ba190e0..980f8d1 100644
--- a/libs/sensor/SensorManager.cpp
+++ b/libs/sensor/SensorManager.cpp
@@ -176,11 +176,8 @@
mSensors = mSensorServer->getSensorList(mOpPackageName);
size_t count = mSensors.size();
- if (count == 0) {
- ALOGE("Failed to get Sensor list");
- mSensorServer.clear();
- return UNKNOWN_ERROR;
- }
+ // If count is 0, mSensorList will be non-null. This is old
+ // existing behavior and callers expect this.
mSensorList =
static_cast<Sensor const**>(malloc(count * sizeof(Sensor*)));
LOG_ALWAYS_FATAL_IF(mSensorList == nullptr, "mSensorList NULL");
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 472d7a1..ddebcad 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -57,9 +57,8 @@
* The event flow is via the "InputListener" interface, as follows:
* InputReader -> UnwantedInteractionBlocker -> InputProcessor -> InputDispatcher
*/
-InputManager::InputManager(
- const sp<InputReaderPolicyInterface>& readerPolicy,
- const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
+InputManager::InputManager(const sp<InputReaderPolicyInterface>& readerPolicy,
+ InputDispatcherPolicyInterface& dispatcherPolicy) {
mDispatcher = createInputDispatcher(dispatcherPolicy);
mProcessor = std::make_unique<InputProcessor>(*mDispatcher);
mBlocker = std::make_unique<UnwantedInteractionBlocker>(*mProcessor);
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index 793757d..b6ad419 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -100,9 +100,8 @@
~InputManager() override;
public:
- InputManager(
- const sp<InputReaderPolicyInterface>& readerPolicy,
- const sp<InputDispatcherPolicyInterface>& dispatcherPolicy);
+ InputManager(const sp<InputReaderPolicyInterface>& readerPolicy,
+ InputDispatcherPolicyInterface& dispatcherPolicy);
status_t start() override;
status_t stop() override;
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index f852001..cf299c0 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -48,10 +48,8 @@
class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
public:
- FakeInputDispatcherPolicy() {}
-
-protected:
- virtual ~FakeInputDispatcherPolicy() {}
+ FakeInputDispatcherPolicy() = default;
+ virtual ~FakeInputDispatcherPolicy() = default;
private:
void notifyConfigurationChanged(nsecs_t) override {}
@@ -82,24 +80,23 @@
void notifyVibratorState(int32_t deviceId, bool isOn) override {}
- void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override {
- *outConfig = mConfig;
+ InputDispatcherConfiguration getDispatcherConfiguration() override { return mConfig; }
+
+ bool filterInputEvent(const InputEvent& inputEvent, uint32_t policyFlags) override {
+ return true; // dispatch event normally
}
- bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override {
- return true;
- }
-
- void interceptKeyBeforeQueueing(const KeyEvent*, uint32_t&) override {}
+ void interceptKeyBeforeQueueing(const KeyEvent&, uint32_t&) override {}
void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {}
- nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent*, uint32_t) override {
+ nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent&, uint32_t) override {
return 0;
}
- bool dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent*, uint32_t, KeyEvent*) override {
- return false;
+ std::optional<KeyEvent> dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent&,
+ uint32_t) override {
+ return {};
}
void notifySwitch(nsecs_t, uint32_t, uint32_t, uint32_t) override {}
@@ -258,7 +255,7 @@
static void benchmarkNotifyMotion(benchmark::State& state) {
// Create dispatcher
- sp<FakeInputDispatcherPolicy> fakePolicy = sp<FakeInputDispatcherPolicy>::make();
+ FakeInputDispatcherPolicy fakePolicy;
InputDispatcher dispatcher(fakePolicy);
dispatcher.setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
dispatcher.start();
@@ -293,7 +290,7 @@
static void benchmarkInjectMotion(benchmark::State& state) {
// Create dispatcher
- sp<FakeInputDispatcherPolicy> fakePolicy = sp<FakeInputDispatcherPolicy>::make();
+ FakeInputDispatcherPolicy fakePolicy;
InputDispatcher dispatcher(fakePolicy);
dispatcher.setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
dispatcher.start();
@@ -327,7 +324,7 @@
static void benchmarkOnWindowInfosChanged(benchmark::State& state) {
// Create dispatcher
- sp<FakeInputDispatcherPolicy> fakePolicy = sp<FakeInputDispatcherPolicy>::make();
+ FakeInputDispatcherPolicy fakePolicy;
InputDispatcher dispatcher(fakePolicy);
dispatcher.setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
dispatcher.start();
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 97b57b4..e4f367d 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -54,6 +54,7 @@
#define INDENT4 " "
using namespace android::ftl::flag_operators;
+using android::base::Error;
using android::base::HwTimeoutMultiplier;
using android::base::Result;
using android::base::StringPrintf;
@@ -129,48 +130,68 @@
AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
}
-bool isValidKeyAction(int32_t action) {
+Result<void> checkKeyAction(int32_t action) {
switch (action) {
case AKEY_EVENT_ACTION_DOWN:
case AKEY_EVENT_ACTION_UP:
- return true;
+ return {};
default:
- return false;
+ return Error() << "Key event has invalid action code " << action;
}
}
-bool validateKeyEvent(int32_t action) {
- if (!isValidKeyAction(action)) {
- ALOGE("Key event has invalid action code 0x%x", action);
- return false;
- }
- return true;
+Result<void> validateKeyEvent(int32_t action) {
+ return checkKeyAction(action);
}
-bool isValidMotionAction(int32_t action, int32_t actionButton, int32_t pointerCount) {
+Result<void> checkMotionAction(int32_t action, int32_t actionButton, int32_t pointerCount) {
switch (MotionEvent::getActionMasked(action)) {
case AMOTION_EVENT_ACTION_DOWN:
- case AMOTION_EVENT_ACTION_UP:
- return pointerCount == 1;
+ case AMOTION_EVENT_ACTION_UP: {
+ if (pointerCount != 1) {
+ return Error() << "invalid pointer count " << pointerCount;
+ }
+ return {};
+ }
case AMOTION_EVENT_ACTION_MOVE:
case AMOTION_EVENT_ACTION_HOVER_ENTER:
case AMOTION_EVENT_ACTION_HOVER_MOVE:
- case AMOTION_EVENT_ACTION_HOVER_EXIT:
- return pointerCount >= 1;
+ case AMOTION_EVENT_ACTION_HOVER_EXIT: {
+ if (pointerCount < 1) {
+ return Error() << "invalid pointer count " << pointerCount;
+ }
+ return {};
+ }
case AMOTION_EVENT_ACTION_CANCEL:
case AMOTION_EVENT_ACTION_OUTSIDE:
case AMOTION_EVENT_ACTION_SCROLL:
- return true;
+ return {};
case AMOTION_EVENT_ACTION_POINTER_DOWN:
case AMOTION_EVENT_ACTION_POINTER_UP: {
const int32_t index = MotionEvent::getActionIndex(action);
- return index >= 0 && index < pointerCount && pointerCount > 1;
+ if (index < 0) {
+ return Error() << "invalid index " << index << " for "
+ << MotionEvent::actionToString(action);
+ }
+ if (index >= pointerCount) {
+ return Error() << "invalid index " << index << " for pointerCount " << pointerCount;
+ }
+ if (pointerCount <= 1) {
+ return Error() << "invalid pointer count " << pointerCount << " for "
+ << MotionEvent::actionToString(action);
+ }
+ return {};
}
case AMOTION_EVENT_ACTION_BUTTON_PRESS:
- case AMOTION_EVENT_ACTION_BUTTON_RELEASE:
- return actionButton != 0;
+ case AMOTION_EVENT_ACTION_BUTTON_RELEASE: {
+ if (actionButton == 0) {
+ return Error() << "action button should be nonzero for "
+ << MotionEvent::actionToString(action);
+ }
+ return {};
+ }
default:
- return false;
+ return Error() << "invalid action " << action;
}
}
@@ -178,32 +199,50 @@
return std::chrono::duration_cast<std::chrono::milliseconds>(t).count();
}
-bool validateMotionEvent(int32_t action, int32_t actionButton, size_t pointerCount,
- const PointerProperties* pointerProperties) {
- if (!isValidMotionAction(action, actionButton, pointerCount)) {
- ALOGE("Motion event has invalid action code 0x%x", action);
- return false;
+Result<void> validateMotionEvent(int32_t action, int32_t actionButton, size_t pointerCount,
+ const PointerProperties* pointerProperties) {
+ Result<void> actionCheck = checkMotionAction(action, actionButton, pointerCount);
+ if (!actionCheck.ok()) {
+ return actionCheck;
}
if (pointerCount < 1 || pointerCount > MAX_POINTERS) {
- ALOGE("Motion event has invalid pointer count %zu; value must be between 1 and %zu.",
- pointerCount, MAX_POINTERS);
- return false;
+ return Error() << "Motion event has invalid pointer count " << pointerCount
+ << "; value must be between 1 and " << MAX_POINTERS << ".";
}
std::bitset<MAX_POINTER_ID + 1> pointerIdBits;
for (size_t i = 0; i < pointerCount; i++) {
int32_t id = pointerProperties[i].id;
if (id < 0 || id > MAX_POINTER_ID) {
- ALOGE("Motion event has invalid pointer id %d; value must be between 0 and %d", id,
- MAX_POINTER_ID);
- return false;
+ return Error() << "Motion event has invalid pointer id " << id
+ << "; value must be between 0 and " << MAX_POINTER_ID;
}
if (pointerIdBits.test(id)) {
- ALOGE("Motion event has duplicate pointer id %d", id);
- return false;
+ return Error() << "Motion event has duplicate pointer id " << id;
}
pointerIdBits.set(id);
}
- return true;
+ return {};
+}
+
+Result<void> validateInputEvent(const InputEvent& event) {
+ switch (event.getType()) {
+ case InputEventType::KEY: {
+ const KeyEvent& key = static_cast<const KeyEvent&>(event);
+ const int32_t action = key.getAction();
+ return validateKeyEvent(action);
+ }
+ case InputEventType::MOTION: {
+ const MotionEvent& motion = static_cast<const MotionEvent&>(event);
+ const int32_t action = motion.getAction();
+ const size_t pointerCount = motion.getPointerCount();
+ const PointerProperties* pointerProperties = motion.getPointerProperties();
+ const int32_t actionButton = motion.getActionButton();
+ return validateMotionEvent(action, actionButton, pointerCount, pointerProperties);
+ }
+ default: {
+ return {};
+ }
+ }
}
std::string dumpRegion(const Region& region) {
@@ -459,14 +498,14 @@
// Returns true if the event type passed as argument represents a user activity.
bool isUserActivityEvent(const EventEntry& eventEntry) {
switch (eventEntry.type) {
+ case EventEntry::Type::CONFIGURATION_CHANGED:
+ case EventEntry::Type::DEVICE_RESET:
+ case EventEntry::Type::DRAG:
case EventEntry::Type::FOCUS:
case EventEntry::Type::POINTER_CAPTURE_CHANGED:
- case EventEntry::Type::DRAG:
- case EventEntry::Type::TOUCH_MODE_CHANGED:
case EventEntry::Type::SENSOR:
- case EventEntry::Type::CONFIGURATION_CHANGED:
+ case EventEntry::Type::TOUCH_MODE_CHANGED:
return false;
- case EventEntry::Type::DEVICE_RESET:
case EventEntry::Type::KEY:
case EventEntry::Type::MOTION:
return true;
@@ -646,10 +685,10 @@
// --- InputDispatcher ---
-InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy)
+InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy)
: InputDispatcher(policy, STALE_EVENT_TIMEOUT) {}
-InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy,
+InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy,
std::chrono::nanoseconds staleEventTimeout)
: mPolicy(policy),
mPendingEvent(nullptr),
@@ -676,7 +715,6 @@
SurfaceComposerClient::getDefault()->addWindowInfosListener(mWindowInfoListener);
#endif
mKeyRepeatState.lastKeyEntry = nullptr;
- policy->getDispatcherConfiguration(&mConfig);
}
InputDispatcher::~InputDispatcher() {
@@ -1402,7 +1440,7 @@
// Enqueue a command to run outside the lock to tell the policy that the configuration changed.
auto command = [this, eventTime = entry.eventTime]() REQUIRES(mLock) {
scoped_unlock unlock(mLock);
- mPolicy->notifyConfigurationChanged(eventTime);
+ mPolicy.notifyConfigurationChanged(eventTime);
};
postCommandLocked(std::move(command));
return true;
@@ -1423,6 +1461,11 @@
CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS, "device was reset");
options.deviceId = entry.deviceId;
synthesizeCancelationEventsForAllConnectionsLocked(options);
+
+ // Remove all active pointers from this device
+ for (auto& [_, touchState] : mTouchStatesByDisplay) {
+ touchState.removeAllPointersForDevice(entry.deviceId);
+ }
return true;
}
@@ -1646,6 +1689,8 @@
doInterceptKeyBeforeDispatchingCommand(focusedWindowToken, *entry);
};
postCommandLocked(std::move(command));
+ // Poke user activity for keys not passed to user
+ pokeUserActivityLocked(*entry);
return false; // wait for the command to run
} else {
entry->interceptKeyResult = KeyEntry::InterceptKeyResult::CONTINUE;
@@ -1662,6 +1707,8 @@
*dropReason == DropReason::POLICY ? InputEventInjectionResult::SUCCEEDED
: InputEventInjectionResult::FAILED);
mReporter->reportDroppedKey(entry->id);
+ // Poke user activity for undispatched keys
+ pokeUserActivityLocked(*entry);
return true;
}
@@ -1717,10 +1764,10 @@
scoped_unlock unlock(mLock);
if (entry->accuracyChanged) {
- mPolicy->notifySensorAccuracy(entry->deviceId, entry->sensorType, entry->accuracy);
+ mPolicy.notifySensorAccuracy(entry->deviceId, entry->sensorType, entry->accuracy);
}
- mPolicy->notifySensorEvent(entry->deviceId, entry->sensorType, entry->accuracy,
- entry->hwTimestamp, entry->values);
+ mPolicy.notifySensorEvent(entry->deviceId, entry->sensorType, entry->accuracy,
+ entry->hwTimestamp, entry->values);
};
postCommandLocked(std::move(command));
}
@@ -2165,7 +2212,7 @@
// event injection will be allowed.
const int32_t displayId = entry.displayId;
const int32_t action = entry.action;
- const int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
+ const int32_t maskedAction = MotionEvent::getActionMasked(action);
// Update the touch state as needed based on the properties of the touch event.
outInjectionResult = InputEventInjectionResult::PENDING;
@@ -2292,16 +2339,11 @@
continue;
}
- if (isHoverAction) {
+ if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
+ maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
const int32_t pointerId = entry.pointerProperties[0].id;
- if (maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT) {
- // Pointer left. Remove it
- tempTouchState.removeHoveringPointer(entry.deviceId, pointerId);
- } else {
- // The "windowHandle" is the target of this hovering pointer.
- tempTouchState.addHoveringPointerToWindow(windowHandle, entry.deviceId,
- pointerId);
- }
+ // The "windowHandle" is the target of this hovering pointer.
+ tempTouchState.addHoveringPointerToWindow(windowHandle, entry.deviceId, pointerId);
}
// Set target flags.
@@ -2666,9 +2708,6 @@
if (uint32_t(pointerIndex) == entry.pointerCount) {
LOG_ALWAYS_FATAL("Should find a valid pointer index by id %d", mDragState->pointerId);
- sendDropWindowCommandLocked(nullptr, 0, 0);
- mDragState.reset();
- return;
}
const int32_t maskedAction = entry.action & AMOTION_EVENT_ACTION_MASK;
@@ -2980,13 +3019,11 @@
}
int32_t displayId = getTargetDisplayId(eventEntry);
sp<WindowInfoHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId);
+ const WindowInfo* windowDisablingUserActivityInfo = nullptr;
if (focusedWindowHandle != nullptr) {
const WindowInfo* info = focusedWindowHandle->getInfo();
if (info->inputConfig.test(WindowInfo::InputConfig::DISABLE_USER_ACTIVITY)) {
- if (DEBUG_DISPATCH_CYCLE) {
- ALOGD("Not poking user activity: disabled by window '%s'.", info->name.c_str());
- }
- return;
+ windowDisablingUserActivityInfo = info;
}
}
@@ -2997,7 +3034,13 @@
if (motionEntry.action == AMOTION_EVENT_ACTION_CANCEL) {
return;
}
-
+ if (windowDisablingUserActivityInfo != nullptr) {
+ if (DEBUG_DISPATCH_CYCLE) {
+ ALOGD("Not poking user activity: disabled by window '%s'.",
+ windowDisablingUserActivityInfo->name.c_str());
+ }
+ return;
+ }
if (MotionEvent::isTouchEvent(motionEntry.source, motionEntry.action)) {
eventType = USER_ACTIVITY_EVENT_TOUCH;
}
@@ -3008,6 +3051,22 @@
if (keyEntry.flags & AKEY_EVENT_FLAG_CANCELED) {
return;
}
+ // If the key code is unknown, we don't consider it user activity
+ if (keyEntry.keyCode == AKEYCODE_UNKNOWN) {
+ return;
+ }
+ // Don't inhibit events that were intercepted or are not passed to
+ // the apps, like system shortcuts
+ if (windowDisablingUserActivityInfo != nullptr &&
+ keyEntry.interceptKeyResult != KeyEntry::InterceptKeyResult::SKIP &&
+ keyEntry.policyFlags & POLICY_FLAG_PASS_TO_USER) {
+ if (DEBUG_DISPATCH_CYCLE) {
+ ALOGD("Not poking user activity: disabled by window '%s'.",
+ windowDisablingUserActivityInfo->name.c_str());
+ }
+ return;
+ }
+
eventType = USER_ACTIVITY_EVENT_BUTTON;
break;
}
@@ -3021,7 +3080,7 @@
auto command = [this, eventTime = eventEntry.eventTime, eventType, displayId]()
REQUIRES(mLock) {
scoped_unlock unlock(mLock);
- mPolicy->pokeUserActivity(eventTime, eventType, displayId);
+ mPolicy.pokeUserActivity(eventTime, eventType, displayId);
};
postCommandLocked(std::move(command));
}
@@ -3362,7 +3421,7 @@
auto command = [this, token]() REQUIRES(mLock) {
scoped_unlock unlock(mLock);
- mPolicy->onPointerDownOutsideFocus(token);
+ mPolicy.onPointerDownOutsideFocus(token);
};
postCommandLocked(std::move(command));
}
@@ -3573,7 +3632,7 @@
const std::array<uint8_t, 32> InputDispatcher::getSignature(
const MotionEntry& motionEntry, const DispatchEntry& dispatchEntry) const {
- const int32_t actionMasked = dispatchEntry.resolvedAction & AMOTION_EVENT_ACTION_MASK;
+ const int32_t actionMasked = MotionEvent::getActionMasked(dispatchEntry.resolvedAction);
if (actionMasked != AMOTION_EVENT_ACTION_UP && actionMasked != AMOTION_EVENT_ACTION_DOWN) {
// Only sign events up and down events as the purely move events
// are tied to their up/down counterparts so signing would be redundant.
@@ -3641,7 +3700,7 @@
auto command = [this, connection]() REQUIRES(mLock) {
scoped_unlock unlock(mLock);
- mPolicy->notifyInputChannelBroken(connection->inputChannel->getConnectionToken());
+ mPolicy.notifyInputChannelBroken(connection->inputChannel->getConnectionToken());
};
postCommandLocked(std::move(command));
}
@@ -4093,7 +4152,9 @@
args.id, args.eventTime, args.deviceId, inputEventSourceToString(args.source).c_str(),
args.displayId, args.policyFlags, KeyEvent::actionToString(args.action), args.flags,
KeyEvent::getLabel(args.keyCode), args.scanCode, args.metaState, args.downTime);
- if (!validateKeyEvent(args.action)) {
+ Result<void> keyCheck = validateKeyEvent(args.action);
+ if (!keyCheck.ok()) {
+ LOG(ERROR) << "invalid key event: " << keyCheck.error();
return;
}
@@ -4122,7 +4183,7 @@
args.eventTime);
android::base::Timer t;
- mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
+ mPolicy.interceptKeyBeforeQueueing(event, /*byref*/ policyFlags);
if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
ALOGW("Excessive delay in interceptKeyBeforeQueueing; took %s ms",
std::to_string(t.duration().count()).c_str());
@@ -4136,7 +4197,7 @@
mLock.unlock();
policyFlags |= POLICY_FLAG_FILTERED;
- if (!mPolicy->filterInputEvent(&event, policyFlags)) {
+ if (!mPolicy.filterInputEvent(event, policyFlags)) {
return; // event was consumed by the filter
}
@@ -4190,9 +4251,10 @@
}
}
- if (!validateMotionEvent(args.action, args.actionButton, args.pointerCount,
- args.pointerProperties)) {
- LOG(ERROR) << "Invalid event: " << args.dump();
+ Result<void> motionCheck = validateMotionEvent(args.action, args.actionButton,
+ args.pointerCount, args.pointerProperties);
+ if (!motionCheck.ok()) {
+ LOG(ERROR) << "Invalid event: " << args.dump() << "; reason: " << motionCheck.error();
return;
}
@@ -4200,7 +4262,7 @@
policyFlags |= POLICY_FLAG_TRUSTED;
android::base::Timer t;
- mPolicy->interceptMotionBeforeQueueing(args.displayId, args.eventTime, policyFlags);
+ mPolicy.interceptMotionBeforeQueueing(args.displayId, args.eventTime, policyFlags);
if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms",
std::to_string(t.duration().count()).c_str());
@@ -4239,7 +4301,7 @@
args.pointerProperties, args.pointerCoords);
policyFlags |= POLICY_FLAG_FILTERED;
- if (!mPolicy->filterInputEvent(&event, policyFlags)) {
+ if (!mPolicy.filterInputEvent(event, policyFlags)) {
return; // event was consumed by the filter
}
@@ -4305,7 +4367,7 @@
ALOGD("notifyVibratorState - eventTime=%" PRId64 ", device=%d, isOn=%d", args.eventTime,
args.deviceId, args.isOn);
}
- mPolicy->notifyVibratorState(args.deviceId, args.isOn);
+ mPolicy.notifyVibratorState(args.deviceId, args.isOn);
}
bool InputDispatcher::shouldSendMotionToInputFilterLocked(const NotifyMotionArgs& args) {
@@ -4321,7 +4383,7 @@
uint32_t policyFlags = args.policyFlags;
policyFlags |= POLICY_FLAG_TRUSTED;
- mPolicy->notifySwitch(args.eventTime, args.switchValues, args.switchMask, policyFlags);
+ mPolicy.notifySwitch(args.eventTime, args.switchValues, args.switchMask, policyFlags);
}
void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
@@ -4368,6 +4430,12 @@
InputEventInjectionSync syncMode,
std::chrono::milliseconds timeout,
uint32_t policyFlags) {
+ Result<void> eventValidation = validateInputEvent(*event);
+ if (!eventValidation.ok()) {
+ LOG(INFO) << "Injection failed: invalid event: " << eventValidation.error();
+ return InputEventInjectionResult::FAILED;
+ }
+
if (debugInboundEventDetails()) {
LOG(DEBUG) << __func__ << ": targetUid=" << toString(targetUid)
<< ", syncMode=" << ftl::enum_string(syncMode) << ", timeout=" << timeout.count()
@@ -4393,11 +4461,7 @@
switch (event->getType()) {
case InputEventType::KEY: {
const KeyEvent& incomingKey = static_cast<const KeyEvent&>(*event);
- int32_t action = incomingKey.getAction();
- if (!validateKeyEvent(action)) {
- return InputEventInjectionResult::FAILED;
- }
-
+ const int32_t action = incomingKey.getAction();
int32_t flags = incomingKey.getFlags();
if (policyFlags & POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY) {
flags |= AKEY_EVENT_FLAG_IS_ACCESSIBILITY_EVENT;
@@ -4418,7 +4482,7 @@
if (!(policyFlags & POLICY_FLAG_FILTERED)) {
android::base::Timer t;
- mPolicy->interceptKeyBeforeQueueing(&keyEvent, /*byref*/ policyFlags);
+ mPolicy.interceptKeyBeforeQueueing(keyEvent, /*byref*/ policyFlags);
if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
ALOGW("Excessive delay in interceptKeyBeforeQueueing; took %s ms",
std::to_string(t.duration().count()).c_str());
@@ -4439,25 +4503,18 @@
case InputEventType::MOTION: {
const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
- const int32_t action = motionEvent.getAction();
const bool isPointerEvent =
isFromSource(event->getSource(), AINPUT_SOURCE_CLASS_POINTER);
// If a pointer event has no displayId specified, inject it to the default display.
const uint32_t displayId = isPointerEvent && (event->getDisplayId() == ADISPLAY_ID_NONE)
? ADISPLAY_ID_DEFAULT
: event->getDisplayId();
- const size_t pointerCount = motionEvent.getPointerCount();
- const PointerProperties* pointerProperties = motionEvent.getPointerProperties();
- const int32_t actionButton = motionEvent.getActionButton();
int32_t flags = motionEvent.getFlags();
- if (!validateMotionEvent(action, actionButton, pointerCount, pointerProperties)) {
- return InputEventInjectionResult::FAILED;
- }
if (!(policyFlags & POLICY_FLAG_FILTERED)) {
nsecs_t eventTime = motionEvent.getEventTime();
android::base::Timer t;
- mPolicy->interceptMotionBeforeQueueing(displayId, eventTime, /*byref*/ policyFlags);
+ mPolicy.interceptMotionBeforeQueueing(displayId, eventTime, /*byref*/ policyFlags);
if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms",
std::to_string(t.duration().count()).c_str());
@@ -4474,8 +4531,9 @@
std::unique_ptr<MotionEntry> injectedEntry =
std::make_unique<MotionEntry>(motionEvent.getId(), *sampleEventTimes,
resolvedDeviceId, motionEvent.getSource(),
- displayId, policyFlags, action, actionButton,
- flags, motionEvent.getMetaState(),
+ displayId, policyFlags, motionEvent.getAction(),
+ motionEvent.getActionButton(), flags,
+ motionEvent.getMetaState(),
motionEvent.getButtonState(),
motionEvent.getClassification(),
motionEvent.getEdgeFlags(),
@@ -4483,18 +4541,22 @@
motionEvent.getYPrecision(),
motionEvent.getRawXCursorPosition(),
motionEvent.getRawYCursorPosition(),
- motionEvent.getDownTime(), uint32_t(pointerCount),
- pointerProperties, samplePointerCoords);
+ motionEvent.getDownTime(),
+ motionEvent.getPointerCount(),
+ motionEvent.getPointerProperties(),
+ samplePointerCoords);
transformMotionEntryForInjectionLocked(*injectedEntry, motionEvent.getTransform());
injectedEntries.push(std::move(injectedEntry));
for (size_t i = motionEvent.getHistorySize(); i > 0; i--) {
sampleEventTimes += 1;
- samplePointerCoords += pointerCount;
+ samplePointerCoords += motionEvent.getPointerCount();
std::unique_ptr<MotionEntry> nextInjectedEntry =
std::make_unique<MotionEntry>(motionEvent.getId(), *sampleEventTimes,
resolvedDeviceId, motionEvent.getSource(),
- displayId, policyFlags, action, actionButton,
- flags, motionEvent.getMetaState(),
+ displayId, policyFlags,
+ motionEvent.getAction(),
+ motionEvent.getActionButton(), flags,
+ motionEvent.getMetaState(),
motionEvent.getButtonState(),
motionEvent.getClassification(),
motionEvent.getEdgeFlags(),
@@ -4503,7 +4565,8 @@
motionEvent.getRawXCursorPosition(),
motionEvent.getRawYCursorPosition(),
motionEvent.getDownTime(),
- uint32_t(pointerCount), pointerProperties,
+ motionEvent.getPointerCount(),
+ motionEvent.getPointerProperties(),
samplePointerCoords);
transformMotionEntryForInjectionLocked(*nextInjectedEntry,
motionEvent.getTransform());
@@ -6008,7 +6071,7 @@
const sp<IBinder>& newToken) {
auto command = [this, oldToken, newToken]() REQUIRES(mLock) {
scoped_unlock unlock(mLock);
- mPolicy->notifyFocusChanged(oldToken, newToken);
+ mPolicy.notifyFocusChanged(oldToken, newToken);
};
postCommandLocked(std::move(command));
}
@@ -6016,7 +6079,7 @@
void InputDispatcher::sendDropWindowCommandLocked(const sp<IBinder>& token, float x, float y) {
auto command = [this, token, x, y]() REQUIRES(mLock) {
scoped_unlock unlock(mLock);
- mPolicy->notifyDropWindow(token, x, y);
+ mPolicy.notifyDropWindow(token, x, y);
};
postCommandLocked(std::move(command));
}
@@ -6063,7 +6126,7 @@
auto command = [this, application = std::move(application)]() REQUIRES(mLock) {
scoped_unlock unlock(mLock);
- mPolicy->notifyNoFocusedWindowAnr(application);
+ mPolicy.notifyNoFocusedWindowAnr(application);
};
postCommandLocked(std::move(command));
}
@@ -6103,8 +6166,7 @@
{ // release lock
scoped_unlock unlock(mLock);
android::base::Timer t;
- delay = mPolicy->interceptKeyBeforeDispatching(focusedWindowToken, &event,
- entry.policyFlags);
+ delay = mPolicy.interceptKeyBeforeDispatching(focusedWindowToken, event, entry.policyFlags);
if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
ALOGW("Excessive delay in interceptKeyBeforeDispatching; took %s ms",
std::to_string(t.duration().count()).c_str());
@@ -6126,7 +6188,7 @@
std::string reason) {
auto command = [this, token, pid, reason = std::move(reason)]() REQUIRES(mLock) {
scoped_unlock unlock(mLock);
- mPolicy->notifyWindowUnresponsive(token, pid, reason);
+ mPolicy.notifyWindowUnresponsive(token, pid, reason);
};
postCommandLocked(std::move(command));
}
@@ -6135,7 +6197,7 @@
std::optional<int32_t> pid) {
auto command = [this, token, pid]() REQUIRES(mLock) {
scoped_unlock unlock(mLock);
- mPolicy->notifyWindowResponsive(token, pid);
+ mPolicy.notifyWindowResponsive(token, pid);
};
postCommandLocked(std::move(command));
}
@@ -6219,8 +6281,12 @@
mLock.unlock();
- mPolicy->dispatchUnhandledKey(connection->inputChannel->getConnectionToken(), &event,
- keyEntry.policyFlags, &event);
+ if (const auto unhandledKeyFallback =
+ mPolicy.dispatchUnhandledKey(connection->inputChannel->getConnectionToken(),
+ event, keyEntry.policyFlags);
+ unhandledKeyFallback) {
+ event = *unhandledKeyFallback;
+ }
mLock.lock();
@@ -6260,9 +6326,13 @@
mLock.unlock();
- bool fallback =
- mPolicy->dispatchUnhandledKey(connection->inputChannel->getConnectionToken(),
- &event, keyEntry.policyFlags, &event);
+ bool fallback = false;
+ if (auto fb = mPolicy.dispatchUnhandledKey(connection->inputChannel->getConnectionToken(),
+ event, keyEntry.policyFlags);
+ fb) {
+ fallback = true;
+ event = *fb;
+ }
mLock.lock();
@@ -6514,7 +6584,7 @@
mCurrentPointerCaptureRequest.seq++;
auto command = [this, request = mCurrentPointerCaptureRequest]() REQUIRES(mLock) {
scoped_unlock unlock(mLock);
- mPolicy->setPointerCapture(request);
+ mPolicy.setPointerCapture(request);
};
postCommandLocked(std::move(command));
}
@@ -6606,6 +6676,13 @@
mLooper->wake();
}
+void InputDispatcher::requestRefreshConfiguration() {
+ InputDispatcherConfiguration config = mPolicy.getDispatcherConfiguration();
+
+ std::scoped_lock _l(mLock);
+ mConfig = config;
+}
+
void InputDispatcher::setMonitorDispatchingTimeoutForTest(std::chrono::nanoseconds timeout) {
std::scoped_lock _l(mLock);
mMonitorDispatchingTimeout = timeout;
@@ -6615,7 +6692,7 @@
const sp<WindowInfoHandle>& oldWindowHandle,
const sp<WindowInfoHandle>& newWindowHandle,
TouchState& state, int32_t pointerId,
- std::vector<InputTarget>& targets) {
+ std::vector<InputTarget>& targets) const {
std::bitset<MAX_POINTER_ID + 1> pointerIds;
pointerIds.set(pointerId);
const bool oldHasWallpaper = oldWindowHandle->getInfo()->inputConfig.test(
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 7aa1a2d..0e9ccfb 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -82,8 +82,8 @@
public:
static constexpr bool kDefaultInTouchMode = true;
- explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy);
- explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy,
+ explicit InputDispatcher(InputDispatcherPolicyInterface& policy);
+ explicit InputDispatcher(InputDispatcherPolicyInterface& policy,
std::chrono::nanoseconds staleEventTimeout);
~InputDispatcher() override;
@@ -149,6 +149,8 @@
void cancelCurrentTouch() override;
+ void requestRefreshConfiguration() override;
+
// Public to allow tests to verify that a Monitor can get ANR.
void setMonitorDispatchingTimeoutForTest(std::chrono::nanoseconds timeout);
@@ -165,8 +167,8 @@
std::unique_ptr<InputThread> mThread;
- sp<InputDispatcherPolicyInterface> mPolicy;
- android::InputDispatcherConfiguration mConfig;
+ InputDispatcherPolicyInterface& mPolicy;
+ android::InputDispatcherConfiguration mConfig GUARDED_BY(mLock);
std::mutex mLock;
@@ -705,8 +707,8 @@
void slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags,
const sp<android::gui::WindowInfoHandle>& oldWindowHandle,
const sp<android::gui::WindowInfoHandle>& newWindowHandle,
- TouchState& state, int32_t pointerId, std::vector<InputTarget>& targets)
- REQUIRES(mLock);
+ TouchState& state, int32_t pointerId,
+ std::vector<InputTarget>& targets) const REQUIRES(mLock);
void transferWallpaperTouch(ftl::Flags<InputTarget::Flags> oldTargetFlags,
ftl::Flags<InputTarget::Flags> newTargetFlags,
const sp<android::gui::WindowInfoHandle> fromWindowHandle,
diff --git a/services/inputflinger/dispatcher/InputDispatcherFactory.cpp b/services/inputflinger/dispatcher/InputDispatcherFactory.cpp
index bca1600..3ef8419 100644
--- a/services/inputflinger/dispatcher/InputDispatcherFactory.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcherFactory.cpp
@@ -20,7 +20,7 @@
namespace android {
std::unique_ptr<InputDispatcherInterface> createInputDispatcher(
- const sp<InputDispatcherPolicyInterface>& policy) {
+ InputDispatcherPolicyInterface& policy) {
return std::make_unique<android::inputdispatcher::InputDispatcher>(policy);
}
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 9c443f1..0a61d48 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -242,6 +242,18 @@
clearWindowsWithoutPointers();
}
+void TouchState::removeAllPointersForDevice(int32_t removedDeviceId) {
+ for (TouchedWindow& window : windows) {
+ window.removeAllHoveringPointersForDevice(removedDeviceId);
+ }
+ if (deviceId == removedDeviceId) {
+ for (TouchedWindow& window : windows) {
+ window.removeAllTouchingPointers();
+ }
+ }
+ clearWindowsWithoutPointers();
+}
+
std::string TouchState::dump() const {
std::string out;
out += StringPrintf("deviceId=%d, source=%s\n", deviceId,
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index a20080f..15b840f 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -54,6 +54,8 @@
int32_t deviceId, int32_t hoveringPointerId);
void removeHoveringPointer(int32_t deviceId, int32_t hoveringPointerId);
void clearHoveringPointers();
+
+ void removeAllPointersForDevice(int32_t removedDeviceId);
void removeWindowByToken(const sp<IBinder>& token);
void filterNonAsIsTouchWindows();
diff --git a/services/inputflinger/dispatcher/TouchedWindow.cpp b/services/inputflinger/dispatcher/TouchedWindow.cpp
index 99c4769..d55d657 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.cpp
+++ b/services/inputflinger/dispatcher/TouchedWindow.cpp
@@ -58,6 +58,10 @@
}
}
+void TouchedWindow::removeAllTouchingPointers() {
+ pointerIds.reset();
+}
+
void TouchedWindow::removeHoveringPointer(int32_t deviceId, int32_t pointerId) {
const auto it = mHoveringPointerIdsByDevice.find(deviceId);
if (it == mHoveringPointerIdsByDevice.end()) {
@@ -70,6 +74,10 @@
}
}
+void TouchedWindow::removeAllHoveringPointersForDevice(int32_t deviceId) {
+ mHoveringPointerIdsByDevice.erase(deviceId);
+}
+
std::string TouchedWindow::dump() const {
std::string out;
std::string hoveringPointers =
diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h
index aa2e9dd..43e7169 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.h
+++ b/services/inputflinger/dispatcher/TouchedWindow.h
@@ -44,6 +44,9 @@
void addHoveringPointer(int32_t deviceId, int32_t pointerId);
void removeHoveringPointer(int32_t deviceId, int32_t pointerId);
void removeTouchingPointer(int32_t pointerId);
+
+ void removeAllTouchingPointers();
+ void removeAllHoveringPointersForDevice(int32_t deviceId);
void clearHoveringPointers();
std::string dump() const;
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherFactory.h b/services/inputflinger/dispatcher/include/InputDispatcherFactory.h
index 5247d8e..6b298c2 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherFactory.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherFactory.h
@@ -25,6 +25,6 @@
// This factory method is used to encapsulate implementation details in internal header files.
std::unique_ptr<InputDispatcherInterface> createInputDispatcher(
- const sp<InputDispatcherPolicyInterface>& policy);
+ InputDispatcherPolicyInterface& policy);
} // namespace android
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 76dce63..c752ddd 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -225,6 +225,12 @@
* Abort the current touch stream.
*/
virtual void cancelCurrentTouch() = 0;
+
+ /**
+ * Request that the InputDispatcher's configuration, which can be obtained through the policy,
+ * be updated.
+ */
+ virtual void requestRefreshConfiguration() = 0;
};
} // namespace android
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index 7843923..5539915 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -35,12 +35,11 @@
* The actual implementation is partially supported by callbacks into the DVM
* via JNI. This interface is also mocked in the unit tests.
*/
-class InputDispatcherPolicyInterface : public virtual RefBase {
-protected:
- InputDispatcherPolicyInterface() {}
- virtual ~InputDispatcherPolicyInterface() {}
-
+class InputDispatcherPolicyInterface {
public:
+ InputDispatcherPolicyInterface() = default;
+ virtual ~InputDispatcherPolicyInterface() = default;
+
/* Notifies the system that a configuration change has occurred. */
virtual void notifyConfigurationChanged(nsecs_t when) = 0;
@@ -75,14 +74,14 @@
virtual void notifyVibratorState(int32_t deviceId, bool isOn) = 0;
/* Gets the input dispatcher configuration. */
- virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) = 0;
+ virtual InputDispatcherConfiguration getDispatcherConfiguration() = 0;
/* Filters an input event.
* Return true to dispatch the event unmodified, false to consume the event.
* A filter can also transform and inject events later by passing POLICY_FLAG_FILTERED
* to injectInputEvent.
*/
- virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) = 0;
+ virtual bool filterInputEvent(const InputEvent& inputEvent, uint32_t policyFlags) = 0;
/* Intercepts a key event immediately before queueing it.
* The policy can use this method as an opportunity to perform power management functions
@@ -91,7 +90,7 @@
* This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event
* should be dispatched to applications.
*/
- virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) = 0;
+ virtual void interceptKeyBeforeQueueing(const KeyEvent& keyEvent, uint32_t& policyFlags) = 0;
/* Intercepts a touch, trackball or other motion event before queueing it.
* The policy can use this method as an opportunity to perform power management functions
@@ -100,18 +99,19 @@
* This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event
* should be dispatched to applications.
*/
- virtual void interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when,
+ virtual void interceptMotionBeforeQueueing(int32_t displayId, nsecs_t when,
uint32_t& policyFlags) = 0;
/* Allows the policy a chance to intercept a key before dispatching. */
virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>& token,
- const KeyEvent* keyEvent,
+ const KeyEvent& keyEvent,
uint32_t policyFlags) = 0;
/* Allows the policy a chance to perform default processing for an unhandled key.
- * Returns an alternate keycode to redispatch as a fallback, or 0 to give up. */
- virtual bool dispatchUnhandledKey(const sp<IBinder>& token, const KeyEvent* keyEvent,
- uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) = 0;
+ * Returns an alternate key event to redispatch as a fallback, if needed. */
+ virtual std::optional<KeyEvent> dispatchUnhandledKey(const sp<IBinder>& token,
+ const KeyEvent& keyEvent,
+ uint32_t policyFlags) = 0;
/* Notifies the policy about switch events.
*/
diff --git a/services/inputflinger/tests/FakeEventHub.cpp b/services/inputflinger/tests/FakeEventHub.cpp
index 4626f5a..212fceb 100644
--- a/services/inputflinger/tests/FakeEventHub.cpp
+++ b/services/inputflinger/tests/FakeEventHub.cpp
@@ -617,9 +617,11 @@
}
// If device sysfs changed -> reopen the device
if (!mRawLightInfos.empty() && !foundDevice->classes.test(InputDeviceClass::LIGHT)) {
+ InputDeviceIdentifier identifier = foundDevice->identifier;
+ ftl::Flags<InputDeviceClass> classes = foundDevice->classes;
removeDevice(foundDeviceId);
- addDevice(foundDeviceId, foundDevice->identifier.name,
- foundDevice->classes | InputDeviceClass::LIGHT, foundDevice->identifier.bus);
+ addDevice(foundDeviceId, identifier.name, classes | InputDeviceClass::LIGHT,
+ identifier.bus);
}
}
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 3bfc7bf..147b7d4 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -205,11 +205,9 @@
using AnrResult = std::pair<sp<IBinder>, int32_t /*pid*/>;
-protected:
- virtual ~FakeInputDispatcherPolicy() {}
-
public:
- FakeInputDispatcherPolicy() {}
+ FakeInputDispatcherPolicy() = default;
+ virtual ~FakeInputDispatcherPolicy() = default;
void assertFilterInputEventWasCalled(const NotifyKeyArgs& args) {
assertFilterInputEventWasCalledInternal([&args](const InputEvent& event) {
@@ -404,6 +402,16 @@
mInterceptKeyTimeout = timeout;
}
+ void assertUserActivityPoked() {
+ std::scoped_lock lock(mLock);
+ ASSERT_TRUE(mPokedUserActivity) << "Expected user activity to have been poked";
+ }
+
+ void assertUserActivityNotPoked() {
+ std::scoped_lock lock(mLock);
+ ASSERT_FALSE(mPokedUserActivity) << "Expected user activity not to have been poked";
+ }
+
private:
std::mutex mLock;
std::unique_ptr<InputEvent> mFilteredEvent GUARDED_BY(mLock);
@@ -425,6 +433,7 @@
sp<IBinder> mDropTargetWindowToken GUARDED_BY(mLock);
bool mNotifyDropWindowWasCalled GUARDED_BY(mLock) = false;
+ bool mPokedUserActivity GUARDED_BY(mLock) = false;
std::chrono::milliseconds mInterceptKeyTimeout = 0ms;
@@ -523,22 +532,20 @@
void notifyVibratorState(int32_t deviceId, bool isOn) override {}
- void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override {
- *outConfig = mConfig;
- }
+ InputDispatcherConfiguration getDispatcherConfiguration() override { return mConfig; }
- bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override {
+ bool filterInputEvent(const InputEvent& inputEvent, uint32_t policyFlags) override {
std::scoped_lock lock(mLock);
- switch (inputEvent->getType()) {
+ switch (inputEvent.getType()) {
case InputEventType::KEY: {
- const KeyEvent* keyEvent = static_cast<const KeyEvent*>(inputEvent);
- mFilteredEvent = std::make_unique<KeyEvent>(*keyEvent);
+ const KeyEvent& keyEvent = static_cast<const KeyEvent&>(inputEvent);
+ mFilteredEvent = std::make_unique<KeyEvent>(keyEvent);
break;
}
case InputEventType::MOTION: {
- const MotionEvent* motionEvent = static_cast<const MotionEvent*>(inputEvent);
- mFilteredEvent = std::make_unique<MotionEvent>(*motionEvent);
+ const MotionEvent& motionEvent = static_cast<const MotionEvent&>(inputEvent);
+ mFilteredEvent = std::make_unique<MotionEvent>(motionEvent);
break;
}
default: {
@@ -549,8 +556,8 @@
return true;
}
- void interceptKeyBeforeQueueing(const KeyEvent* inputEvent, uint32_t&) override {
- if (inputEvent->getAction() == AKEY_EVENT_ACTION_UP) {
+ void interceptKeyBeforeQueueing(const KeyEvent& inputEvent, uint32_t&) override {
+ if (inputEvent.getAction() == AKEY_EVENT_ACTION_UP) {
// Clear intercept state when we handled the event.
mInterceptKeyTimeout = 0ms;
}
@@ -558,15 +565,16 @@
void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {}
- nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent*, uint32_t) override {
+ nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent&, uint32_t) override {
nsecs_t delay = std::chrono::nanoseconds(mInterceptKeyTimeout).count();
// Clear intercept state so we could dispatch the event in next wake.
mInterceptKeyTimeout = 0ms;
return delay;
}
- bool dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent*, uint32_t, KeyEvent*) override {
- return false;
+ std::optional<KeyEvent> dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent&,
+ uint32_t) override {
+ return {};
}
void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
@@ -578,7 +586,10 @@
mLastNotifySwitch = NotifySwitchArgs(/*id=*/1, when, policyFlags, switchValues, switchMask);
}
- void pokeUserActivity(nsecs_t, int32_t, int32_t) override {}
+ void pokeUserActivity(nsecs_t, int32_t, int32_t) override {
+ std::scoped_lock lock(mLock);
+ mPokedUserActivity = true;
+ }
void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {
std::scoped_lock lock(mLock);
@@ -610,12 +621,12 @@
class InputDispatcherTest : public testing::Test {
protected:
- sp<FakeInputDispatcherPolicy> mFakePolicy;
+ std::unique_ptr<FakeInputDispatcherPolicy> mFakePolicy;
std::unique_ptr<InputDispatcher> mDispatcher;
void SetUp() override {
- mFakePolicy = sp<FakeInputDispatcherPolicy>::make();
- mDispatcher = std::make_unique<InputDispatcher>(mFakePolicy, STALE_EVENT_TIMEOUT);
+ mFakePolicy = std::make_unique<FakeInputDispatcherPolicy>();
+ mDispatcher = std::make_unique<InputDispatcher>(*mFakePolicy, STALE_EVENT_TIMEOUT);
mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
// Start InputDispatcher thread
ASSERT_EQ(OK, mDispatcher->start());
@@ -623,7 +634,7 @@
void TearDown() override {
ASSERT_EQ(OK, mDispatcher->stop());
- mFakePolicy.clear();
+ mFakePolicy.reset();
mDispatcher.reset();
}
@@ -1190,6 +1201,10 @@
mInfo.setInputConfig(WindowInfo::InputConfig::NO_INPUT_CHANNEL, noInputChannel);
}
+ void setDisableUserActivity(bool disableUserActivity) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::DISABLE_USER_ACTIVITY, disableUserActivity);
+ }
+
void setAlpha(float alpha) { mInfo.alpha = alpha; }
void setTouchOcclusionMode(TouchOcclusionMode mode) { mInfo.touchOcclusionMode = mode; }
@@ -1742,6 +1757,28 @@
return args;
}
+static NotifyKeyArgs generateSystemShortcutArgs(int32_t action,
+ int32_t displayId = ADISPLAY_ID_NONE) {
+ nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ // Define a valid key event.
+ NotifyKeyArgs args(/*id=*/0, currentTime, /*readTime=*/0, DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
+ displayId, 0, action, /* flags */ 0, AKEYCODE_C, KEY_C, AMETA_META_ON,
+ currentTime);
+
+ return args;
+}
+
+static NotifyKeyArgs generateAssistantKeyArgs(int32_t action,
+ int32_t displayId = ADISPLAY_ID_NONE) {
+ nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ // Define a valid key event.
+ NotifyKeyArgs args(/*id=*/0, currentTime, /*readTime=*/0, DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
+ displayId, 0, action, /* flags */ 0, AKEYCODE_ASSIST, KEY_ASSISTANT,
+ AMETA_NONE, currentTime);
+
+ return args;
+}
+
[[nodiscard]] static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source,
int32_t displayId,
const std::vector<PointF>& points) {
@@ -3641,6 +3678,30 @@
AllOf(WithMotionAction(ACTION_CANCEL), WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
+TEST_F(InputDispatcherTest, NotifyDeviceResetCancelsHoveringStream) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
+ "Fake Window", ADISPLAY_ID_DEFAULT);
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(10).y(10))
+ .build());
+
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+
+ // When device reset happens, that hover stream should be terminated with ACTION_HOVER_EXIT
+ mDispatcher->notifyDeviceReset({/*id=*/10, /*eventTime=*/20, DEVICE_ID});
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
+
+ // After the device has been reset, a new hovering stream can be sent to the window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(15).y(15))
+ .build());
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+}
+
TEST_F(InputDispatcherTest, InterceptKeyByPolicy) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
@@ -3957,6 +4018,7 @@
firstWindow->assertNoEvents();
const MotionEvent* event = secondWindow->consumeMotion();
+ ASSERT_NE(nullptr, event);
EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, event->getAction());
// Ensure that the events from the "getRaw" API are in logical display coordinates.
@@ -4530,6 +4592,94 @@
// Window should receive key down event.
window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+
+ // Should have poked user activity
+ mFakePolicy->assertUserActivityPoked();
+}
+
+TEST_F(InputDispatcherTest, FocusedWindow_DisableUserActivity) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
+ "Fake Window", ADISPLAY_ID_DEFAULT);
+
+ window->setDisableUserActivity(true);
+ window->setFocusable(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ setFocusedWindow(window);
+
+ window->consumeFocusEvent(true);
+
+ mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT));
+
+ // Window should receive key down event.
+ window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+
+ // Should have poked user activity
+ mFakePolicy->assertUserActivityNotPoked();
+}
+
+TEST_F(InputDispatcherTest, FocusedWindow_DoesNotReceiveSystemShortcut) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
+ "Fake Window", ADISPLAY_ID_DEFAULT);
+
+ window->setFocusable(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ setFocusedWindow(window);
+
+ window->consumeFocusEvent(true);
+
+ mDispatcher->notifyKey(generateSystemShortcutArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT));
+ mDispatcher->waitForIdle();
+
+ // System key is not passed down
+ window->assertNoEvents();
+
+ // Should have poked user activity
+ mFakePolicy->assertUserActivityPoked();
+}
+
+TEST_F(InputDispatcherTest, FocusedWindow_DoesNotReceiveAssistantKey) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
+ "Fake Window", ADISPLAY_ID_DEFAULT);
+
+ window->setFocusable(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ setFocusedWindow(window);
+
+ window->consumeFocusEvent(true);
+
+ mDispatcher->notifyKey(generateAssistantKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT));
+ mDispatcher->waitForIdle();
+
+ // System key is not passed down
+ window->assertNoEvents();
+
+ // Should have poked user activity
+ mFakePolicy->assertUserActivityPoked();
+}
+
+TEST_F(InputDispatcherTest, FocusedWindow_SystemKeyIgnoresDisableUserActivity) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
+ "Fake Window", ADISPLAY_ID_DEFAULT);
+
+ window->setDisableUserActivity(true);
+ window->setFocusable(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ setFocusedWindow(window);
+
+ window->consumeFocusEvent(true);
+
+ mDispatcher->notifyKey(generateSystemShortcutArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT));
+ mDispatcher->waitForIdle();
+
+ // System key is not passed down
+ window->assertNoEvents();
+
+ // Should have poked user activity
+ mFakePolicy->assertUserActivityPoked();
}
TEST_F(InputDispatcherTest, UnfocusedWindow_DoesNotReceiveFocusEventOrKeyEvent) {
@@ -5281,9 +5431,10 @@
sp<FakeWindowHandle> mWindow;
virtual void SetUp() override {
- mFakePolicy = sp<FakeInputDispatcherPolicy>::make();
+ mFakePolicy = std::make_unique<FakeInputDispatcherPolicy>();
mFakePolicy->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY);
- mDispatcher = std::make_unique<InputDispatcher>(mFakePolicy);
+ mDispatcher = std::make_unique<InputDispatcher>(*mFakePolicy);
+ mDispatcher->requestRefreshConfiguration();
mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
ASSERT_EQ(OK, mDispatcher->start());
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index fe72a69..0aa1bcb 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -288,7 +288,7 @@
void onUidStateChanged(uid_t uid __unused, int32_t procState __unused,
int64_t procStateSeq __unused,
int32_t capability __unused) override {}
- void onUidProcAdjChanged(uid_t uid __unused) override {}
+ void onUidProcAdjChanged(uid_t uid __unused, int32_t adj __unused) override {}
void addOverrideUid(uid_t uid, bool active);
void removeOverrideUid(uid_t uid);
diff --git a/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp b/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp
index cfa2b03..97af445 100644
--- a/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp
@@ -50,7 +50,7 @@
}
if (internalLayer) {
- sequence = getInternalLayerId(sInternalSequence++);
+ sequence = id.value_or(getInternalLayerId(sInternalSequence++));
} else if (id) {
sequence = *id;
sSequence = *id + 1;
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
index 6cacfb5..cd9515c 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
@@ -38,7 +38,8 @@
RequestedLayerState& layer = *newLayer.get();
auto [it, inserted] = mIdToLayer.try_emplace(layer.id, References{.owner = layer});
if (!inserted) {
- LOG_ALWAYS_FATAL("Duplicate layer id %d found. Existing layer: %s", layer.id,
+ LOG_ALWAYS_FATAL("Duplicate layer id found. New layer: %s Existing layer: %s",
+ layer.getDebugString().c_str(),
it->second.owner.getDebugString().c_str());
}
mAddedLayers.push_back(newLayer.get());
@@ -200,8 +201,10 @@
if (layer->what & layer_state_t::eBackgroundColorChanged) {
if (layer->bgColorLayerId == UNASSIGNED_LAYER_ID && layer->bgColor.a != 0) {
- LayerCreationArgs backgroundLayerArgs(layer->id,
- /*internalLayer=*/true);
+ LayerCreationArgs
+ backgroundLayerArgs(LayerCreationArgs::getInternalLayerId(
+ LayerCreationArgs::sInternalSequence++),
+ /*internalLayer=*/true);
backgroundLayerArgs.parentId = layer->id;
backgroundLayerArgs.name = layer->name + "BackgroundColorLayer";
backgroundLayerArgs.flags = ISurfaceComposerClient::eFXSurfaceEffect;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 9e40d7f..bfb7a22 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -254,7 +254,8 @@
mFlinger->mTunnelModeEnabledReporter->decrementTunnelModeCount();
}
if (mHadClonedChild) {
- mFlinger->mNumClones--;
+ auto& roots = mFlinger->mLayerMirrorRoots;
+ roots.erase(std::remove(roots.begin(), roots.end(), this), roots.end());
}
if (hasTrustedPresentationListener()) {
mFlinger->mNumTrustedPresentationListeners--;
@@ -2552,7 +2553,10 @@
return outputLayer ? outputLayer->getState().visibleRegion : Region();
}
-void Layer::setInitialValuesForClone(const sp<Layer>& clonedFrom) {
+void Layer::setInitialValuesForClone(const sp<Layer>& clonedFrom, uint32_t mirrorRootId) {
+ mSnapshot->path.id = clonedFrom->getSequence();
+ mSnapshot->path.mirrorRootId = mirrorRootId;
+
cloneDrawingState(clonedFrom.get());
mClonedFrom = clonedFrom;
mPremultipliedAlpha = clonedFrom->mPremultipliedAlpha;
@@ -2591,7 +2595,7 @@
mDrawingState.inputInfo = tmpInputInfo;
}
-void Layer::updateMirrorInfo() {
+bool Layer::updateMirrorInfo(const std::deque<Layer*>& cloneRootsPendingUpdates) {
if (mClonedChild == nullptr || !mClonedChild->isClonedFromAlive()) {
// If mClonedChild is null, there is nothing to mirror. If isClonedFromAlive returns false,
// it means that there is a clone, but the layer it was cloned from has been destroyed. In
@@ -2599,7 +2603,7 @@
// destroyed. The root, this layer, will still be around since the client can continue
// to hold a reference, but no cloned layers will be displayed.
mClonedChild = nullptr;
- return;
+ return true;
}
std::map<sp<Layer>, sp<Layer>> clonedLayersMap;
@@ -2614,6 +2618,13 @@
mClonedChild->updateClonedDrawingState(clonedLayersMap);
mClonedChild->updateClonedChildren(sp<Layer>::fromExisting(this), clonedLayersMap);
mClonedChild->updateClonedRelatives(clonedLayersMap);
+
+ for (Layer* root : cloneRootsPendingUpdates) {
+ if (clonedLayersMap.find(sp<Layer>::fromExisting(root)) != clonedLayersMap.end()) {
+ return false;
+ }
+ }
+ return true;
}
void Layer::updateClonedDrawingState(std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) {
@@ -2653,7 +2664,7 @@
}
sp<Layer> clonedChild = clonedLayersMap[child];
if (clonedChild == nullptr) {
- clonedChild = child->createClone();
+ clonedChild = child->createClone(mirrorRoot->getSequence());
clonedLayersMap[child] = clonedChild;
}
addChildToDrawing(clonedChild);
@@ -2761,7 +2772,7 @@
void Layer::setClonedChild(const sp<Layer>& clonedChild) {
mClonedChild = clonedChild;
mHadClonedChild = true;
- mFlinger->mNumClones++;
+ mFlinger->mLayerMirrorRoots.push_back(this);
}
bool Layer::setDropInputMode(gui::DropInputMode mode) {
@@ -3491,11 +3502,11 @@
}
}
-sp<Layer> Layer::createClone() {
+sp<Layer> Layer::createClone(uint32_t mirrorRootId) {
LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata());
args.textureName = mTextureName;
sp<Layer> layer = mFlinger->getFactory().createBufferStateLayer(args);
- layer->setInitialValuesForClone(sp<Layer>::fromExisting(this));
+ layer->setInitialValuesForClone(sp<Layer>::fromExisting(this), mirrorRootId);
return layer;
}
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index b37fa15..4374914 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -248,7 +248,7 @@
// true if this layer is visible, false otherwise
virtual bool isVisible() const;
- virtual sp<Layer> createClone();
+ virtual sp<Layer> createClone(uint32_t mirrorRoot);
// Set a 2x2 transformation matrix on the layer. This transform
// will be applied after parent transforms, but before any final
@@ -651,7 +651,7 @@
gui::WindowInfo::Type getWindowType() const { return mWindowType; }
- void updateMirrorInfo();
+ bool updateMirrorInfo(const std::deque<Layer*>& cloneRootsPendingUpdates);
/*
* doTransaction - process the transaction. This is a good place to figure
@@ -922,7 +922,7 @@
friend class TransactionFrameTracerTest;
friend class TransactionSurfaceFrameTest;
- virtual void setInitialValuesForClone(const sp<Layer>& clonedFrom);
+ virtual void setInitialValuesForClone(const sp<Layer>& clonedFrom, uint32_t mirrorRootId);
void preparePerFrameCompositionState();
void preparePerFrameBufferCompositionState();
void preparePerFrameEffectsCompositionState();
diff --git a/services/surfaceflinger/LayerRenderArea.cpp b/services/surfaceflinger/LayerRenderArea.cpp
index f6b5afc..d606cff 100644
--- a/services/surfaceflinger/LayerRenderArea.cpp
+++ b/services/surfaceflinger/LayerRenderArea.cpp
@@ -85,7 +85,7 @@
// If layer is offscreen, update mirroring info if it exists
if (mLayer->isRemovedFromCurrentState()) {
mLayer->traverse(LayerVector::StateSet::Drawing,
- [&](Layer* layer) { layer->updateMirrorInfo(); });
+ [&](Layer* layer) { layer->updateMirrorInfo({}); });
mLayer->traverse(LayerVector::StateSet::Drawing,
[&](Layer* layer) { layer->updateCloneBufferInfo(); });
}
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 9a4261d..f1fd6db 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -355,6 +355,8 @@
if (isSetByHwc()) {
transaction.setFlags(surface, layer_state_t::eLayerIsRefreshRateIndicator,
layer_state_t::eLayerIsRefreshRateIndicator);
+ // Disable overlay layer caching when refresh rate is updated by the HWC.
+ transaction.setCachingHint(surface, gui::CachingHint::Disabled);
}
transaction.setFrameRate(surface, kFrameRate, kCompatibility, kSeamlessness);
return transaction;
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 74665a7..af9acf3 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -42,6 +42,7 @@
#include <utils/Errors.h>
#include <utils/Trace.h>
+#include <scheduler/VsyncConfig.h>
#include "DisplayHardware/DisplayMode.h"
#include "FrameTimeline.h"
#include "VSyncDispatch.h"
@@ -597,25 +598,34 @@
nsecs_t timestamp,
nsecs_t preferredExpectedPresentationTime,
nsecs_t preferredDeadlineTimestamp) const {
+ uint32_t currentIndex = 0;
// Add 1 to ensure the preferredFrameTimelineIndex entry (when multiplier == 0) is included.
- for (int64_t multiplier = -VsyncEventData::kFrameTimelinesLength + 1, currentIndex = 0;
- currentIndex < VsyncEventData::kFrameTimelinesLength; multiplier++) {
+ for (int64_t multiplier = -VsyncEventData::kFrameTimelinesCapacity + 1;
+ currentIndex < VsyncEventData::kFrameTimelinesCapacity; multiplier++) {
nsecs_t deadlineTimestamp = preferredDeadlineTimestamp + multiplier * frameInterval;
- // Valid possible frame timelines must have future values.
- if (deadlineTimestamp > timestamp) {
- if (multiplier == 0) {
- outVsyncEventData.preferredFrameTimelineIndex = currentIndex;
- }
- nsecs_t expectedPresentationTime =
- preferredExpectedPresentationTime + multiplier * frameInterval;
- outVsyncEventData.frameTimelines[currentIndex] =
- {.vsyncId =
- generateToken(timestamp, deadlineTimestamp, expectedPresentationTime),
- .deadlineTimestamp = deadlineTimestamp,
- .expectedPresentationTime = expectedPresentationTime};
- currentIndex++;
+ // Valid possible frame timelines must have future values, so find a later frame timeline.
+ if (deadlineTimestamp <= timestamp) {
+ continue;
}
+
+ nsecs_t expectedPresentationTime =
+ preferredExpectedPresentationTime + multiplier * frameInterval;
+ if (expectedPresentationTime >= preferredExpectedPresentationTime +
+ scheduler::VsyncConfig::kEarlyLatchMaxThreshold.count()) {
+ break;
+ }
+
+ if (multiplier == 0) {
+ outVsyncEventData.preferredFrameTimelineIndex = currentIndex;
+ }
+
+ outVsyncEventData.frameTimelines[currentIndex] =
+ {.vsyncId = generateToken(timestamp, deadlineTimestamp, expectedPresentationTime),
+ .deadlineTimestamp = deadlineTimestamp,
+ .expectedPresentationTime = expectedPresentationTime};
+ currentIndex++;
}
+ outVsyncEventData.frameTimelinesLength = currentIndex;
}
void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event,
@@ -692,17 +702,27 @@
}
void EventThread::onNewVsyncSchedule(std::shared_ptr<scheduler::VsyncSchedule> schedule) {
+ // Hold onto the old registration until after releasing the mutex to avoid deadlock.
+ scheduler::VSyncCallbackRegistration oldRegistration =
+ onNewVsyncScheduleInternal(std::move(schedule));
+}
+
+scheduler::VSyncCallbackRegistration EventThread::onNewVsyncScheduleInternal(
+ std::shared_ptr<scheduler::VsyncSchedule> schedule) {
std::lock_guard<std::mutex> lock(mMutex);
const bool reschedule = mVsyncRegistration.cancel() == scheduler::CancelResult::Cancelled;
mVsyncSchedule = std::move(schedule);
- mVsyncRegistration =
- scheduler::VSyncCallbackRegistration(mVsyncSchedule->getDispatch(),
- createDispatchCallback(), mThreadName);
+ auto oldRegistration =
+ std::exchange(mVsyncRegistration,
+ scheduler::VSyncCallbackRegistration(mVsyncSchedule->getDispatch(),
+ createDispatchCallback(),
+ mThreadName));
if (reschedule) {
mVsyncRegistration.schedule({.workDuration = mWorkDuration.get().count(),
.readyDuration = mReadyDuration.count(),
.earliestVsync = mLastVsyncCallbackTime.ns()});
}
+ return oldRegistration;
}
scheduler::VSyncDispatch::Callback EventThread::createDispatchCallback() {
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 30869e9..684745b 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -174,7 +174,7 @@
size_t getEventThreadConnectionCount() override;
- void onNewVsyncSchedule(std::shared_ptr<scheduler::VsyncSchedule>) override;
+ void onNewVsyncSchedule(std::shared_ptr<scheduler::VsyncSchedule>) override EXCLUDES(mMutex);
private:
friend EventThreadTest;
@@ -201,6 +201,11 @@
scheduler::VSyncDispatch::Callback createDispatchCallback();
+ // Returns the old registration so it can be destructed outside the lock to
+ // avoid deadlock.
+ scheduler::VSyncCallbackRegistration onNewVsyncScheduleInternal(
+ std::shared_ptr<scheduler::VsyncSchedule>) EXCLUDES(mMutex);
+
const char* const mThreadName;
TracedOrdinal<int> mVsyncTracer;
TracedOrdinal<std::chrono::nanoseconds> mWorkDuration GUARDED_BY(mMutex);
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 63a0173..9319543 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -406,11 +406,13 @@
void Scheduler::enableHardwareVsync(PhysicalDisplayId id) {
auto schedule = getVsyncSchedule(id);
+ LOG_ALWAYS_FATAL_IF(!schedule);
schedule->enableHardwareVsync(mSchedulerCallback);
}
void Scheduler::disableHardwareVsync(PhysicalDisplayId id, bool disallow) {
auto schedule = getVsyncSchedule(id);
+ LOG_ALWAYS_FATAL_IF(!schedule);
schedule->disableHardwareVsync(mSchedulerCallback, disallow);
}
@@ -427,7 +429,10 @@
void Scheduler::resyncToHardwareVsyncLocked(PhysicalDisplayId id, bool allowToEnable,
std::optional<Fps> refreshRate) {
const auto displayOpt = mDisplays.get(id);
- LOG_ALWAYS_FATAL_IF(!displayOpt);
+ if (!displayOpt) {
+ ALOGW("%s: Invalid display %s!", __func__, to_string(id).c_str());
+ return;
+ }
const Display& display = *displayOpt;
if (display.schedulePtr->isHardwareVsyncAllowed(allowToEnable)) {
@@ -446,7 +451,10 @@
ftl::FakeGuard guard(kMainThreadContext);
const auto displayOpt = mDisplays.get(id);
- LOG_ALWAYS_FATAL_IF(!displayOpt);
+ if (!displayOpt) {
+ ALOGW("%s: Invalid display %s!", __func__, to_string(id).c_str());
+ return;
+ }
const Display& display = *displayOpt;
const auto mode = display.selectorPtr->getActiveMode();
@@ -478,12 +486,18 @@
const auto hwcVsyncPeriod = ftl::Optional(hwcVsyncPeriodIn).transform([](nsecs_t nanos) {
return Period::fromNs(nanos);
});
- return getVsyncSchedule(id)->addResyncSample(mSchedulerCallback, TimePoint::fromNs(timestamp),
- hwcVsyncPeriod);
+ auto schedule = getVsyncSchedule(id);
+ if (!schedule) {
+ ALOGW("%s: Invalid display %s!", __func__, to_string(id).c_str());
+ return false;
+ }
+ return schedule->addResyncSample(mSchedulerCallback, TimePoint::fromNs(timestamp),
+ hwcVsyncPeriod);
}
void Scheduler::addPresentFence(PhysicalDisplayId id, std::shared_ptr<FenceTime> fence) {
auto schedule = getVsyncSchedule(id);
+ LOG_ALWAYS_FATAL_IF(!schedule);
const bool needMoreSignals = schedule->getController().addPresentFence(std::move(fence));
if (needMoreSignals) {
schedule->enableHardwareVsync(mSchedulerCallback);
@@ -553,6 +567,7 @@
{
std::scoped_lock lock(mDisplayLock);
auto vsyncSchedule = getVsyncScheduleLocked(id);
+ LOG_ALWAYS_FATAL_IF(!vsyncSchedule);
vsyncSchedule->getController().setDisplayPowerMode(powerMode);
}
if (!isPacesetter) return;
@@ -582,7 +597,9 @@
}
const auto displayOpt = mDisplays.get(*idOpt);
- LOG_ALWAYS_FATAL_IF(!displayOpt);
+ if (!displayOpt) {
+ return nullptr;
+ }
return displayOpt->get().schedulePtr;
}
@@ -830,81 +847,35 @@
using RankedRefreshRates = RefreshRateSelector::RankedFrameRates;
display::PhysicalDisplayVector<RankedRefreshRates> perDisplayRanking;
-
- // Tallies the score of a refresh rate across `displayCount` displays.
- struct RefreshRateTally {
- explicit RefreshRateTally(float score) : score(score) {}
-
- float score;
- size_t displayCount = 1;
- };
-
- // Chosen to exceed a typical number of refresh rates across displays.
- constexpr size_t kStaticCapacity = 8;
- ftl::SmallMap<Fps, RefreshRateTally, kStaticCapacity, FpsApproxEqual> refreshRateTallies;
-
const auto globalSignals = makeGlobalSignals();
+ Fps pacesetterFps;
for (const auto& [id, display] : mDisplays) {
auto rankedFrameRates =
display.selectorPtr->getRankedFrameRates(mPolicy.contentRequirements,
globalSignals);
-
- for (const auto& [frameRateMode, score] : rankedFrameRates.ranking) {
- const auto [it, inserted] = refreshRateTallies.try_emplace(frameRateMode.fps, score);
-
- if (!inserted) {
- auto& tally = it->second;
- tally.score += score;
- tally.displayCount++;
- }
+ if (id == *mPacesetterDisplayId) {
+ pacesetterFps = rankedFrameRates.ranking.front().frameRateMode.fps;
}
-
perDisplayRanking.push_back(std::move(rankedFrameRates));
}
- auto maxScoreIt = refreshRateTallies.cbegin();
-
- // Find the first refresh rate common to all displays.
- while (maxScoreIt != refreshRateTallies.cend() &&
- maxScoreIt->second.displayCount != mDisplays.size()) {
- ++maxScoreIt;
- }
-
- if (maxScoreIt != refreshRateTallies.cend()) {
- // Choose the highest refresh rate common to all displays, if any.
- for (auto it = maxScoreIt + 1; it != refreshRateTallies.cend(); ++it) {
- const auto [fps, tally] = *it;
-
- if (tally.displayCount == mDisplays.size() && tally.score > maxScoreIt->second.score) {
- maxScoreIt = it;
- }
- }
- }
-
- const std::optional<Fps> chosenFps = maxScoreIt != refreshRateTallies.cend()
- ? std::make_optional(maxScoreIt->first)
- : std::nullopt;
-
DisplayModeChoiceMap modeChoices;
-
using fps_approx_ops::operator==;
- for (auto& [ranking, signals] : perDisplayRanking) {
- if (!chosenFps) {
- const auto& [frameRateMode, _] = ranking.front();
- modeChoices.try_emplace(frameRateMode.modePtr->getPhysicalDisplayId(),
- DisplayModeChoice{frameRateMode, signals});
- continue;
- }
+ for (auto& [rankings, signals] : perDisplayRanking) {
+ const auto chosenFrameRateMode =
+ ftl::find_if(rankings,
+ [&](const auto& ranking) {
+ return ranking.frameRateMode.fps == pacesetterFps;
+ })
+ .transform([](const auto& scoredFrameRate) {
+ return scoredFrameRate.get().frameRateMode;
+ })
+ .value_or(rankings.front().frameRateMode);
- for (auto& [frameRateMode, _] : ranking) {
- if (frameRateMode.fps == *chosenFps) {
- modeChoices.try_emplace(frameRateMode.modePtr->getPhysicalDisplayId(),
- DisplayModeChoice{frameRateMode, signals});
- break;
- }
- }
+ modeChoices.try_emplace(chosenFrameRateMode.modePtr->getPhysicalDisplayId(),
+ DisplayModeChoice{chosenFrameRateMode, signals});
}
return modeChoices;
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 43aab2d..f13c878 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -196,8 +196,8 @@
// Sets the render rate for the scheduler to run at.
void setRenderRate(PhysicalDisplayId, Fps);
- void enableHardwareVsync(PhysicalDisplayId);
- void disableHardwareVsync(PhysicalDisplayId, bool disallow);
+ void enableHardwareVsync(PhysicalDisplayId) REQUIRES(kMainThreadContext);
+ void disableHardwareVsync(PhysicalDisplayId, bool disallow) REQUIRES(kMainThreadContext);
// Resyncs the scheduler to hardware vsync.
// If allowToEnable is true, then hardware vsync will be turned on.
@@ -219,7 +219,8 @@
// otherwise.
bool addResyncSample(PhysicalDisplayId, nsecs_t timestamp,
std::optional<nsecs_t> hwcVsyncPeriod);
- void addPresentFence(PhysicalDisplayId, std::shared_ptr<FenceTime>) EXCLUDES(mDisplayLock);
+ void addPresentFence(PhysicalDisplayId, std::shared_ptr<FenceTime>) EXCLUDES(mDisplayLock)
+ REQUIRES(kMainThreadContext);
// Layers are registered on creation, and unregistered when the weak reference expires.
void registerLayer(Layer*);
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index 77875e3..c3a952f 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -155,10 +155,6 @@
VSyncDispatch& operator=(const VSyncDispatch&) = delete;
};
-/*
- * Helper class to operate on registered callbacks. It is up to user of the class to ensure
- * that VsyncDispatch lifetime exceeds the lifetime of VSyncCallbackRegistation.
- */
class VSyncCallbackRegistration {
public:
VSyncCallbackRegistration(std::shared_ptr<VSyncDispatch>, VSyncDispatch::Callback,
@@ -178,9 +174,10 @@
CancelResult cancel();
private:
+ friend class VSyncCallbackRegistrationTest;
+
std::shared_ptr<VSyncDispatch> mDispatch;
- VSyncDispatch::CallbackToken mToken;
- bool mValidToken;
+ std::optional<VSyncDispatch::CallbackToken> mToken;
};
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index 26389eb..1f922f1 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -21,12 +21,16 @@
#include <android-base/stringprintf.h>
#include <ftl/concat.h>
#include <utils/Trace.h>
+#include <log/log_main.h>
#include <scheduler/TimeKeeper.h>
#include "VSyncDispatchTimerQueue.h"
#include "VSyncTracker.h"
+#undef LOG_TAG
+#define LOG_TAG "VSyncDispatch"
+
namespace android::scheduler {
using base::StringAppendF;
@@ -225,6 +229,10 @@
VSyncDispatchTimerQueue::~VSyncDispatchTimerQueue() {
std::lock_guard lock(mMutex);
cancelTimer();
+ for (auto& [_, entry] : mCallbacks) {
+ ALOGE("Forgot to unregister a callback on VSyncDispatch!");
+ entry->ensureNotRunning();
+ }
}
void VSyncDispatchTimerQueue::cancelTimer() {
@@ -438,47 +446,44 @@
VSyncDispatch::Callback callback,
std::string callbackName)
: mDispatch(std::move(dispatch)),
- mToken(mDispatch->registerCallback(std::move(callback), std::move(callbackName))),
- mValidToken(true) {}
+ mToken(mDispatch->registerCallback(std::move(callback), std::move(callbackName))) {}
VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncCallbackRegistration&& other)
- : mDispatch(std::move(other.mDispatch)),
- mToken(std::move(other.mToken)),
- mValidToken(std::move(other.mValidToken)) {
- other.mValidToken = false;
-}
+ : mDispatch(std::move(other.mDispatch)), mToken(std::exchange(other.mToken, std::nullopt)) {}
VSyncCallbackRegistration& VSyncCallbackRegistration::operator=(VSyncCallbackRegistration&& other) {
+ if (this == &other) return *this;
+ if (mToken) {
+ mDispatch->unregisterCallback(*mToken);
+ }
mDispatch = std::move(other.mDispatch);
- mToken = std::move(other.mToken);
- mValidToken = std::move(other.mValidToken);
- other.mValidToken = false;
+ mToken = std::exchange(other.mToken, std::nullopt);
return *this;
}
VSyncCallbackRegistration::~VSyncCallbackRegistration() {
- if (mValidToken) mDispatch->unregisterCallback(mToken);
+ if (mToken) mDispatch->unregisterCallback(*mToken);
}
ScheduleResult VSyncCallbackRegistration::schedule(VSyncDispatch::ScheduleTiming scheduleTiming) {
- if (!mValidToken) {
+ if (!mToken) {
return std::nullopt;
}
- return mDispatch->schedule(mToken, scheduleTiming);
+ return mDispatch->schedule(*mToken, scheduleTiming);
}
ScheduleResult VSyncCallbackRegistration::update(VSyncDispatch::ScheduleTiming scheduleTiming) {
- if (!mValidToken) {
+ if (!mToken) {
return std::nullopt;
}
- return mDispatch->update(mToken, scheduleTiming);
+ return mDispatch->update(*mToken, scheduleTiming);
}
CancelResult VSyncCallbackRegistration::cancel() {
- if (!mValidToken) {
+ if (!mToken) {
return CancelResult::Error;
}
- return mDispatch->cancel(mToken);
+ return mDispatch->cancel(*mToken);
}
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/VsyncConfig.h b/services/surfaceflinger/Scheduler/include/scheduler/VsyncConfig.h
index 3b1985f..47d95a8 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/VsyncConfig.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/VsyncConfig.h
@@ -22,6 +22,8 @@
namespace android::scheduler {
+using namespace std::chrono_literals;
+
// Phase offsets and work durations for SF and app deadlines from VSYNC.
struct VsyncConfig {
nsecs_t sfOffset;
@@ -35,6 +37,10 @@
}
bool operator!=(const VsyncConfig& other) const { return !(*this == other); }
+
+ // The duration for which SF can delay a frame if it is considered early based on the
+ // VsyncConfig::appWorkDuration.
+ static constexpr std::chrono::nanoseconds kEarlyLatchMaxThreshold = 100ms;
};
struct VsyncConfigSet {
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index d406aff..b5ba94e 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1146,17 +1146,26 @@
std::optional<PhysicalDisplayId> displayIdOpt;
{
Mutex::Autolock lock(mStateLock);
- displayIdOpt = getPhysicalDisplayIdLocked(displayToken);
+ if (displayToken) {
+ displayIdOpt = getPhysicalDisplayIdLocked(displayToken);
+ if (!displayIdOpt) {
+ ALOGW("%s: Invalid physical display token %p", __func__, displayToken.get());
+ return NAME_NOT_FOUND;
+ }
+ } else {
+ // TODO (b/277364366): Clients should be updated to pass in the display they
+ // want, rather than us picking an arbitrary one (the active display, in this
+ // case).
+ displayIdOpt = mActiveDisplayId;
+ }
}
- // TODO (b/277364366): Clients should be updated to pass in the display they
- // want, rather than us picking an arbitrary one (the pacesetter, in this
- // case).
- if (displayToken && !displayIdOpt) {
- ALOGE("%s: Invalid physical display token %p", __func__, displayToken.get());
+ const auto schedule = mScheduler->getVsyncSchedule(displayIdOpt);
+ if (!schedule) {
+ ALOGE("%s: Missing VSYNC schedule for display %s!", __func__,
+ to_string(*displayIdOpt).c_str());
return NAME_NOT_FOUND;
}
- const auto schedule = mScheduler->getVsyncSchedule(displayIdOpt);
outStats->vsyncTime = schedule->vsyncDeadlineAfter(TimePoint::now()).ns();
outStats->vsyncPeriod = schedule->period().ns();
return NO_ERROR;
@@ -2136,7 +2145,9 @@
static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) {
{
ftl::FakeGuard guard(kMainThreadContext);
- mScheduler->getVsyncSchedule(id)->setPendingHardwareVsyncState(enabled);
+ if (auto schedule = mScheduler->getVsyncSchedule(id)) {
+ schedule->setPendingHardwareVsyncState(enabled);
+ }
}
ATRACE_FORMAT("%s (%d) for %" PRIu64 " (main thread)", whence, enabled, id.value);
@@ -2307,7 +2318,7 @@
sp<Layer> bgColorLayer = getFactory().createEffectLayer(
LayerCreationArgs(this, nullptr, layer->name,
ISurfaceComposerClient::eFXSurfaceEffect, LayerMetadata(),
- std::make_optional(layer->parentId), true));
+ std::make_optional(layer->id), true));
mLegacyLayers[bgColorLayer->sequence] = bgColorLayer;
}
const bool willReleaseBufferOnLatch = layer->willReleaseBufferOnLatch();
@@ -2874,7 +2885,10 @@
}
for (auto layer : mLayersWithBuffersRemoved) {
- for (auto layerStack : layer->mPreviouslyPresentedLayerStacks) {
+ std::vector<ui::LayerStack> previouslyPresentedLayerStacks =
+ std::move(layer->mPreviouslyPresentedLayerStacks);
+ layer->mPreviouslyPresentedLayerStacks.clear();
+ for (auto layerStack : previouslyPresentedLayerStacks) {
auto optDisplay = layerStackToDisplay.get(layerStack);
if (optDisplay && !optDisplay->get()->isVirtual()) {
auto fence = getHwComposer().getPresentFence(optDisplay->get()->getPhysicalId());
@@ -4006,8 +4020,21 @@
}
commitOffscreenLayers();
- if (mNumClones > 0) {
- mDrawingState.traverse([&](Layer* layer) { layer->updateMirrorInfo(); });
+ if (mLayerMirrorRoots.size() > 0) {
+ std::deque<Layer*> pendingUpdates;
+ pendingUpdates.insert(pendingUpdates.end(), mLayerMirrorRoots.begin(),
+ mLayerMirrorRoots.end());
+ std::vector<Layer*> needsUpdating;
+ for (Layer* cloneRoot : mLayerMirrorRoots) {
+ pendingUpdates.pop_front();
+ if (cloneRoot->updateMirrorInfo(pendingUpdates)) {
+ } else {
+ needsUpdating.push_back(cloneRoot);
+ }
+ }
+ for (Layer* cloneRoot : needsUpdating) {
+ cloneRoot->updateMirrorInfo({});
+ }
}
}
@@ -4114,7 +4141,7 @@
mBootStage = BootStage::BOOTANIMATION;
}
- if (mNumClones > 0) {
+ if (mLayerMirrorRoots.size() > 0) {
mDrawingState.traverse([&](Layer* layer) { layer->updateCloneBufferInfo(); });
}
@@ -4394,10 +4421,8 @@
const auto predictedPresentTime = TimePoint::fromNs(prediction->presentTime);
- // The duration for which SF can delay a frame if it is considered early based on the
- // VsyncConfig::appWorkDuration.
- if (constexpr std::chrono::nanoseconds kEarlyLatchMaxThreshold = 100ms;
- std::chrono::abs(predictedPresentTime - expectedPresentTime) >= kEarlyLatchMaxThreshold) {
+ if (std::chrono::abs(predictedPresentTime - expectedPresentTime) >=
+ scheduler::VsyncConfig::kEarlyLatchMaxThreshold) {
return false;
}
@@ -5243,7 +5268,7 @@
return result;
}
- mirrorLayer->setClonedChild(mirrorFrom->createClone());
+ mirrorLayer->setClonedChild(mirrorFrom->createClone(mirrorLayer->getSequence()));
}
outResult.layerId = mirrorLayer->sequence;
@@ -8022,7 +8047,7 @@
Mutex::Autolock lock(mStateLock);
createEffectLayer(mirrorArgs, &unused, &childMirror);
MUTEX_ALIAS(mStateLock, childMirror->mFlinger->mStateLock);
- childMirror->setClonedChild(layer->createClone());
+ childMirror->setClonedChild(layer->createClone(childMirror->getSequence()));
childMirror->reparent(mirrorDisplay.rootHandle);
}
// lock on mStateLock needs to be released before binder handle gets destroyed
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index cd7659b..92e19c0 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -296,8 +296,7 @@
// the client can no longer modify this layer directly.
void onHandleDestroyed(BBinder* handle, sp<Layer>& layer, uint32_t layerId);
- // TODO: Remove atomic if move dtor to main thread CL lands
- std::atomic<uint32_t> mNumClones;
+ std::vector<Layer*> mLayerMirrorRoots;
TransactionCallbackInvoker& getTransactionCallbackInvoker() {
return mTransactionCallbackInvoker;
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
index a32750e..8061a8f 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
@@ -75,7 +75,7 @@
bool isVisible() const override { return true; }
- sp<Layer> createClone() override { return nullptr; }
+ sp<Layer> createClone(uint32_t /* mirrorRootId */) override { return nullptr; }
};
class FuzzImplVSyncTracker : public scheduler::VSyncTracker {
diff --git a/services/surfaceflinger/tests/DisplayEventReceiver_test.cpp b/services/surfaceflinger/tests/DisplayEventReceiver_test.cpp
index 0df7e2f..4c26017 100644
--- a/services/surfaceflinger/tests/DisplayEventReceiver_test.cpp
+++ b/services/surfaceflinger/tests/DisplayEventReceiver_test.cpp
@@ -33,9 +33,14 @@
const VsyncEventData& vsyncEventData = parcelableVsyncEventData.vsync;
EXPECT_NE(std::numeric_limits<size_t>::max(), vsyncEventData.preferredFrameTimelineIndex);
+ EXPECT_GT(static_cast<int64_t>(vsyncEventData.frameTimelinesLength), 0)
+ << "Frame timelines length should be greater than 0";
+ EXPECT_LE(static_cast<int64_t>(vsyncEventData.frameTimelinesLength),
+ VsyncEventData::kFrameTimelinesCapacity)
+ << "Frame timelines length should not exceed max capacity";
EXPECT_GT(vsyncEventData.frameTimelines[0].deadlineTimestamp, now)
<< "Deadline timestamp should be greater than frame time";
- for (size_t i = 0; i < VsyncEventData::kFrameTimelinesLength; i++) {
+ for (size_t i = 0; i < vsyncEventData.frameTimelinesLength; i++) {
EXPECT_NE(gui::FrameTimelineInfo::INVALID_VSYNC_ID,
vsyncEventData.frameTimelines[i].vsyncId);
EXPECT_GT(vsyncEventData.frameTimelines[i].expectedPresentationTime,
diff --git a/services/surfaceflinger/tests/tracing/Android.bp b/services/surfaceflinger/tests/tracing/Android.bp
index aa6c74e..21ebaea 100644
--- a/services/surfaceflinger/tests/tracing/Android.bp
+++ b/services/surfaceflinger/tests/tracing/Android.bp
@@ -30,7 +30,7 @@
],
test_suites: ["device-tests"],
sanitize: {
- address: false,
+ address: true,
},
srcs: [
":libsurfaceflinger_sources",
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 201d37f..55705ca 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -104,6 +104,7 @@
"SurfaceFlinger_DisplayTransactionCommitTest.cpp",
"SurfaceFlinger_ExcludeDolbyVisionTest.cpp",
"SurfaceFlinger_GetDisplayNativePrimariesTest.cpp",
+ "SurfaceFlinger_GetDisplayStatsTest.cpp",
"SurfaceFlinger_HdrOutputControlTest.cpp",
"SurfaceFlinger_HotplugTest.cpp",
"SurfaceFlinger_InitializeDisplaysTest.cpp",
@@ -128,6 +129,7 @@
"TransactionTracingTest.cpp",
"TunnelModeEnabledReporterTest.cpp",
"StrongTypingTest.cpp",
+ "VSyncCallbackRegistrationTest.cpp",
"VSyncDispatchTimerQueueTest.cpp",
"VSyncDispatchRealtimeTest.cpp",
"VsyncModulatorTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index f1cdca3..5fed9b4 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -24,6 +24,7 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <log/log.h>
+#include <scheduler/VsyncConfig.h>
#include <utils/Errors.h>
#include "AsyncCallRecorder.h"
@@ -77,7 +78,7 @@
EventThreadTest();
~EventThreadTest() override;
- void createThread();
+ void setupEventThread(std::chrono::nanoseconds vsyncPeriod);
sp<MockEventThreadConnection> createConnection(ConnectionEventRecorder& recorder,
EventRegistrationFlags eventRegistration = {},
uid_t ownerUid = mConnectionUid);
@@ -90,8 +91,9 @@
nsecs_t expectedTimestamp, unsigned expectedCount);
void expectVsyncEventReceivedByConnection(nsecs_t expectedTimestamp, unsigned expectedCount);
void expectVsyncEventFrameTimelinesCorrect(
- nsecs_t expectedTimestamp,
- /*VSyncSource::VSyncData*/ gui::VsyncEventData::FrameTimeline preferredVsyncData);
+ nsecs_t expectedTimestamp, gui::VsyncEventData::FrameTimeline preferredVsyncData);
+ void expectVsyncEventDataFrameTimelinesValidLength(VsyncEventData vsyncEventData,
+ std::chrono::nanoseconds vsyncPeriod);
void expectHotplugEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
bool expectedConnected);
void expectConfigChangedEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
@@ -154,19 +156,6 @@
.WillRepeatedly(Invoke(mVSyncCallbackUpdateRecorder.getInvocable()));
EXPECT_CALL(mockDispatch, unregisterCallback(_))
.WillRepeatedly(Invoke(mVSyncCallbackUnregisterRecorder.getInvocable()));
-
- createThread();
- mConnection =
- createConnection(mConnectionEventCallRecorder,
- gui::ISurfaceComposer::EventRegistration::modeChanged |
- gui::ISurfaceComposer::EventRegistration::frameRateOverride);
- mThrottledConnection = createConnection(mThrottledConnectionEventCallRecorder,
- gui::ISurfaceComposer::EventRegistration::modeChanged,
- mThrottledConnectionUid);
-
- // A display must be connected for VSYNC events to be delivered.
- mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, true);
- expectHotplugEventReceivedByConnection(INTERNAL_DISPLAY_ID, true);
}
EventThreadTest::~EventThreadTest() {
@@ -179,14 +168,12 @@
EXPECT_TRUE(mVSyncCallbackUnregisterRecorder.waitForCall().has_value());
}
-void EventThreadTest::createThread() {
+void EventThreadTest::setupEventThread(std::chrono::nanoseconds vsyncPeriod) {
const auto throttleVsync = [&](nsecs_t expectedVsyncTimestamp, uid_t uid) {
mThrottleVsyncCallRecorder.getInvocable()(expectedVsyncTimestamp, uid);
return (uid == mThrottledConnectionUid);
};
- const auto getVsyncPeriod = [](uid_t uid) {
- return VSYNC_PERIOD.count();
- };
+ const auto getVsyncPeriod = [vsyncPeriod](uid_t uid) { return vsyncPeriod.count(); };
mTokenManager = std::make_unique<frametimeline::impl::TokenManager>();
mThread = std::make_unique<impl::EventThread>("EventThreadTest", mVsyncSchedule,
@@ -195,6 +182,18 @@
// EventThread should register itself as VSyncSource callback.
EXPECT_TRUE(mVSyncCallbackRegisterRecorder.waitForCall().has_value());
+
+ mConnection =
+ createConnection(mConnectionEventCallRecorder,
+ gui::ISurfaceComposer::EventRegistration::modeChanged |
+ gui::ISurfaceComposer::EventRegistration::frameRateOverride);
+ mThrottledConnection = createConnection(mThrottledConnectionEventCallRecorder,
+ gui::ISurfaceComposer::EventRegistration::modeChanged,
+ mThrottledConnectionUid);
+
+ // A display must be connected for VSYNC events to be delivered.
+ mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, true);
+ expectHotplugEventReceivedByConnection(INTERNAL_DISPLAY_ID, true);
}
sp<EventThreadTest::MockEventThreadConnection> EventThreadTest::createConnection(
@@ -259,7 +258,7 @@
ASSERT_TRUE(args.has_value()) << " did not receive an event for timestamp "
<< expectedTimestamp;
const auto& event = std::get<0>(args.value());
- for (int i = 0; i < VsyncEventData::kFrameTimelinesLength; i++) {
+ for (int i = 0; i < event.vsync.vsyncData.frameTimelinesLength; i++) {
auto prediction = mTokenManager->getPredictionsForToken(
event.vsync.vsyncData.frameTimelines[i].vsyncId);
EXPECT_TRUE(prediction.has_value());
@@ -293,6 +292,21 @@
}
}
+void EventThreadTest::expectVsyncEventDataFrameTimelinesValidLength(
+ VsyncEventData vsyncEventData, std::chrono::nanoseconds vsyncPeriod) {
+ float nonPreferredTimelinesAmount =
+ scheduler::VsyncConfig::kEarlyLatchMaxThreshold / vsyncPeriod;
+ EXPECT_LE(vsyncEventData.frameTimelinesLength, nonPreferredTimelinesAmount + 1)
+ << "Amount of non-preferred frame timelines too many;"
+ << " expected presentation time will be over threshold";
+ EXPECT_LT(nonPreferredTimelinesAmount, VsyncEventData::kFrameTimelinesCapacity)
+ << "Amount of non-preferred frame timelines should be less than max capacity";
+ EXPECT_GT(static_cast<int64_t>(vsyncEventData.frameTimelinesLength), 0)
+ << "Frame timelines length should be greater than 0";
+ EXPECT_LT(vsyncEventData.preferredFrameTimelineIndex, vsyncEventData.frameTimelinesLength)
+ << "Preferred frame timeline index should be less than frame timelines length";
+}
+
void EventThreadTest::expectHotplugEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
bool expectedConnected) {
auto args = mConnectionEventCallRecorder.waitForCall();
@@ -343,6 +357,8 @@
*/
TEST_F(EventThreadTest, canCreateAndDestroyThreadWithNoEventsSent) {
+ setupEventThread(VSYNC_PERIOD);
+
EXPECT_FALSE(mVSyncCallbackRegisterRecorder.waitForCall(0us).has_value());
EXPECT_FALSE(mVSyncCallbackScheduleRecorder.waitForCall(0us).has_value());
EXPECT_FALSE(mVSyncCallbackUpdateRecorder.waitForCall(0us).has_value());
@@ -352,6 +368,8 @@
}
TEST_F(EventThreadTest, vsyncRequestIsIgnoredIfDisplayIsDisconnected) {
+ setupEventThread(VSYNC_PERIOD);
+
mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, false);
expectHotplugEventReceivedByConnection(INTERNAL_DISPLAY_ID, false);
@@ -363,6 +381,8 @@
}
TEST_F(EventThreadTest, requestNextVsyncPostsASingleVSyncEventToTheConnection) {
+ setupEventThread(VSYNC_PERIOD);
+
// Signal that we want the next vsync event to be posted to the connection
mThread->requestNextVsync(mConnection);
@@ -394,6 +414,8 @@
}
TEST_F(EventThreadTest, requestNextVsyncEventFrameTimelinesCorrect) {
+ setupEventThread(VSYNC_PERIOD);
+
// Signal that we want the next vsync event to be posted to the connection
mThread->requestNextVsync(mConnection);
@@ -405,7 +427,34 @@
expectVsyncEventFrameTimelinesCorrect(123, {-1, 789, 456});
}
+TEST_F(EventThreadTest, requestNextVsyncEventFrameTimelinesValidLength) {
+ // The VsyncEventData should not have kFrameTimelinesCapacity amount of valid frame timelines,
+ // due to longer vsync period and kEarlyLatchMaxThreshold. Use length-2 to avoid decimal
+ // truncation (e.g. 60Hz has 16.6... ms vsync period).
+ std::chrono::nanoseconds vsyncPeriod(scheduler::VsyncConfig::kEarlyLatchMaxThreshold /
+ (VsyncEventData::kFrameTimelinesCapacity - 2));
+ setupEventThread(vsyncPeriod);
+
+ // Signal that we want the next vsync event to be posted to the connection
+ mThread->requestNextVsync(mConnection);
+
+ expectVSyncCallbackScheduleReceived(true);
+
+ // Use the received callback to signal a vsync event.
+ // The throttler should receive the event, as well as the connection.
+ nsecs_t expectedTimestamp = 123;
+ onVSyncEvent(expectedTimestamp, 456, 789);
+
+ auto args = mConnectionEventCallRecorder.waitForCall();
+ ASSERT_TRUE(args.has_value()) << " did not receive an event for timestamp "
+ << expectedTimestamp;
+ const VsyncEventData vsyncEventData = std::get<0>(args.value()).vsync.vsyncData;
+ expectVsyncEventDataFrameTimelinesValidLength(vsyncEventData, vsyncPeriod);
+}
+
TEST_F(EventThreadTest, getLatestVsyncEventData) {
+ setupEventThread(VSYNC_PERIOD);
+
const nsecs_t now = systemTime();
const nsecs_t preferredExpectedPresentationTime = now + 20000000;
const nsecs_t preferredDeadline = preferredExpectedPresentationTime - kReadyDuration.count();
@@ -420,9 +469,10 @@
// Check EventThread immediately requested a resync.
EXPECT_TRUE(mResyncCallRecorder.waitForCall().has_value());
+ expectVsyncEventDataFrameTimelinesValidLength(vsyncEventData, VSYNC_PERIOD);
EXPECT_GT(vsyncEventData.frameTimelines[0].deadlineTimestamp, now)
<< "Deadline timestamp should be greater than frame time";
- for (size_t i = 0; i < VsyncEventData::kFrameTimelinesLength; i++) {
+ for (size_t i = 0; i < vsyncEventData.frameTimelinesLength; i++) {
auto prediction =
mTokenManager->getPredictionsForToken(vsyncEventData.frameTimelines[i].vsyncId);
EXPECT_TRUE(prediction.has_value());
@@ -458,6 +508,8 @@
}
TEST_F(EventThreadTest, setVsyncRateZeroPostsNoVSyncEventsToThatConnection) {
+ setupEventThread(VSYNC_PERIOD);
+
// Create a first connection, register it, and request a vsync rate of zero.
ConnectionEventRecorder firstConnectionEventRecorder{0};
sp<MockEventThreadConnection> firstConnection = createConnection(firstConnectionEventRecorder);
@@ -485,6 +537,8 @@
}
TEST_F(EventThreadTest, setVsyncRateOnePostsAllEventsToThatConnection) {
+ setupEventThread(VSYNC_PERIOD);
+
mThread->setVsyncRate(1, mConnection);
// EventThread should enable vsync callbacks.
@@ -508,6 +562,8 @@
}
TEST_F(EventThreadTest, setVsyncRateTwoPostsEveryOtherEventToThatConnection) {
+ setupEventThread(VSYNC_PERIOD);
+
mThread->setVsyncRate(2, mConnection);
// EventThread should enable vsync callbacks.
@@ -534,6 +590,8 @@
}
TEST_F(EventThreadTest, connectionsRemovedIfInstanceDestroyed) {
+ setupEventThread(VSYNC_PERIOD);
+
mThread->setVsyncRate(1, mConnection);
// EventThread should enable vsync callbacks.
@@ -551,6 +609,8 @@
}
TEST_F(EventThreadTest, connectionsRemovedIfEventDeliveryError) {
+ setupEventThread(VSYNC_PERIOD);
+
ConnectionEventRecorder errorConnectionEventRecorder{NO_MEMORY};
sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
mThread->setVsyncRate(1, errorConnection);
@@ -575,6 +635,8 @@
}
TEST_F(EventThreadTest, tracksEventConnections) {
+ setupEventThread(VSYNC_PERIOD);
+
EXPECT_EQ(2, mThread->getEventThreadConnectionCount());
ConnectionEventRecorder errorConnectionEventRecorder{NO_MEMORY};
sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
@@ -598,6 +660,8 @@
}
TEST_F(EventThreadTest, eventsDroppedIfNonfatalEventDeliveryError) {
+ setupEventThread(VSYNC_PERIOD);
+
ConnectionEventRecorder errorConnectionEventRecorder{WOULD_BLOCK};
sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
mThread->setVsyncRate(1, errorConnection);
@@ -622,31 +686,43 @@
}
TEST_F(EventThreadTest, setPhaseOffsetForwardsToVSyncSource) {
+ setupEventThread(VSYNC_PERIOD);
+
mThread->setDuration(321ns, 456ns);
expectVSyncSetDurationCallReceived(321ns, 456ns);
}
TEST_F(EventThreadTest, postHotplugInternalDisconnect) {
+ setupEventThread(VSYNC_PERIOD);
+
mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, false);
expectHotplugEventReceivedByConnection(INTERNAL_DISPLAY_ID, false);
}
TEST_F(EventThreadTest, postHotplugInternalConnect) {
+ setupEventThread(VSYNC_PERIOD);
+
mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, true);
expectHotplugEventReceivedByConnection(INTERNAL_DISPLAY_ID, true);
}
TEST_F(EventThreadTest, postHotplugExternalDisconnect) {
+ setupEventThread(VSYNC_PERIOD);
+
mThread->onHotplugReceived(EXTERNAL_DISPLAY_ID, false);
expectHotplugEventReceivedByConnection(EXTERNAL_DISPLAY_ID, false);
}
TEST_F(EventThreadTest, postHotplugExternalConnect) {
+ setupEventThread(VSYNC_PERIOD);
+
mThread->onHotplugReceived(EXTERNAL_DISPLAY_ID, true);
expectHotplugEventReceivedByConnection(EXTERNAL_DISPLAY_ID, true);
}
TEST_F(EventThreadTest, postConfigChangedPrimary) {
+ setupEventThread(VSYNC_PERIOD);
+
const auto mode = DisplayMode::Builder(hal::HWConfigId(0))
.setPhysicalDisplayId(INTERNAL_DISPLAY_ID)
.setId(DisplayModeId(7))
@@ -659,6 +735,8 @@
}
TEST_F(EventThreadTest, postConfigChangedExternal) {
+ setupEventThread(VSYNC_PERIOD);
+
const auto mode = DisplayMode::Builder(hal::HWConfigId(0))
.setPhysicalDisplayId(EXTERNAL_DISPLAY_ID)
.setId(DisplayModeId(5))
@@ -671,6 +749,8 @@
}
TEST_F(EventThreadTest, postConfigChangedPrimary64bit) {
+ setupEventThread(VSYNC_PERIOD);
+
const auto mode = DisplayMode::Builder(hal::HWConfigId(0))
.setPhysicalDisplayId(DISPLAY_ID_64BIT)
.setId(DisplayModeId(7))
@@ -682,6 +762,8 @@
}
TEST_F(EventThreadTest, suppressConfigChanged) {
+ setupEventThread(VSYNC_PERIOD);
+
ConnectionEventRecorder suppressConnectionEventRecorder{0};
sp<MockEventThreadConnection> suppressConnection =
createConnection(suppressConnectionEventRecorder);
@@ -701,6 +783,8 @@
}
TEST_F(EventThreadTest, postUidFrameRateMapping) {
+ setupEventThread(VSYNC_PERIOD);
+
const std::vector<FrameRateOverride> overrides = {
{.uid = 1, .frameRateHz = 20},
{.uid = 3, .frameRateHz = 40},
@@ -712,6 +796,8 @@
}
TEST_F(EventThreadTest, suppressUidFrameRateMapping) {
+ setupEventThread(VSYNC_PERIOD);
+
const std::vector<FrameRateOverride> overrides = {
{.uid = 1, .frameRateHz = 20},
{.uid = 3, .frameRateHz = 40},
@@ -730,6 +816,8 @@
}
TEST_F(EventThreadTest, requestNextVsyncWithThrottleVsyncDoesntPostVSync) {
+ setupEventThread(VSYNC_PERIOD);
+
// Signal that we want the next vsync event to be posted to the throttled connection
mThread->requestNextVsync(mThrottledConnection);
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index dc76b4c..965e378 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -359,7 +359,8 @@
EXPECT_EQ(expectedChoices, actualChoices);
}
{
- // This display does not support 120 Hz, so we should choose 60 Hz despite the touch signal.
+ // The kDisplayId3 does not support 120Hz, The pacesetter display rate is chosen to be 120
+ // Hz. In this case only the display kDisplayId3 choose 60Hz as it does not support 120Hz.
mScheduler
->registerDisplay(kDisplayId3,
std::make_shared<RefreshRateSelector>(kDisplay3Modes,
@@ -371,6 +372,26 @@
expectedChoices = ftl::init::map<
const PhysicalDisplayId&,
+ DisplayModeChoice>(kDisplayId1, FrameRateMode{120_Hz, kDisplay1Mode120},
+ globalSignals)(kDisplayId2,
+ FrameRateMode{120_Hz, kDisplay2Mode120},
+ globalSignals)(kDisplayId3,
+ FrameRateMode{60_Hz,
+ kDisplay3Mode60},
+ globalSignals);
+
+ const auto actualChoices = mScheduler->chooseDisplayModes();
+ EXPECT_EQ(expectedChoices, actualChoices);
+ }
+ {
+ // We should choose 60Hz despite the touch signal as pacesetter only supports 60Hz
+ mScheduler->setPacesetterDisplay(kDisplayId3);
+ const GlobalSignals globalSignals = {.touch = true};
+ mScheduler->replaceTouchTimer(10);
+ mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+
+ expectedChoices = ftl::init::map<
+ const PhysicalDisplayId&,
DisplayModeChoice>(kDisplayId1, FrameRateMode{60_Hz, kDisplay1Mode60},
globalSignals)(kDisplayId2,
FrameRateMode{60_Hz, kDisplay2Mode60},
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayStatsTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayStatsTest.cpp
new file mode 100644
index 0000000..29acfaa
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayStatsTest.cpp
@@ -0,0 +1,116 @@
+/*
+ * 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 "SurfaceFlingerGetDisplayStatsTest"
+
+#include <compositionengine/Display.h>
+#include <compositionengine/mock/DisplaySurface.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <renderengine/mock/RenderEngine.h>
+#include <ui/DisplayStatInfo.h>
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockComposer.h"
+#include "mock/DisplayHardware/MockPowerAdvisor.h"
+#include "mock/MockTimeStats.h"
+#include "mock/system/window/MockNativeWindow.h"
+
+using namespace android;
+using namespace testing;
+
+namespace android {
+namespace {
+using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
+
+constexpr hal::HWDisplayId HWC_DISPLAY = FakeHwcDisplayInjector::DEFAULT_HWC_DISPLAY_ID;
+constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u);
+constexpr int DEFAULT_DISPLAY_WIDTH = 1920;
+constexpr int DEFAULT_DISPLAY_HEIGHT = 1024;
+
+class SurfaceFlingerGetDisplayStatsTest : public Test {
+public:
+ void SetUp() override;
+
+protected:
+ TestableSurfaceFlinger mFlinger;
+ renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
+ sp<DisplayDevice> mDisplay;
+ sp<compositionengine::mock::DisplaySurface> mDisplaySurface =
+ sp<compositionengine::mock::DisplaySurface>::make();
+ sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make();
+ mock::TimeStats* mTimeStats = new mock::TimeStats();
+ Hwc2::mock::PowerAdvisor* mPowerAdvisor = nullptr;
+ Hwc2::mock::Composer* mComposer = nullptr;
+};
+
+void SurfaceFlingerGetDisplayStatsTest::SetUp() {
+ mFlinger.setupMockScheduler({.displayId = DEFAULT_DISPLAY_ID});
+ mComposer = new Hwc2::mock::Composer();
+ mPowerAdvisor = new Hwc2::mock::PowerAdvisor();
+ mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
+ mFlinger.setupTimeStats(std::shared_ptr<TimeStats>(mTimeStats));
+ mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
+ mFlinger.setupPowerAdvisor(std::unique_ptr<Hwc2::PowerAdvisor>(mPowerAdvisor));
+ static constexpr bool kIsPrimary = true;
+ FakeHwcDisplayInjector(DEFAULT_DISPLAY_ID, hal::DisplayType::PHYSICAL, kIsPrimary)
+ .setPowerMode(hal::PowerMode::ON)
+ .inject(&mFlinger, mComposer);
+ auto compostionEngineDisplayArgs =
+ compositionengine::DisplayCreationArgsBuilder()
+ .setId(DEFAULT_DISPLAY_ID)
+ .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
+ .setPowerAdvisor(mPowerAdvisor)
+ .setName("injected display")
+ .build();
+ auto compositionDisplay =
+ compositionengine::impl::createDisplay(mFlinger.getCompositionEngine(),
+ std::move(compostionEngineDisplayArgs));
+ mDisplay =
+ FakeDisplayDeviceInjector(mFlinger, compositionDisplay,
+ ui::DisplayConnectionType::Internal, HWC_DISPLAY, kIsPrimary)
+ .setDisplaySurface(mDisplaySurface)
+ .setNativeWindow(mNativeWindow)
+ .setPowerMode(hal::PowerMode::ON)
+ .setRefreshRateSelector(mFlinger.scheduler()->refreshRateSelector())
+ .skipRegisterDisplay()
+ .inject();
+}
+
+// TODO (b/277364366): Clients should be updated to pass in the display they want.
+TEST_F(SurfaceFlingerGetDisplayStatsTest, nullptrSucceeds) {
+ DisplayStatInfo info;
+ status_t status = mFlinger.getDisplayStats(nullptr, &info);
+ EXPECT_EQ(status, NO_ERROR);
+}
+
+TEST_F(SurfaceFlingerGetDisplayStatsTest, explicitToken) {
+ DisplayStatInfo info;
+ status_t status = mFlinger.getDisplayStats(mDisplay->getDisplayToken().promote(), &info);
+ EXPECT_EQ(status, NO_ERROR);
+}
+
+TEST_F(SurfaceFlingerGetDisplayStatsTest, invalidToken) {
+ const String8 displayName("fakeDisplay");
+ sp<IBinder> displayToken = mFlinger.createDisplay(displayName, false);
+ DisplayStatInfo info;
+ status_t status = mFlinger.getDisplayStats(displayToken, &info);
+ EXPECT_EQ(status, NAME_NOT_FOUND);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index cfa366f..a189c00 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -56,6 +56,9 @@
#include "mock/system/window/MockNativeWindow.h"
namespace android {
+
+struct DisplayStatInfo;
+
namespace renderengine {
class RenderEngine;
@@ -545,6 +548,10 @@
return sp<DisplayDevice>::make(creationArgs);
}
+ status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* outInfo) {
+ return mFlinger->getDisplayStats(displayToken, outInfo);
+ }
+
/* ------------------------------------------------------------------------
* Read-only access to private data to assert post-conditions.
*/
diff --git a/services/surfaceflinger/tests/unittests/VSyncCallbackRegistrationTest.cpp b/services/surfaceflinger/tests/unittests/VSyncCallbackRegistrationTest.cpp
new file mode 100644
index 0000000..69b3861
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VSyncCallbackRegistrationTest.cpp
@@ -0,0 +1,144 @@
+/*
+ * 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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "Scheduler/VSyncDispatch.h"
+#include "mock/MockVSyncDispatch.h"
+
+using namespace testing;
+
+namespace android::scheduler {
+
+class VSyncCallbackRegistrationTest : public Test {
+protected:
+ VSyncDispatch::Callback mCallback = [](nsecs_t, nsecs_t, nsecs_t) {};
+
+ std::shared_ptr<mock::VSyncDispatch> mVsyncDispatch = std::make_shared<mock::VSyncDispatch>();
+ VSyncDispatch::CallbackToken mCallbackToken{7};
+ std::string mCallbackName = "callback";
+
+ std::shared_ptr<mock::VSyncDispatch> mVsyncDispatch2 = std::make_shared<mock::VSyncDispatch>();
+ VSyncDispatch::CallbackToken mCallbackToken2{42};
+ std::string mCallbackName2 = "callback2";
+
+ void assertDispatch(const VSyncCallbackRegistration& registration,
+ std::shared_ptr<VSyncDispatch> dispatch) {
+ ASSERT_EQ(registration.mDispatch, dispatch);
+ }
+
+ void assertToken(const VSyncCallbackRegistration& registration,
+ const std::optional<VSyncDispatch::CallbackToken>& token) {
+ ASSERT_EQ(registration.mToken, token);
+ }
+};
+
+TEST_F(VSyncCallbackRegistrationTest, unregistersCallbackOnDestruction) {
+ // TODO (b/279581095): With ftl::Function, `_` can be replaced with
+ // `mCallback`, here and in other calls to `registerCallback, since the
+ // ftl version has an operator==, unlike std::function.
+ EXPECT_CALL(*mVsyncDispatch, registerCallback(_, mCallbackName))
+ .WillOnce(Return(mCallbackToken));
+ EXPECT_CALL(*mVsyncDispatch, unregisterCallback(mCallbackToken)).Times(1);
+
+ VSyncCallbackRegistration registration(mVsyncDispatch, mCallback, mCallbackName);
+ ASSERT_NO_FATAL_FAILURE(assertDispatch(registration, mVsyncDispatch));
+ ASSERT_NO_FATAL_FAILURE(assertToken(registration, mCallbackToken));
+}
+
+TEST_F(VSyncCallbackRegistrationTest, unregistersCallbackOnPointerMove) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mVsyncDispatch, registerCallback(_, mCallbackName))
+ .WillOnce(Return(mCallbackToken));
+ EXPECT_CALL(*mVsyncDispatch2, registerCallback(_, mCallbackName2))
+ .WillOnce(Return(mCallbackToken2));
+ EXPECT_CALL(*mVsyncDispatch2, unregisterCallback(mCallbackToken2)).Times(1);
+ EXPECT_CALL(*mVsyncDispatch, unregisterCallback(mCallbackToken)).Times(1);
+ }
+
+ auto registration =
+ std::make_unique<VSyncCallbackRegistration>(mVsyncDispatch, mCallback, mCallbackName);
+
+ auto registration2 =
+ std::make_unique<VSyncCallbackRegistration>(mVsyncDispatch2, mCallback, mCallbackName2);
+
+ registration2 = std::move(registration);
+
+ ASSERT_NO_FATAL_FAILURE(assertDispatch(*registration2.get(), mVsyncDispatch));
+ ASSERT_NO_FATAL_FAILURE(assertToken(*registration2.get(), mCallbackToken));
+}
+
+TEST_F(VSyncCallbackRegistrationTest, unregistersCallbackOnMoveOperator) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mVsyncDispatch, registerCallback(_, mCallbackName))
+ .WillOnce(Return(mCallbackToken));
+ EXPECT_CALL(*mVsyncDispatch2, registerCallback(_, mCallbackName2))
+ .WillOnce(Return(mCallbackToken2));
+ EXPECT_CALL(*mVsyncDispatch2, unregisterCallback(mCallbackToken2)).Times(1);
+ EXPECT_CALL(*mVsyncDispatch, unregisterCallback(mCallbackToken)).Times(1);
+ }
+
+ VSyncCallbackRegistration registration(mVsyncDispatch, mCallback, mCallbackName);
+
+ VSyncCallbackRegistration registration2(mVsyncDispatch2, mCallback, mCallbackName2);
+
+ registration2 = std::move(registration);
+
+ ASSERT_NO_FATAL_FAILURE(assertDispatch(registration, nullptr));
+ ASSERT_NO_FATAL_FAILURE(assertToken(registration, std::nullopt));
+
+ ASSERT_NO_FATAL_FAILURE(assertDispatch(registration2, mVsyncDispatch));
+ ASSERT_NO_FATAL_FAILURE(assertToken(registration2, mCallbackToken));
+}
+
+TEST_F(VSyncCallbackRegistrationTest, moveConstructor) {
+ EXPECT_CALL(*mVsyncDispatch, registerCallback(_, mCallbackName))
+ .WillOnce(Return(mCallbackToken));
+ EXPECT_CALL(*mVsyncDispatch, unregisterCallback(mCallbackToken)).Times(1);
+
+ VSyncCallbackRegistration registration(mVsyncDispatch, mCallback, mCallbackName);
+ VSyncCallbackRegistration registration2(std::move(registration));
+
+ ASSERT_NO_FATAL_FAILURE(assertDispatch(registration, nullptr));
+ ASSERT_NO_FATAL_FAILURE(assertToken(registration, std::nullopt));
+
+ ASSERT_NO_FATAL_FAILURE(assertDispatch(registration2, mVsyncDispatch));
+ ASSERT_NO_FATAL_FAILURE(assertToken(registration2, mCallbackToken));
+}
+
+TEST_F(VSyncCallbackRegistrationTest, moveOperatorEqualsSelf) {
+ EXPECT_CALL(*mVsyncDispatch, registerCallback(_, mCallbackName))
+ .WillOnce(Return(mCallbackToken));
+ EXPECT_CALL(*mVsyncDispatch, unregisterCallback(mCallbackToken)).Times(1);
+
+ VSyncCallbackRegistration registration(mVsyncDispatch, mCallback, mCallbackName);
+
+ // Use a reference so the compiler doesn't realize that registration is
+ // being moved to itself.
+ VSyncCallbackRegistration& registrationRef = registration;
+ registration = std::move(registrationRef);
+
+ ASSERT_NO_FATAL_FAILURE(assertDispatch(registration, mVsyncDispatch));
+ ASSERT_NO_FATAL_FAILURE(assertToken(registration, mCallbackToken));
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index 0d94f4c..50e07fc 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -32,7 +32,7 @@
MOCK_CONST_METHOD0(getType, const char*());
MOCK_METHOD0(getFrameSelectionPriority, int32_t());
MOCK_CONST_METHOD0(isVisible, bool());
- MOCK_METHOD0(createClone, sp<Layer>());
+ MOCK_METHOD1(createClone, sp<Layer>(uint32_t));
MOCK_CONST_METHOD0(getFrameRateForLayerTree, FrameRate());
MOCK_CONST_METHOD0(getDefaultFrameRateCompatibility,
scheduler::LayerInfo::FrameRateCompatibility());