summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-11-01 21:37:22 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-11-01 21:37:22 +0000
commit2a29a5603f6ce1881a72e80ffebff3a92aa2ea48 (patch)
treeffebaaf45ab249d55507d07426c68fe221f76794
parent2fe7eddf185ecb63223eeadc58804f9bf6cbd426 (diff)
parente3ad741d3997a408c91382620a831de088c5cd0c (diff)
downloadnative-android-platform-13.0.0_r4.tar.gz
Snap for 9239618 from e3ad741d3997a408c91382620a831de088c5cd0c to tm-platform-releaseandroid-platform-13.0.0_r4android-platform-13.0.0_r3
Change-Id: I55bbf43d148bbc9cdafb1f0bfd54b96adbde9389
-rw-r--r--cmds/dumpstate/DumpstateInternal.cpp10
-rw-r--r--cmds/installd/utils.cpp4
-rw-r--r--include/android/choreographer.h62
-rw-r--r--include/android/surface_control.h18
-rw-r--r--include/input/Input.h2
-rw-r--r--include/input/InputDevice.h6
-rw-r--r--include/input/KeyLayoutMap.h10
-rw-r--r--include/input/Keyboard.h4
-rw-r--r--include/input/PrintTools.h10
-rw-r--r--libs/dumputils/dump_utils.cpp1
-rw-r--r--libs/gui/BLASTBufferQueue.cpp70
-rw-r--r--libs/gui/SurfaceComposerClient.cpp10
-rw-r--r--libs/gui/include/gui/BLASTBufferQueue.h12
-rw-r--r--libs/gui/include/gui/SurfaceComposerClient.h11
-rw-r--r--libs/gui/include/gui/test/CallbackUtils.h3
-rw-r--r--libs/gui/tests/BLASTBufferQueue_test.cpp37
-rw-r--r--libs/gui/tests/EndToEndNativeInputTest.cpp11
-rw-r--r--libs/input/Android.bp1
-rw-r--r--libs/input/Input.cpp24
-rw-r--r--libs/input/InputDevice.cpp30
-rw-r--r--libs/input/KeyLayoutMap.cpp197
-rw-r--r--libs/input/Keyboard.cpp39
-rw-r--r--libs/input/tests/Android.bp3
-rw-r--r--libs/input/tests/InputDevice_test.cpp20
-rw-r--r--libs/input/tests/InputEvent_test.cpp10
-rw-r--r--libs/input/tests/data/kl_with_required_fake_config.kl20
-rw-r--r--libs/input/tests/data/kl_with_required_real_config.kl21
-rw-r--r--services/inputflinger/InputListener.cpp13
-rw-r--r--services/inputflinger/UnwantedInteractionBlocker.cpp253
-rw-r--r--services/inputflinger/UnwantedInteractionBlocker.h32
-rw-r--r--services/inputflinger/dispatcher/DebugConfig.h5
-rw-r--r--services/inputflinger/dispatcher/InputDispatcher.cpp70
-rw-r--r--services/inputflinger/dispatcher/InputTarget.cpp4
-rw-r--r--services/inputflinger/dispatcher/TouchedWindow.h2
-rw-r--r--services/inputflinger/reader/InputDevice.cpp13
-rw-r--r--services/inputflinger/reader/InputReader.cpp9
-rw-r--r--services/inputflinger/reader/controller/PeripheralController.cpp4
-rw-r--r--services/inputflinger/reader/controller/PeripheralController.h2
-rw-r--r--services/inputflinger/reader/controller/PeripheralControllerInterface.h2
-rw-r--r--services/inputflinger/reader/mapper/CursorInputMapper.cpp115
-rw-r--r--services/inputflinger/reader/mapper/CursorInputMapper.h19
-rw-r--r--services/inputflinger/reader/mapper/TouchInputMapper.cpp46
-rw-r--r--services/inputflinger/tests/Android.bp1
-rw-r--r--services/inputflinger/tests/InputDispatcher_test.cpp167
-rw-r--r--services/inputflinger/tests/InputReader_test.cpp286
-rw-r--r--services/inputflinger/tests/TestInputListener.cpp7
-rw-r--r--services/inputflinger/tests/TestInputListener.h3
-rw-r--r--services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp249
-rw-r--r--services/sensorservice/AidlSensorHalWrapper.cpp2
-rw-r--r--services/sensorservice/SensorService.cpp19
-rw-r--r--services/sensorservice/SensorService.h1
-rw-r--r--services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h3
-rw-r--r--services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h3
-rw-r--r--services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h3
-rw-r--r--services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h2
-rw-r--r--services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h2
-rw-r--r--services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h3
-rw-r--r--services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h3
-rw-r--r--services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h1
-rw-r--r--services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h2
-rw-r--r--services/surfaceflinger/CompositionEngine/src/Display.cpp65
-rw-r--r--services/surfaceflinger/CompositionEngine/src/Output.cpp43
-rw-r--r--services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp5
-rw-r--r--services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp5
-rw-r--r--services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp8
-rw-r--r--services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp1
-rw-r--r--services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h1
-rw-r--r--services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h28
-rw-r--r--services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp44
-rw-r--r--services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp65
-rw-r--r--services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp96
-rw-r--r--services/surfaceflinger/DisplayDevice.cpp49
-rw-r--r--services/surfaceflinger/DisplayDevice.h15
-rw-r--r--services/surfaceflinger/DisplayHardware/HWComposer.cpp7
-rw-r--r--services/surfaceflinger/DisplayHardware/HWComposer.h5
-rw-r--r--services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp476
-rw-r--r--services/surfaceflinger/DisplayHardware/PowerAdvisor.h243
-rw-r--r--services/surfaceflinger/Layer.cpp17
-rw-r--r--services/surfaceflinger/Layer.h6
-rw-r--r--services/surfaceflinger/RefreshRateOverlay.cpp51
-rw-r--r--services/surfaceflinger/RefreshRateOverlay.h16
-rw-r--r--services/surfaceflinger/Scheduler/DispSyncSource.cpp6
-rw-r--r--services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp142
-rw-r--r--services/surfaceflinger/Scheduler/RefreshRateConfigs.h3
-rw-r--r--services/surfaceflinger/Scheduler/Scheduler.cpp17
-rw-r--r--services/surfaceflinger/Scheduler/Scheduler.h4
-rw-r--r--services/surfaceflinger/Scheduler/VSyncReactor.cpp10
-rw-r--r--services/surfaceflinger/Scheduler/VSyncReactor.h4
-rw-r--r--services/surfaceflinger/Scheduler/VsyncController.h10
-rw-r--r--services/surfaceflinger/SurfaceFlinger.cpp143
-rw-r--r--services/surfaceflinger/SurfaceFlinger.h13
-rw-r--r--services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp85
-rw-r--r--services/surfaceflinger/tests/unittests/Android.bp2
-rw-r--r--services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp11
-rw-r--r--services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp203
-rw-r--r--services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp45
-rw-r--r--services/surfaceflinger/tests/unittests/SchedulerTest.cpp8
-rw-r--r--services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp28
-rw-r--r--services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h5
-rw-r--r--services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp17
-rw-r--r--services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.cpp26
-rw-r--r--services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h51
-rw-r--r--services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h28
-rw-r--r--services/surfaceflinger/tests/unittests/mock/MockVsyncController.h1
104 files changed, 3108 insertions, 992 deletions
diff --git a/cmds/dumpstate/DumpstateInternal.cpp b/cmds/dumpstate/DumpstateInternal.cpp
index 3091f6b5d0..6f7fea3432 100644
--- a/cmds/dumpstate/DumpstateInternal.cpp
+++ b/cmds/dumpstate/DumpstateInternal.cpp
@@ -162,17 +162,16 @@ int DumpFileFromFdToFd(const std::string& title, const std::string& path_string,
return 0;
}
bool newline = false;
+ int poll_timeout_ms = 30 * 1000;
while (true) {
- uint64_t start_time = Nanotime();
pollfd fds[] = { { .fd = fd, .events = POLLIN } };
- int ret = TEMP_FAILURE_RETRY(poll(fds, arraysize(fds), 30 * 1000));
+ int ret = TEMP_FAILURE_RETRY(poll(fds, arraysize(fds), poll_timeout_ms));
if (ret == -1) {
dprintf(out_fd, "*** %s: poll failed: %s\n", path, strerror(errno));
newline = true;
break;
- } else if (ret == 0) {
- uint64_t elapsed = Nanotime() - start_time;
- dprintf(out_fd, "*** %s: Timed out after %.3fs\n", path, (float)elapsed / NANOS_PER_SEC);
+ } else if (ret == 0 && poll_timeout_ms != 0) {
+ dprintf(out_fd, "*** %s: Timed out after %ds\n", path, poll_timeout_ms / 1000 );
newline = true;
break;
} else {
@@ -189,6 +188,7 @@ int DumpFileFromFdToFd(const std::string& title, const std::string& path_string,
break;
}
}
+ poll_timeout_ms = 0;
}
if (!newline) dprintf(out_fd, "\n");
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 45aeab6fa8..4d9b71016e 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -1184,8 +1184,8 @@ static int wait_child(pid_t pid) {
int wait_child_with_timeout(pid_t pid, int timeout_ms) {
int pidfd = pidfd_open(pid, /*flags=*/0);
if (pidfd < 0) {
- PLOG(ERROR) << "pidfd_open failed for pid " << pid;
- kill(pid, SIGKILL);
+ PLOG(ERROR) << "pidfd_open failed for pid " << pid
+ << ", waiting for child process without timeout";
return wait_child(pid);
}
diff --git a/include/android/choreographer.h b/include/android/choreographer.h
index 63aa7ff6c0..cd8e63dec2 100644
--- a/include/android/choreographer.h
+++ b/include/android/choreographer.h
@@ -16,6 +16,28 @@
/**
* @addtogroup Choreographer
+ *
+ * Choreographer coordinates the timing of frame rendering. This is the C version of the
+ * android.view.Choreographer object in Java.
+ *
+ * As of API level 33, apps can follow proper frame pacing and even choose a future frame to render.
+ * The API is used as follows:
+ * 1. The app posts an {@link AChoreographer_vsyncCallback} to Choreographer to run on the next
+ * frame.
+ * 2. The callback is called when it is the time to start the frame with an {@link
+ * AChoreographerFrameCallbackData} payload: information about multiple possible frame
+ * timelines.
+ * 3. Apps can choose a frame timeline from the {@link
+ * AChoreographerFrameCallbackData} payload, depending on the frame deadline they can meet when
+ * rendering the frame and their desired presentation time, and subsequently
+ * {@link ASurfaceTransaction_setFrameTimeline notify SurfaceFlinger}
+ * of the choice. Alternatively, for apps that do not choose a frame timeline, their frame would be
+ * presented at the earliest possible timeline.
+ * - The preferred frame timeline is the default frame
+ * timeline that the platform scheduled for the app, based on device configuration.
+ * 4. SurfaceFlinger attempts to follow the chosen frame timeline, by not applying transactions or
+ * latching buffers before the desired presentation time.
+ *
* @{
*/
@@ -47,7 +69,8 @@ typedef int64_t AVsyncId;
struct AChoreographerFrameCallbackData;
/**
- * Opaque type that provides access to an AChoreographerFrameCallbackData object.
+ * Opaque type that provides access to an AChoreographerFrameCallbackData object, which contains
+ * various methods to extract frame information.
*/
typedef struct AChoreographerFrameCallbackData AChoreographerFrameCallbackData;
@@ -73,8 +96,9 @@ typedef void (*AChoreographer_frameCallback64)(int64_t frameTimeNanos, void* dat
/**
* Prototype of the function that is called when a new frame is being rendered.
- * It's passed the frame data that should not outlive the callback, as well as the data pointer
- * provided by the application that registered a callback.
+ * It is called with \c callbackData describing multiple frame timelines, as well as the \c data
+ * pointer provided by the application that registered a callback. The \c callbackData does not
+ * outlive the callback.
*/
typedef void (*AChoreographer_vsyncCallback)(
const AChoreographerFrameCallbackData* callbackData, void* data);
@@ -110,7 +134,7 @@ void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer,
__DEPRECATED_IN(29);
/**
- * Power a callback to be run on the next frame. The data pointer provided will
+ * Post a callback to be run on the next frame. The data pointer provided will
* be passed to the callback function when it's called.
*
* Available since API level 29.
@@ -131,8 +155,10 @@ void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer,
uint32_t delayMillis) __INTRODUCED_IN(29);
/**
- * Posts a callback to run on the next frame. The data pointer provided will
+ * Posts a callback to be run on the next frame. The data pointer provided will
* be passed to the callback function when it's called.
+ *
+ * Available since API level 33.
*/
void AChoreographer_postVsyncCallback(AChoreographer* choreographer,
AChoreographer_vsyncCallback callback, void* data)
@@ -189,7 +215,10 @@ void AChoreographer_unregisterRefreshRateCallback(AChoreographer* choreographer,
__INTRODUCED_IN(30);
/**
- * The time in nanoseconds when the frame started being rendered.
+ * The time in nanoseconds at which the frame started being rendered.
+ *
+ * Note that this time should \b not be used to advance animation clocks.
+ * Instead, see AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentationTimeNanos().
*/
int64_t AChoreographerFrameCallbackData_getFrameTimeNanos(
const AChoreographerFrameCallbackData* data) __INTRODUCED_IN(33);
@@ -201,25 +230,38 @@ size_t AChoreographerFrameCallbackData_getFrameTimelinesLength(
const AChoreographerFrameCallbackData* data) __INTRODUCED_IN(33);
/**
- * Get index of the platform-preferred FrameTimeline.
+ * Gets the index of the platform-preferred frame timeline.
+ * The preferred frame timeline is the default
+ * by which the platform scheduled the app, based on the device configuration.
*/
size_t AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex(
const AChoreographerFrameCallbackData* data) __INTRODUCED_IN(33);
/**
- * The vsync ID token used to map Choreographer data.
+ * Gets the token used by the platform to identify the frame timeline at the given \c index.
+ *
+ * \param index index of a frame timeline, in \f( [0, FrameTimelinesLength) \f). See
+ * AChoreographerFrameCallbackData_getFrameTimelinesLength()
*/
AVsyncId AChoreographerFrameCallbackData_getFrameTimelineVsyncId(
const AChoreographerFrameCallbackData* data, size_t index) __INTRODUCED_IN(33);
/**
- * The time in nanoseconds which the frame at given index is expected to be presented.
+ * Gets the time in nanoseconds at which the frame described at the given \c index is expected to
+ * be presented. This time should be used to advance any animation clocks.
+ *
+ * \param index index of a frame timeline, in \f( [0, FrameTimelinesLength) \f). See
+ * AChoreographerFrameCallbackData_getFrameTimelinesLength()
*/
int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentationTimeNanos(
const AChoreographerFrameCallbackData* data, size_t index) __INTRODUCED_IN(33);
/**
- * The time in nanoseconds which the frame at given index needs to be ready by.
+ * Gets the time in nanoseconds at which the frame described at the given \c index needs to be
+ * ready by in order to be presented on time.
+ *
+ * \param index index of a frame timeline, in \f( [0, FrameTimelinesLength) \f). See
+ * AChoreographerFrameCallbackData_getFrameTimelinesLength()
*/
int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos(
const AChoreographerFrameCallbackData* data, size_t index) __INTRODUCED_IN(33);
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index 9a36ecb537..6223ef7f82 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -597,20 +597,20 @@ void ASurfaceTransaction_setEnableBackPressure(ASurfaceTransaction* transaction,
__INTRODUCED_IN(31);
/**
- * Sets the frame timeline to use in Surface Flinger.
+ * Sets the frame timeline to use in SurfaceFlinger.
*
- * A frame timeline should be chosen based on what frame deadline the application
- * can meet when rendering the frame and the application's desired present time.
- * By setting a frame timeline, Surface Flinger tries to present the frame at the corresponding
- * expected present time.
+ * A frame timeline should be chosen based on the frame deadline the application
+ * can meet when rendering the frame and the application's desired presentation time.
+ * By setting a frame timeline, SurfaceFlinger tries to present the frame at the corresponding
+ * expected presentation time.
*
* To receive frame timelines, a callback must be posted to Choreographer using
- * AChoreographer_postExtendedFrameCallback(). The \a vsnycId can then be extracted from the
+ * AChoreographer_postVsyncCallback(). The \c vsyncId can then be extracted from the
* callback payload using AChoreographerFrameCallbackData_getFrameTimelineVsyncId().
*
- * \param vsyncId The vsync ID received from AChoreographer, setting the frame's present target to
- * the corresponding expected present time and deadline from the frame to be rendered. A stale or
- * invalid value will be ignored.
+ * \param vsyncId The vsync ID received from AChoreographer, setting the frame's presentation target
+ * to the corresponding expected presentation time and deadline from the frame to be rendered. A
+ * stale or invalid value will be ignored.
*/
void ASurfaceTransaction_setFrameTimeline(ASurfaceTransaction* transaction,
AVsyncId vsyncId) __INTRODUCED_IN(33);
diff --git a/include/input/Input.h b/include/input/Input.h
index e7d68fc349..7ea297049b 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -297,6 +297,8 @@ enum class MotionClassification : uint8_t {
*/
const char* motionClassificationToString(MotionClassification classification);
+const char* motionToolTypeToString(int32_t toolType);
+
/**
* Portion of FrameMetrics timeline of interest to input code.
*/
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index c4f03c9119..3585392c2b 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -300,6 +300,8 @@ enum class InputDeviceConfigurationFileType : int32_t {
/*
* Gets the path of an input device configuration file, if one is available.
* Considers both system provided and user installed configuration files.
+ * The optional suffix is appended to the end of the file name (before the
+ * extension).
*
* The device identifier is used to construct several default configuration file
* names to try based on the device name, vendor, product, and version.
@@ -307,8 +309,8 @@ enum class InputDeviceConfigurationFileType : int32_t {
* Returns an empty string if not found.
*/
extern std::string getInputDeviceConfigurationFilePathByDeviceIdentifier(
- const InputDeviceIdentifier& deviceIdentifier,
- InputDeviceConfigurationFileType type);
+ const InputDeviceIdentifier& deviceIdentifier, InputDeviceConfigurationFileType type,
+ const char* suffix = "");
/*
* Gets the path of an input device configuration file, if one is available.
diff --git a/include/input/KeyLayoutMap.h b/include/input/KeyLayoutMap.h
index b2bd535cbf..50849506a4 100644
--- a/include/input/KeyLayoutMap.h
+++ b/include/input/KeyLayoutMap.h
@@ -21,8 +21,8 @@
#include <stdint.h>
#include <utils/Errors.h>
#include <utils/KeyedVector.h>
-#include <utils/RefBase.h>
#include <utils/Tokenizer.h>
+#include <set>
#include <input/InputDevice.h>
@@ -65,8 +65,8 @@ struct AxisInfo {
*/
class KeyLayoutMap {
public:
- static base::Result<std::shared_ptr<KeyLayoutMap>> load(const std::string& filename);
- static base::Result<std::shared_ptr<KeyLayoutMap>> load(Tokenizer* tokenizer);
+ static base::Result<std::shared_ptr<KeyLayoutMap>> load(const std::string& filename,
+ const char* contents = nullptr);
static base::Result<std::shared_ptr<KeyLayoutMap>> loadContents(const std::string& filename,
const char* contents);
@@ -84,6 +84,8 @@ public:
virtual ~KeyLayoutMap();
private:
+ static base::Result<std::shared_ptr<KeyLayoutMap>> load(Tokenizer* tokenizer);
+
struct Key {
int32_t keyCode;
uint32_t flags;
@@ -104,6 +106,7 @@ private:
KeyedVector<int32_t, Led> mLedsByScanCode;
KeyedVector<int32_t, Led> mLedsByUsageCode;
std::unordered_map<int32_t, Sensor> mSensorsByAbsCode;
+ std::set<std::string> mRequiredKernelConfigs;
std::string mLoadFileName;
KeyLayoutMap();
@@ -124,6 +127,7 @@ private:
status_t parseAxis();
status_t parseLed();
status_t parseSensor();
+ status_t parseRequiredKernelConfig();
};
};
diff --git a/include/input/Keyboard.h b/include/input/Keyboard.h
index 08ad8c6e5a..9a3e15f1cd 100644
--- a/include/input/Keyboard.h
+++ b/include/input/Keyboard.h
@@ -61,9 +61,7 @@ private:
bool probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, const std::string& name);
status_t loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, const std::string& name);
status_t loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier,
- const std::string& name);
- std::string getPath(const InputDeviceIdentifier& deviceIdentifier,
- const std::string& name, InputDeviceConfigurationFileType type);
+ const std::string& name);
};
/**
diff --git a/include/input/PrintTools.h b/include/input/PrintTools.h
index 0a75278494..55f730b287 100644
--- a/include/input/PrintTools.h
+++ b/include/input/PrintTools.h
@@ -17,6 +17,7 @@
#pragma once
#include <map>
+#include <optional>
#include <set>
#include <string>
@@ -28,6 +29,15 @@ std::string constToString(const T& v) {
}
/**
+ * Convert an optional type to string.
+ */
+template <typename T>
+std::string toString(const std::optional<T>& optional,
+ std::string (*toString)(const T&) = constToString) {
+ return optional ? toString(*optional) : "<not set>";
+}
+
+/**
* Convert a set of integral types to string.
*/
template <typename T>
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 0f1a02a553..3551a8fd0a 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -86,6 +86,7 @@ static const char* hidl_hal_interfaces_to_dump[] {
/* list of hal interface to dump containing process during native dumps */
static const std::vector<std::string> aidl_interfaces_to_dump {
"android.hardware.camera.provider.ICameraProvider",
+ "android.hardware.input.processor.IInputProcessor",
};
/* list of extra hal interfaces to dump containing process during native dumps */
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index dbccf30fae..a51bbb1553 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -287,18 +287,17 @@ void BLASTBufferQueue::transactionCommittedCallback(nsecs_t /*latchTime*/,
// We need to check if we were waiting for a transaction callback in order to
// process any pending buffers and unblock. It's possible to get transaction
- // callbacks for previous requests so we need to ensure the frame from this
- // transaction callback matches the last acquired buffer. Since acquireNextBuffer
- // will stop processing buffers when mWaitForTransactionCallback is set, we know
- // that mLastAcquiredFrameNumber is the frame we're waiting on.
- // We also want to check if mNextTransaction is null because it's possible another
+ // callbacks for previous requests so we need to ensure that there are no pending
+ // frame numbers that were in a sync. We remove the frame from mSyncedFrameNumbers
+ // set and then check if it's empty. If there are no more pending syncs, we can
+ // proceed with flushing the shadow queue.
+ // We also want to check if mSyncTransaction is null because it's possible another
// sync request came in while waiting, but it hasn't started processing yet. In that
// case, we don't actually want to flush the frames in between since they will get
// processed and merged with the sync transaction and released earlier than if they
// were sent to SF
- if (mWaitForTransactionCallback && mSyncTransaction == nullptr &&
- currFrameNumber >= mLastAcquiredFrameNumber) {
- mWaitForTransactionCallback = false;
+ mSyncedFrameNumbers.erase(currFrameNumber);
+ if (mSyncedFrameNumbers.empty() && mSyncTransaction == nullptr) {
flushShadowQueue();
}
} else {
@@ -308,7 +307,6 @@ void BLASTBufferQueue::transactionCommittedCallback(nsecs_t /*latchTime*/,
BQA_LOGE("No matching SurfaceControls found: mSurfaceControlsWithPendingCallback was "
"empty.");
}
-
decStrong((void*)transactionCommittedCallbackThunk);
}
}
@@ -351,6 +349,20 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence
stat.latchTime,
stat.frameEventStats.dequeueReadyTime);
}
+ auto currFrameNumber = stat.frameEventStats.frameNumber;
+ std::vector<ReleaseCallbackId> staleReleases;
+ for (const auto& [key, value]: mSubmitted) {
+ if (currFrameNumber > key.framenumber) {
+ staleReleases.push_back(key);
+ }
+ }
+ for (const auto& staleRelease : staleReleases) {
+ BQA_LOGE("Faking releaseBufferCallback from transactionCompleteCallback");
+ BBQ_TRACE("FakeReleaseCallback");
+ releaseBufferCallbackLocked(staleRelease,
+ stat.previousReleaseFence ? stat.previousReleaseFence : Fence::NO_FENCE,
+ stat.currentMaxAcquiredBufferCount);
+ }
} else {
BQA_LOGE("Failed to find matching SurfaceControl in transactionCallback");
}
@@ -391,7 +403,14 @@ void BLASTBufferQueue::releaseBufferCallback(
const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
std::optional<uint32_t> currentMaxAcquiredBufferCount) {
BBQ_TRACE();
+
std::unique_lock _lock{mMutex};
+ releaseBufferCallbackLocked(id, releaseFence, currentMaxAcquiredBufferCount);
+}
+
+void BLASTBufferQueue::releaseBufferCallbackLocked(const ReleaseCallbackId& id,
+ const sp<Fence>& releaseFence, std::optional<uint32_t> currentMaxAcquiredBufferCount) {
+ ATRACE_CALL();
BQA_LOGV("releaseBufferCallback %s", id.to_string().c_str());
// Calculate how many buffers we need to hold before we release them back
@@ -409,16 +428,22 @@ void BLASTBufferQueue::releaseBufferCallback(
const auto numPendingBuffersToHold =
isEGL ? std::max(0u, mMaxAcquiredBuffers - mCurrentMaxAcquiredBufferCount) : 0;
- mPendingRelease.emplace_back(ReleasedBuffer{id, releaseFence});
+
+ auto rb = ReleasedBuffer{id, releaseFence};
+ if (std::find(mPendingRelease.begin(), mPendingRelease.end(), rb) == mPendingRelease.end()) {
+ mPendingRelease.emplace_back(rb);
+ }
// Release all buffers that are beyond the ones that we need to hold
while (mPendingRelease.size() > numPendingBuffersToHold) {
const auto releasedBuffer = mPendingRelease.front();
mPendingRelease.pop_front();
releaseBuffer(releasedBuffer.callbackId, releasedBuffer.releaseFence);
- // Don't process the transactions here if mWaitForTransactionCallback is set. Instead, let
- // onFrameAvailable handle processing them since it will merge with the syncTransaction.
- if (!mWaitForTransactionCallback) {
+ // Don't process the transactions here if mSyncedFrameNumbers is not empty. That means
+ // are still transactions that have sync buffers in them that have not been applied or
+ // dropped. Instead, let onFrameAvailable handle processing them since it will merge with
+ // the syncTransaction.
+ if (mSyncedFrameNumbers.empty()) {
acquireNextBufferLocked(std::nullopt);
}
}
@@ -442,6 +467,9 @@ void BLASTBufferQueue::releaseBuffer(const ReleaseCallbackId& callbackId,
BQA_LOGV("released %s", callbackId.to_string().c_str());
mBufferItemConsumer->releaseBuffer(it->second, releaseFence);
mSubmitted.erase(it);
+ // Remove the frame number from mSyncedFrameNumbers since we can get a release callback
+ // without getting a transaction committed if the buffer was dropped.
+ mSyncedFrameNumbers.erase(callbackId.framenumber);
}
void BLASTBufferQueue::acquireNextBufferLocked(
@@ -608,7 +636,7 @@ void BLASTBufferQueue::acquireAndReleaseBuffer() {
}
void BLASTBufferQueue::flushAndWaitForFreeBuffer(std::unique_lock<std::mutex>& lock) {
- if (mWaitForTransactionCallback && mNumFrameAvailable > 0) {
+ if (!mSyncedFrameNumbers.empty() && mNumFrameAvailable > 0) {
// We are waiting on a previous sync's transaction callback so allow another sync
// transaction to proceed.
//
@@ -635,6 +663,8 @@ void BLASTBufferQueue::flushAndWaitForFreeBuffer(std::unique_lock<std::mutex>& l
void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
std::function<void(SurfaceComposerClient::Transaction*)> prevCallback = nullptr;
SurfaceComposerClient::Transaction* prevTransaction = nullptr;
+ bool waitForTransactionCallback = !mSyncedFrameNumbers.empty();
+
{
BBQ_TRACE();
std::unique_lock _lock{mMutex};
@@ -666,7 +696,7 @@ void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
// add to shadow queue
mNumFrameAvailable++;
- if (mWaitForTransactionCallback && mNumFrameAvailable >= 2) {
+ if (waitForTransactionCallback && mNumFrameAvailable >= 2) {
acquireAndReleaseBuffer();
}
ATRACE_INT(mQueuedBufferTrace.c_str(),
@@ -683,14 +713,14 @@ void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
incStrong((void*)transactionCommittedCallbackThunk);
mSyncTransaction->addTransactionCommittedCallback(transactionCommittedCallbackThunk,
static_cast<void*>(this));
- mWaitForTransactionCallback = true;
+ mSyncedFrameNumbers.emplace(item.mFrameNumber);
if (mAcquireSingleBuffer) {
prevCallback = mTransactionReadyCallback;
prevTransaction = mSyncTransaction;
mTransactionReadyCallback = nullptr;
mSyncTransaction = nullptr;
}
- } else if (!mWaitForTransactionCallback) {
+ } else if (!waitForTransactionCallback) {
acquireNextBufferLocked(std::nullopt);
}
}
@@ -1097,9 +1127,9 @@ void BLASTBufferQueue::abandon() {
}
// Clear sync states
- if (mWaitForTransactionCallback) {
- BQA_LOGD("mWaitForTransactionCallback cleared");
- mWaitForTransactionCallback = false;
+ if (!mSyncedFrameNumbers.empty()) {
+ BQA_LOGD("mSyncedFrameNumbers cleared");
+ mSyncedFrameNumbers.clear();
}
if (mSyncTransaction != nullptr) {
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 47d801a78d..0f5192d41c 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -352,7 +352,8 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener
transactionStats.latchTime, surfaceStats.acquireTimeOrFence,
transactionStats.presentFence,
surfaceStats.previousReleaseFence, surfaceStats.transformHint,
- surfaceStats.eventStats);
+ surfaceStats.eventStats,
+ surfaceStats.currentMaxAcquiredBufferCount);
}
callbackFunction(transactionStats.latchTime, transactionStats.presentFence,
@@ -377,7 +378,8 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener
transactionStats.latchTime, surfaceStats.acquireTimeOrFence,
transactionStats.presentFence,
surfaceStats.previousReleaseFence, surfaceStats.transformHint,
- surfaceStats.eventStats);
+ surfaceStats.eventStats,
+ surfaceStats.currentMaxAcquiredBufferCount);
if (callbacksMap[callbackId].surfaceControls[surfaceStats.surfaceControl]) {
callbacksMap[callbackId]
.surfaceControls[surfaceStats.surfaceControl]
@@ -897,6 +899,10 @@ void SurfaceComposerClient::Transaction::clear() {
mApplyToken = nullptr;
}
+uint64_t SurfaceComposerClient::Transaction::getId() {
+ return mId;
+}
+
void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) {
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 9328a54184..f5898d20f1 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -95,9 +95,12 @@ public:
const std::vector<SurfaceControlStats>& stats);
void releaseBufferCallback(const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
std::optional<uint32_t> currentMaxAcquiredBufferCount);
+ void releaseBufferCallbackLocked(const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
+ std::optional<uint32_t> currentMaxAcquiredBufferCount);
void syncNextTransaction(std::function<void(SurfaceComposerClient::Transaction*)> callback,
bool acquireSingleBuffer = true);
void stopContinuousSyncTransaction();
+
void mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, uint64_t frameNumber);
void applyPendingTransactions(uint64_t frameNumber);
SurfaceComposerClient::Transaction* gatherPendingTransactions(uint64_t frameNumber);
@@ -177,6 +180,12 @@ private:
struct ReleasedBuffer {
ReleaseCallbackId callbackId;
sp<Fence> releaseFence;
+ bool operator==(const ReleasedBuffer& rhs) const {
+ // Only compare Id so if we somehow got two callbacks
+ // with different fences we don't decrement mNumAcquired
+ // too far.
+ return rhs.callbackId == callbackId;
+ }
};
std::deque<ReleasedBuffer> mPendingRelease GUARDED_BY(mMutex);
@@ -251,7 +260,6 @@ private:
std::queue<sp<SurfaceControl>> mSurfaceControlsWithPendingCallback GUARDED_BY(mMutex);
uint32_t mCurrentMaxAcquiredBufferCount;
- bool mWaitForTransactionCallback GUARDED_BY(mMutex) = false;
// Flag to determine if syncTransaction should only acquire a single buffer and then clear or
// continue to acquire buffers until explicitly cleared
@@ -279,6 +287,8 @@ private:
uint64_t mLastAppliedFrameNumber = 0;
std::function<void(bool)> mTransactionHangCallback;
+
+ std::unordered_set<uint64_t> mSyncedFrameNumbers GUARDED_BY(mMutex);
};
} // namespace android
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index efbdb36fef..9033e17c53 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -65,14 +65,16 @@ struct SurfaceControlStats {
SurfaceControlStats(const sp<SurfaceControl>& sc, nsecs_t latchTime,
std::variant<nsecs_t, sp<Fence>> acquireTimeOrFence,
const sp<Fence>& presentFence, const sp<Fence>& prevReleaseFence,
- uint32_t hint, FrameEventHistoryStats eventStats)
+ uint32_t hint, FrameEventHistoryStats eventStats,
+ uint32_t currentMaxAcquiredBufferCount)
: surfaceControl(sc),
latchTime(latchTime),
acquireTimeOrFence(std::move(acquireTimeOrFence)),
presentFence(presentFence),
previousReleaseFence(prevReleaseFence),
transformHint(hint),
- frameEventStats(eventStats) {}
+ frameEventStats(eventStats),
+ currentMaxAcquiredBufferCount(currentMaxAcquiredBufferCount) {}
sp<SurfaceControl> surfaceControl;
nsecs_t latchTime = -1;
@@ -81,6 +83,7 @@ struct SurfaceControlStats {
sp<Fence> previousReleaseFence;
uint32_t transformHint = 0;
FrameEventHistoryStats frameEventStats;
+ uint32_t currentMaxAcquiredBufferCount = 0;
};
using TransactionCompletedCallbackTakesContext =
@@ -461,6 +464,10 @@ public:
// Clears the contents of the transaction without applying it.
void clear();
+ // Returns the current id of the transaction.
+ // The id is updated every time the transaction is applied.
+ uint64_t getId();
+
status_t apply(bool synchronous = false, bool oneWay = false);
// Merge another transaction in to this one, clearing other
// as if it had been applied.
diff --git a/libs/gui/include/gui/test/CallbackUtils.h b/libs/gui/include/gui/test/CallbackUtils.h
index 62d1496ccd..08785b49c1 100644
--- a/libs/gui/include/gui/test/CallbackUtils.h
+++ b/libs/gui/include/gui/test/CallbackUtils.h
@@ -135,7 +135,8 @@ private:
void verifySurfaceControlStats(const SurfaceControlStats& surfaceControlStats,
nsecs_t latchTime) const {
const auto& [surfaceControl, latch, acquireTimeOrFence, presentFence,
- previousReleaseFence, transformHint, frameEvents] = surfaceControlStats;
+ previousReleaseFence, transformHint, frameEvents, ignore] =
+ surfaceControlStats;
ASSERT_TRUE(std::holds_alternative<nsecs_t>(acquireTimeOrFence));
ASSERT_EQ(std::get<nsecs_t>(acquireTimeOrFence) > 0,
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index cb7e94c932..b993289e6a 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -161,6 +161,10 @@ public:
ASSERT_EQ(numFramesSubmitted, mBlastBufferQueueAdapter->mSubmitted.size());
}
+ void mergeWithNextTransaction(Transaction* merge, uint64_t frameNumber) {
+ mBlastBufferQueueAdapter->mergeWithNextTransaction(merge, frameNumber);
+ }
+
private:
sp<TestBLASTBufferQueue> mBlastBufferQueueAdapter;
};
@@ -1111,6 +1115,39 @@ TEST_F(BLASTBufferQueueTest, SyncNextTransactionOverwrite) {
ASSERT_TRUE(receivedCallback);
}
+TEST_F(BLASTBufferQueueTest, SyncNextTransactionDropBuffer) {
+ uint8_t r = 255;
+ uint8_t g = 0;
+ uint8_t b = 0;
+
+ BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+
+ sp<IGraphicBufferProducer> igbProducer;
+ setUpProducer(adapter, igbProducer);
+
+ Transaction sync;
+ adapter.setSyncTransaction(sync);
+ queueBuffer(igbProducer, 0, 255, 0, 0);
+
+ // Merge a transaction that has a complete callback into the next frame so we can get notified
+ // when to take a screenshot
+ CallbackHelper transactionCallback;
+ Transaction t;
+ t.addTransactionCompletedCallback(transactionCallback.function,
+ transactionCallback.getContext());
+ adapter.mergeWithNextTransaction(&t, 2);
+ queueBuffer(igbProducer, r, g, b, 0);
+
+ // Drop the buffer, but ensure the next one continues to get processed.
+ sync.setBuffer(mSurfaceControl, nullptr);
+
+ CallbackData callbackData;
+ transactionCallback.getCallbackData(&callbackData);
+ ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+}
+
// This test will currently fail because the old surfacecontrol will steal the last presented buffer
// until the old surface control is destroyed. This is not necessarily a bug but to document a
// limitation with the update API and to test any changes to make the api more robust. The current
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index c6cdeb7706..2637f59b5e 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -1184,18 +1184,23 @@ public:
std::vector<sp<IGraphicBufferProducer>> mProducers;
};
-TEST_F(MultiDisplayTests, drop_input_if_layer_on_invalid_display) {
+TEST_F(MultiDisplayTests, drop_touch_if_layer_on_invalid_display) {
ui::LayerStack layerStack = ui::LayerStack::fromValue(42);
// Do not create a display associated with the LayerStack.
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
surface->doTransaction([&](auto &t, auto &sc) { t.setLayerStack(sc, layerStack); });
surface->showAt(100, 100);
+ // Touches should be dropped if the layer is on an invalid display.
injectTapOnDisplay(101, 101, layerStack.id);
+ EXPECT_EQ(surface->consumeEvent(100), nullptr);
+
+ // However, we still let the window be focused and receive keys.
surface->requestFocus(layerStack.id);
- injectKeyOnDisplay(AKEYCODE_V, layerStack.id);
+ surface->assertFocusChange(true);
- EXPECT_EQ(surface->consumeEvent(100), nullptr);
+ injectKeyOnDisplay(AKEYCODE_V, layerStack.id);
+ surface->expectKey(AKEYCODE_V);
}
TEST_F(MultiDisplayTests, virtual_display_receives_input) {
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 5d7874af77..1335e4dfd0 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -67,6 +67,7 @@ cc_library {
"libbase",
"liblog",
"libcutils",
+ "libvintf",
],
static_libs: [
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 13ca9ecd35..155cb040fb 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -64,9 +64,10 @@ float transformAngle(const ui::Transform& transform, float angleRadians) {
}
bool shouldDisregardTransformation(uint32_t source) {
- // Do not apply any transformations to axes from joysticks or touchpads.
+ // Do not apply any transformations to axes from joysticks, touchpads, or relative mice.
return isFromSource(source, AINPUT_SOURCE_CLASS_JOYSTICK) ||
- isFromSource(source, AINPUT_SOURCE_CLASS_POSITION);
+ isFromSource(source, AINPUT_SOURCE_CLASS_POSITION) ||
+ isFromSource(source, AINPUT_SOURCE_MOUSE_RELATIVE);
}
bool shouldDisregardOffset(uint32_t source) {
@@ -89,6 +90,25 @@ const char* motionClassificationToString(MotionClassification classification) {
}
}
+const char* motionToolTypeToString(int32_t toolType) {
+ switch (toolType) {
+ case AMOTION_EVENT_TOOL_TYPE_UNKNOWN:
+ return "UNKNOWN";
+ case AMOTION_EVENT_TOOL_TYPE_FINGER:
+ return "FINGER";
+ case AMOTION_EVENT_TOOL_TYPE_STYLUS:
+ return "STYLUS";
+ case AMOTION_EVENT_TOOL_TYPE_MOUSE:
+ return "MOUSE";
+ case AMOTION_EVENT_TOOL_TYPE_ERASER:
+ return "ERASER";
+ case AMOTION_EVENT_TOOL_TYPE_PALM:
+ return "PALM";
+ default:
+ return "INVALID";
+ }
+}
+
// --- IdGenerator ---
IdGenerator::IdGenerator(Source source) : mSource(source) {}
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index 0bee1b6f2a..a9089690b0 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -53,33 +53,39 @@ static void appendInputDeviceConfigurationFileRelativePath(std::string& path,
}
std::string getInputDeviceConfigurationFilePathByDeviceIdentifier(
- const InputDeviceIdentifier& deviceIdentifier,
- InputDeviceConfigurationFileType type) {
+ const InputDeviceIdentifier& deviceIdentifier, InputDeviceConfigurationFileType type,
+ const char* suffix) {
if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) {
if (deviceIdentifier.version != 0) {
// Try vendor product version.
- std::string versionPath = getInputDeviceConfigurationFilePathByName(
- StringPrintf("Vendor_%04x_Product_%04x_Version_%04x",
- deviceIdentifier.vendor, deviceIdentifier.product,
- deviceIdentifier.version),
- type);
+ std::string versionPath =
+ getInputDeviceConfigurationFilePathByName(StringPrintf("Vendor_%04x_Product_%"
+ "04x_Version_%04x%s",
+ deviceIdentifier.vendor,
+ deviceIdentifier.product,
+ deviceIdentifier.version,
+ suffix),
+ type);
if (!versionPath.empty()) {
return versionPath;
}
}
// Try vendor product.
- std::string productPath = getInputDeviceConfigurationFilePathByName(
- StringPrintf("Vendor_%04x_Product_%04x",
- deviceIdentifier.vendor, deviceIdentifier.product),
- type);
+ std::string productPath =
+ getInputDeviceConfigurationFilePathByName(StringPrintf("Vendor_%04x_Product_%04x%s",
+ deviceIdentifier.vendor,
+ deviceIdentifier.product,
+ suffix),
+ type);
if (!productPath.empty()) {
return productPath;
}
}
// Try device name.
- return getInputDeviceConfigurationFilePathByName(deviceIdentifier.getCanonicalName(), type);
+ return getInputDeviceConfigurationFilePathByName(deviceIdentifier.getCanonicalName() + suffix,
+ type);
}
std::string getInputDeviceConfigurationFilePathByName(
diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp
index 7c25cda9ac..170e748ca6 100644
--- a/libs/input/KeyLayoutMap.cpp
+++ b/libs/input/KeyLayoutMap.cpp
@@ -21,24 +21,33 @@
#include <input/InputEventLabels.h>
#include <input/KeyLayoutMap.h>
#include <input/Keyboard.h>
+#include <log/log.h>
#include <utils/Errors.h>
-#include <utils/Log.h>
#include <utils/Timers.h>
#include <utils/Tokenizer.h>
+#include <vintf/RuntimeInfo.h>
+#include <vintf/VintfObject.h>
#include <cstdlib>
#include <string_view>
#include <unordered_map>
-// Enables debug output for the parser.
-#define DEBUG_PARSER 0
+/**
+ * Log debug output for the parser.
+ * Enable this via "adb shell setprop log.tag.KeyLayoutMapParser DEBUG" (requires restart)
+ */
+const bool DEBUG_PARSER =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Parser", ANDROID_LOG_INFO);
// Enables debug output for parser performance.
#define DEBUG_PARSER_PERFORMANCE 0
-// Enables debug output for mapping.
-#define DEBUG_MAPPING 0
-
+/**
+ * Log debug output for mapping.
+ * Enable this via "adb shell setprop log.tag.KeyLayoutMapMapping DEBUG" (requires restart)
+ */
+const bool DEBUG_MAPPING =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Mapping", ANDROID_LOG_INFO);
namespace android {
namespace {
@@ -69,6 +78,29 @@ static const std::unordered_map<std::string_view, InputDeviceSensorType> SENSOR_
sensorPair<InputDeviceSensorType::GYROSCOPE_UNCALIBRATED>(),
sensorPair<InputDeviceSensorType::SIGNIFICANT_MOTION>()};
+bool kernelConfigsArePresent(const std::set<std::string>& configs) {
+ std::shared_ptr<const android::vintf::RuntimeInfo> runtimeInfo =
+ android::vintf::VintfObject::GetInstance()->getRuntimeInfo(
+ vintf::RuntimeInfo::FetchFlag::CONFIG_GZ);
+ LOG_ALWAYS_FATAL_IF(runtimeInfo == nullptr, "Kernel configs could not be fetched");
+
+ const std::map<std::string, std::string>& kernelConfigs = runtimeInfo->kernelConfigs();
+ for (const std::string& requiredConfig : configs) {
+ const auto configIt = kernelConfigs.find(requiredConfig);
+ if (configIt == kernelConfigs.end()) {
+ ALOGI("Required kernel config %s is not found", requiredConfig.c_str());
+ return false;
+ }
+ const std::string& option = configIt->second;
+ if (option != "y" && option != "m") {
+ ALOGI("Required kernel config %s has option %s", requiredConfig.c_str(),
+ option.c_str());
+ return false;
+ }
+ }
+ return true;
+}
+
} // namespace
KeyLayoutMap::KeyLayoutMap() = default;
@@ -76,32 +108,34 @@ KeyLayoutMap::~KeyLayoutMap() = default;
base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::loadContents(const std::string& filename,
const char* contents) {
- Tokenizer* tokenizer;
- status_t status = Tokenizer::fromContents(String8(filename.c_str()), contents, &tokenizer);
- if (status) {
- ALOGE("Error %d opening key layout map.", status);
- return Errorf("Error {} opening key layout map file {}.", status, filename.c_str());
- }
- std::unique_ptr<Tokenizer> t(tokenizer);
- auto ret = load(t.get());
- if (ret.ok()) {
- (*ret)->mLoadFileName = filename;
- }
- return ret;
+ return load(filename, contents);
}
-base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::load(const std::string& filename) {
+base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::load(const std::string& filename,
+ const char* contents) {
Tokenizer* tokenizer;
- status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer);
+ status_t status;
+ if (contents == nullptr) {
+ status = Tokenizer::open(String8(filename.c_str()), &tokenizer);
+ } else {
+ status = Tokenizer::fromContents(String8(filename.c_str()), contents, &tokenizer);
+ }
if (status) {
ALOGE("Error %d opening key layout map file %s.", status, filename.c_str());
return Errorf("Error {} opening key layout map file {}.", status, filename.c_str());
}
std::unique_ptr<Tokenizer> t(tokenizer);
auto ret = load(t.get());
- if (ret.ok()) {
- (*ret)->mLoadFileName = filename;
+ if (!ret.ok()) {
+ return ret;
+ }
+ const std::shared_ptr<KeyLayoutMap>& map = *ret;
+ LOG_ALWAYS_FATAL_IF(map == nullptr, "Returned map should not be null if there's no error");
+ if (!kernelConfigsArePresent(map->mRequiredKernelConfigs)) {
+ ALOGI("Not loading %s because the required kernel configs are not set", filename.c_str());
+ return Errorf("Missing kernel config");
}
+ map->mLoadFileName = filename;
return ret;
}
@@ -134,9 +168,8 @@ status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode,
int32_t* outKeyCode, uint32_t* outFlags) const {
const Key* key = getKey(scanCode, usageCode);
if (!key) {
-#if DEBUG_MAPPING
- ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Failed.", scanCode, usageCode);
-#endif
+ ALOGD_IF(DEBUG_MAPPING, "mapKey: scanCode=%d, usageCode=0x%08x ~ Failed.", scanCode,
+ usageCode);
*outKeyCode = AKEYCODE_UNKNOWN;
*outFlags = 0;
return NAME_NOT_FOUND;
@@ -145,10 +178,9 @@ status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode,
*outKeyCode = key->keyCode;
*outFlags = key->flags;
-#if DEBUG_MAPPING
- ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d, outFlags=0x%08x.",
- scanCode, usageCode, *outKeyCode, *outFlags);
-#endif
+ ALOGD_IF(DEBUG_MAPPING,
+ "mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d, outFlags=0x%08x.",
+ scanCode, usageCode, *outKeyCode, *outFlags);
return NO_ERROR;
}
@@ -156,17 +188,12 @@ status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode,
base::Result<std::pair<InputDeviceSensorType, int32_t>> KeyLayoutMap::mapSensor(int32_t absCode) {
auto it = mSensorsByAbsCode.find(absCode);
if (it == mSensorsByAbsCode.end()) {
-#if DEBUG_MAPPING
- ALOGD("mapSensor: absCode=%d, ~ Failed.", absCode);
-#endif
+ ALOGD_IF(DEBUG_MAPPING, "mapSensor: absCode=%d, ~ Failed.", absCode);
return Errorf("Can't find abs code {}.", absCode);
}
const Sensor& sensor = it->second;
-
-#if DEBUG_MAPPING
- ALOGD("mapSensor: absCode=%d, sensorType=%s, sensorDataIndex=0x%x.", absCode,
- ftl::enum_string(sensor.sensorType).c_str(), sensor.sensorDataIndex);
-#endif
+ ALOGD_IF(DEBUG_MAPPING, "mapSensor: absCode=%d, sensorType=%s, sensorDataIndex=0x%x.", absCode,
+ ftl::enum_string(sensor.sensorType).c_str(), sensor.sensorDataIndex);
return std::make_pair(sensor.sensorType, sensor.sensorDataIndex);
}
@@ -200,21 +227,18 @@ status_t KeyLayoutMap::findScanCodesForKey(
status_t KeyLayoutMap::mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const {
ssize_t index = mAxes.indexOfKey(scanCode);
if (index < 0) {
-#if DEBUG_MAPPING
- ALOGD("mapAxis: scanCode=%d ~ Failed.", scanCode);
-#endif
+ ALOGD_IF(DEBUG_MAPPING, "mapAxis: scanCode=%d ~ Failed.", scanCode);
return NAME_NOT_FOUND;
}
*outAxisInfo = mAxes.valueAt(index);
-#if DEBUG_MAPPING
- ALOGD("mapAxis: scanCode=%d ~ Result mode=%d, axis=%d, highAxis=%d, "
- "splitValue=%d, flatOverride=%d.",
- scanCode,
- outAxisInfo->mode, outAxisInfo->axis, outAxisInfo->highAxis,
- outAxisInfo->splitValue, outAxisInfo->flatOverride);
-#endif
+ ALOGD_IF(DEBUG_MAPPING,
+ "mapAxis: scanCode=%d ~ Result mode=%d, axis=%d, highAxis=%d, "
+ "splitValue=%d, flatOverride=%d.",
+ scanCode, outAxisInfo->mode, outAxisInfo->axis, outAxisInfo->highAxis,
+ outAxisInfo->splitValue, outAxisInfo->flatOverride);
+
return NO_ERROR;
}
@@ -223,15 +247,12 @@ status_t KeyLayoutMap::findScanCodeForLed(int32_t ledCode, int32_t* outScanCode)
for (size_t i = 0; i < N; i++) {
if (mLedsByScanCode.valueAt(i).ledCode == ledCode) {
*outScanCode = mLedsByScanCode.keyAt(i);
-#if DEBUG_MAPPING
- ALOGD("findScanCodeForLed: ledCode=%d, scanCode=%d.", ledCode, *outScanCode);
-#endif
+ ALOGD_IF(DEBUG_MAPPING, "findScanCodeForLed: ledCode=%d, scanCode=%d.", ledCode,
+ *outScanCode);
return NO_ERROR;
}
}
-#if DEBUG_MAPPING
- ALOGD("findScanCodeForLed: ledCode=%d ~ Not found.", ledCode);
-#endif
+ ALOGD_IF(DEBUG_MAPPING, "findScanCodeForLed: ledCode=%d ~ Not found.", ledCode);
return NAME_NOT_FOUND;
}
@@ -240,15 +261,12 @@ status_t KeyLayoutMap::findUsageCodeForLed(int32_t ledCode, int32_t* outUsageCod
for (size_t i = 0; i < N; i++) {
if (mLedsByUsageCode.valueAt(i).ledCode == ledCode) {
*outUsageCode = mLedsByUsageCode.keyAt(i);
-#if DEBUG_MAPPING
- ALOGD("findUsageForLed: ledCode=%d, usage=%x.", ledCode, *outUsageCode);
-#endif
+ ALOGD_IF(DEBUG_MAPPING, "%s: ledCode=%d, usage=%x.", __func__, ledCode, *outUsageCode);
return NO_ERROR;
}
}
-#if DEBUG_MAPPING
- ALOGD("findUsageForLed: ledCode=%d ~ Not found.", ledCode);
-#endif
+ ALOGD_IF(DEBUG_MAPPING, "%s: ledCode=%d ~ Not found.", __func__, ledCode);
+
return NAME_NOT_FOUND;
}
@@ -264,10 +282,8 @@ KeyLayoutMap::Parser::~Parser() {
status_t KeyLayoutMap::Parser::parse() {
while (!mTokenizer->isEof()) {
-#if DEBUG_PARSER
- ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),
- mTokenizer->peekRemainderOfLine().string());
-#endif
+ ALOGD_IF(DEBUG_PARSER, "Parsing %s: '%s'.", mTokenizer->getLocation().string(),
+ mTokenizer->peekRemainderOfLine().string());
mTokenizer->skipDelimiters(WHITESPACE);
@@ -289,6 +305,10 @@ status_t KeyLayoutMap::Parser::parse() {
mTokenizer->skipDelimiters(WHITESPACE);
status_t status = parseSensor();
if (status) return status;
+ } else if (keywordToken == "requires_kernel_config") {
+ mTokenizer->skipDelimiters(WHITESPACE);
+ status_t status = parseRequiredKernelConfig();
+ if (status) return status;
} else {
ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(),
keywordToken.string());
@@ -361,10 +381,9 @@ status_t KeyLayoutMap::Parser::parseKey() {
flags |= flag;
}
-#if DEBUG_PARSER
- ALOGD("Parsed key %s: code=%d, keyCode=%d, flags=0x%08x.",
- mapUsage ? "usage" : "scan code", code, keyCode, flags);
-#endif
+ ALOGD_IF(DEBUG_PARSER, "Parsed key %s: code=%d, keyCode=%d, flags=0x%08x.",
+ mapUsage ? "usage" : "scan code", code, keyCode, flags);
+
Key key;
key.keyCode = keyCode;
key.flags = flags;
@@ -462,13 +481,12 @@ status_t KeyLayoutMap::Parser::parseAxis() {
}
}
-#if DEBUG_PARSER
- ALOGD("Parsed axis: scanCode=%d, mode=%d, axis=%d, highAxis=%d, "
- "splitValue=%d, flatOverride=%d.",
- scanCode,
- axisInfo.mode, axisInfo.axis, axisInfo.highAxis,
- axisInfo.splitValue, axisInfo.flatOverride);
-#endif
+ ALOGD_IF(DEBUG_PARSER,
+ "Parsed axis: scanCode=%d, mode=%d, axis=%d, highAxis=%d, "
+ "splitValue=%d, flatOverride=%d.",
+ scanCode, axisInfo.mode, axisInfo.axis, axisInfo.highAxis, axisInfo.splitValue,
+ axisInfo.flatOverride);
+
mMap->mAxes.add(scanCode, axisInfo);
return NO_ERROR;
}
@@ -505,10 +523,8 @@ status_t KeyLayoutMap::Parser::parseLed() {
return BAD_VALUE;
}
-#if DEBUG_PARSER
- ALOGD("Parsed led %s: code=%d, ledCode=%d.",
- mapUsage ? "usage" : "scan code", code, ledCode);
-#endif
+ ALOGD_IF(DEBUG_PARSER, "Parsed led %s: code=%d, ledCode=%d.", mapUsage ? "usage" : "scan code",
+ code, ledCode);
Led led;
led.ledCode = ledCode;
@@ -584,10 +600,8 @@ status_t KeyLayoutMap::Parser::parseSensor() {
}
int32_t sensorDataIndex = indexOpt.value();
-#if DEBUG_PARSER
- ALOGD("Parsed sensor: abs code=%d, sensorType=%s, sensorDataIndex=%d.", code,
- ftl::enum_string(sensorType).c_str(), sensorDataIndex);
-#endif
+ ALOGD_IF(DEBUG_PARSER, "Parsed sensor: abs code=%d, sensorType=%s, sensorDataIndex=%d.", code,
+ ftl::enum_string(sensorType).c_str(), sensorDataIndex);
Sensor sensor;
sensor.sensorType = sensorType;
@@ -596,4 +610,23 @@ status_t KeyLayoutMap::Parser::parseSensor() {
return NO_ERROR;
}
+// Parse the name of a required kernel config.
+// The layout won't be used if the specified kernel config is not present
+// Examples:
+// requires_kernel_config CONFIG_HID_PLAYSTATION
+status_t KeyLayoutMap::Parser::parseRequiredKernelConfig() {
+ String8 codeToken = mTokenizer->nextToken(WHITESPACE);
+ std::string configName = codeToken.string();
+
+ const auto result = mMap->mRequiredKernelConfigs.emplace(configName);
+ if (!result.second) {
+ ALOGE("%s: Duplicate entry for required kernel config %s.",
+ mTokenizer->getLocation().string(), configName.c_str());
+ return BAD_VALUE;
+ }
+
+ ALOGD_IF(DEBUG_PARSER, "Parsed required kernel config: name=%s", configName.c_str());
+ return NO_ERROR;
+}
+
} // namespace android
diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp
index f0895b32ef..c3f5151fd1 100644
--- a/libs/input/Keyboard.cpp
+++ b/libs/input/Keyboard.cpp
@@ -20,16 +20,23 @@
#include <unistd.h>
#include <limits.h>
-#include <input/Keyboard.h>
+#include <input/InputDevice.h>
#include <input/InputEventLabels.h>
-#include <input/KeyLayoutMap.h>
#include <input/KeyCharacterMap.h>
-#include <input/InputDevice.h>
+#include <input/KeyLayoutMap.h>
+#include <input/Keyboard.h>
+#include <log/log.h>
#include <utils/Errors.h>
-#include <utils/Log.h>
namespace android {
+static std::string getPath(const InputDeviceIdentifier& deviceIdentifier, const std::string& name,
+ InputDeviceConfigurationFileType type) {
+ return name.empty()
+ ? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type)
+ : getInputDeviceConfigurationFilePathByName(name, type);
+}
+
// --- KeyMap ---
KeyMap::KeyMap() {
@@ -111,11 +118,25 @@ status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier,
}
base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(path);
+ if (ret.ok()) {
+ keyLayoutMap = *ret;
+ keyLayoutFile = path;
+ return OK;
+ }
+
+ // Try to load fallback layout if the regular layout could not be loaded due to missing
+ // kernel modules
+ std::string fallbackPath(
+ getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier,
+ InputDeviceConfigurationFileType::
+ KEY_LAYOUT,
+ "_fallback"));
+ ret = KeyLayoutMap::load(fallbackPath);
if (!ret.ok()) {
return ret.error().code();
}
keyLayoutMap = *ret;
- keyLayoutFile = path;
+ keyLayoutFile = fallbackPath;
return OK;
}
@@ -137,14 +158,6 @@ status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifi
return OK;
}
-std::string KeyMap::getPath(const InputDeviceIdentifier& deviceIdentifier,
- const std::string& name, InputDeviceConfigurationFileType type) {
- return name.empty()
- ? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type)
- : getInputDeviceConfigurationFilePathByName(name, type);
-}
-
-
// --- Global functions ---
bool isKeyboardSpecialFunction(const PropertyMap* config) {
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 6ffe8518b6..d947cd99e8 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -36,8 +36,9 @@ cc_test {
"liblog",
"libui",
"libutils",
+ "libvintf",
],
- data: ["data/*.kcm"],
+ data: ["data/*"],
test_suites: ["device-tests"],
}
diff --git a/libs/input/tests/InputDevice_test.cpp b/libs/input/tests/InputDevice_test.cpp
index 61e88df11d..e872fa442b 100644
--- a/libs/input/tests/InputDevice_test.cpp
+++ b/libs/input/tests/InputDevice_test.cpp
@@ -64,13 +64,11 @@ protected:
mKeyMap.keyCharacterMapFile = path;
}
- virtual void SetUp() override {
+ void SetUp() override {
loadKeyLayout("Generic");
loadKeyCharacterMap("Generic");
}
- virtual void TearDown() override {}
-
KeyMap mKeyMap;
};
@@ -132,4 +130,20 @@ TEST_F(InputDeviceKeyMapTest, keyCharacteMapApplyMultipleOverlaysTest) {
ASSERT_EQ(*mKeyMap.keyCharacterMap, *frenchOverlaidKeyCharacterMap);
}
+TEST(InputDeviceKeyLayoutTest, DoesNotLoadWhenRequiredKernelConfigIsMissing) {
+ std::string klPath = base::GetExecutableDirectory() + "/data/kl_with_required_fake_config.kl";
+ base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(klPath);
+ ASSERT_FALSE(ret.ok()) << "Should not be able to load KeyLayout at " << klPath;
+ // We assert error message here because it's used by 'validatekeymaps' tool
+ ASSERT_EQ("Missing kernel config", ret.error().message());
+}
+
+TEST(InputDeviceKeyLayoutTest, LoadsWhenRequiredKernelConfigIsPresent) {
+ std::string klPath = base::GetExecutableDirectory() + "/data/kl_with_required_real_config.kl";
+ base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(klPath);
+ ASSERT_TRUE(ret.ok()) << "Cannot load KeyLayout at " << klPath;
+ const std::shared_ptr<KeyLayoutMap>& map = *ret;
+ ASSERT_NE(nullptr, map) << "Map should be valid because CONFIG_UHID should always be present";
+}
+
} // namespace android
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index a92016ba3b..4b3124636b 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -715,10 +715,10 @@ TEST_F(MotionEventTest, ApplyTransform) {
}
TEST_F(MotionEventTest, JoystickAndTouchpadAreNotTransformed) {
- constexpr static std::array kNonTransformedSources = {std::pair(AINPUT_SOURCE_TOUCHPAD,
- AMOTION_EVENT_ACTION_DOWN),
- std::pair(AINPUT_SOURCE_JOYSTICK,
- AMOTION_EVENT_ACTION_MOVE)};
+ constexpr static std::array kNonTransformedSources =
+ {std::pair(AINPUT_SOURCE_TOUCHPAD, AMOTION_EVENT_ACTION_DOWN),
+ std::pair(AINPUT_SOURCE_JOYSTICK, AMOTION_EVENT_ACTION_MOVE),
+ std::pair(AINPUT_SOURCE_MOUSE_RELATIVE, AMOTION_EVENT_ACTION_MOVE)};
// Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
ui::Transform transform(ui::Transform::ROT_90, 800, 400);
transform.set(transform.tx() + 20, transform.ty() + 40);
@@ -738,7 +738,7 @@ TEST_F(MotionEventTest, JoystickAndTouchpadAreNotTransformed) {
TEST_F(MotionEventTest, NonPointerSourcesAreNotTranslated) {
constexpr static std::array kNonPointerSources = {std::pair(AINPUT_SOURCE_TRACKBALL,
AMOTION_EVENT_ACTION_DOWN),
- std::pair(AINPUT_SOURCE_MOUSE_RELATIVE,
+ std::pair(AINPUT_SOURCE_TOUCH_NAVIGATION,
AMOTION_EVENT_ACTION_MOVE)};
// Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
ui::Transform transform(ui::Transform::ROT_90, 800, 400);
diff --git a/libs/input/tests/data/kl_with_required_fake_config.kl b/libs/input/tests/data/kl_with_required_fake_config.kl
new file mode 100644
index 0000000000..2d0a507fbd
--- /dev/null
+++ b/libs/input/tests/data/kl_with_required_fake_config.kl
@@ -0,0 +1,20 @@
+# Copyright (C) 2022 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.
+
+# This KL should not be loaded unless the below config is present in the kernel
+# This config will never exist, and therefore this KL should never be loaded
+requires_kernel_config CONFIG_HID_FAKEMODULE
+
+# An arbitrary mapping taken from another file
+key 0x130 BUTTON_X \ No newline at end of file
diff --git a/libs/input/tests/data/kl_with_required_real_config.kl b/libs/input/tests/data/kl_with_required_real_config.kl
new file mode 100644
index 0000000000..303b23e48a
--- /dev/null
+++ b/libs/input/tests/data/kl_with_required_real_config.kl
@@ -0,0 +1,21 @@
+# Copyright (C) 2022 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.
+
+# This KL should not be loaded unless the below config is present in the kernel
+# The CONFIG_UHID option has been required for a while, and therefore it's safe
+# to assume that this will always be loaded
+requires_kernel_config CONFIG_UHID
+
+# An arbitrary mapping taken from another file
+key 0x130 BUTTON_X \ No newline at end of file
diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp
index 2a3924b5f2..dce327ed18 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -196,9 +196,20 @@ std::string NotifyMotionArgs::dump() const {
}
coords += StringPrintf("{%" PRIu32 ": ", i);
coords +=
- StringPrintf("id=%" PRIu32 " x=%.1f y=%.1f, pressure=%.1f", pointerProperties[i].id,
+ StringPrintf("id=%" PRIu32 " x=%.1f y=%.1f pressure=%.1f", pointerProperties[i].id,
pointerCoords[i].getX(), pointerCoords[i].getY(),
pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
+ const int32_t toolType = pointerProperties[i].toolType;
+ if (toolType != AMOTION_EVENT_TOOL_TYPE_FINGER) {
+ coords += StringPrintf(" toolType=%s", motionToolTypeToString(toolType));
+ }
+ const float major = pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR);
+ const float minor = pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR);
+ const float orientation = pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
+ if (major != 0 || minor != 0) {
+ coords += StringPrintf(" major=%.1f minor=%.1f orientation=%.1f", major, minor,
+ orientation);
+ }
coords += "}";
}
return StringPrintf("NotifyMotionArgs(id=%" PRId32 ", eventTime=%" PRId64 ", deviceId=%" PRId32
diff --git a/services/inputflinger/UnwantedInteractionBlocker.cpp b/services/inputflinger/UnwantedInteractionBlocker.cpp
index f57ff33d50..ec41025e9d 100644
--- a/services/inputflinger/UnwantedInteractionBlocker.cpp
+++ b/services/inputflinger/UnwantedInteractionBlocker.cpp
@@ -29,8 +29,40 @@
using android::base::StringPrintf;
+/**
+ * This type is declared here to ensure consistency between the instantiated type (used in the
+ * constructor via std::make_unique) and the cast-to type (used in PalmRejector::dump() with
+ * static_cast). Due to the lack of rtti support, dynamic_cast is not available, so this can't be
+ * checked at runtime to avoid undefined behaviour.
+ */
+using PalmFilterImplementation = ::ui::NeuralStylusPalmDetectionFilter;
+
namespace android {
+/**
+ * Log detailed debug messages about each inbound motion event notification to the blocker.
+ * Enable this via "adb shell setprop log.tag.UnwantedInteractionBlockerInboundMotion DEBUG"
+ * (requires restart)
+ */
+const bool DEBUG_INBOUND_MOTION =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "InboundMotion", ANDROID_LOG_INFO);
+
+/**
+ * Log detailed debug messages about each outbound motion event processed by the blocker.
+ * Enable this via "adb shell setprop log.tag.UnwantedInteractionBlockerOutboundMotion DEBUG"
+ * (requires restart)
+ */
+const bool DEBUG_OUTBOUND_MOTION =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "OutboundMotion", ANDROID_LOG_INFO);
+
+/**
+ * Log the data sent to the model and received back from the model.
+ * Enable this via "adb shell setprop log.tag.UnwantedInteractionBlockerModel DEBUG"
+ * (requires restart)
+ */
+const bool DEBUG_MODEL =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Model", ANDROID_LOG_INFO);
+
// Category (=namespace) name for the input settings that are applied at boot time
static const char* INPUT_NATIVE_BOOT = "input_native_boot";
/**
@@ -45,13 +77,11 @@ static std::string toLower(std::string s) {
}
static bool isFromTouchscreen(int32_t source) {
- return isFromSource(source, AINPUT_SOURCE_TOUCHSCREEN) &&
- !isFromSource(source, AINPUT_SOURCE_STYLUS);
+ return isFromSource(source, AINPUT_SOURCE_TOUCHSCREEN);
}
static ::base::TimeTicks toChromeTimestamp(nsecs_t eventTime) {
- return ::base::TimeTicks::UnixEpoch() +
- ::base::Milliseconds(static_cast<float>(ns2ms(eventTime)));
+ return ::base::TimeTicks::UnixEpoch() + ::base::TimeDelta::FromNanosecondsD(eventTime);
}
/**
@@ -61,39 +91,22 @@ static ::base::TimeTicks toChromeTimestamp(nsecs_t eventTime) {
static bool isPalmRejectionEnabled() {
std::string value = toLower(
server_configurable_flags::GetServerConfigurableFlag(INPUT_NATIVE_BOOT,
- PALM_REJECTION_ENABLED, "false"));
- if (value == "true" || value == "1") {
+ PALM_REJECTION_ENABLED, "0"));
+ if (value == "1") {
return true;
}
return false;
}
-static int getLinuxToolType(int32_t toolType) {
- switch (toolType) {
- case AMOTION_EVENT_TOOL_TYPE_FINGER:
- return MT_TOOL_FINGER;
- case AMOTION_EVENT_TOOL_TYPE_STYLUS:
- return MT_TOOL_PEN;
- case AMOTION_EVENT_TOOL_TYPE_PALM:
- return MT_TOOL_PALM;
+static int getLinuxToolCode(int toolType) {
+ if (toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS) {
+ return BTN_TOOL_PEN;
}
- ALOGW("Got tool type %" PRId32 ", converting to MT_TOOL_FINGER", toolType);
- return MT_TOOL_FINGER;
-}
-
-static std::string dumpDeviceInfo(const AndroidPalmFilterDeviceInfo& info) {
- std::string out;
- out += StringPrintf("max_x = %.2f\n", info.max_x);
- out += StringPrintf("max_y = %.2f\n", info.max_y);
- out += StringPrintf("x_res = %.2f\n", info.x_res);
- out += StringPrintf("y_res = %.2f\n", info.y_res);
- out += StringPrintf("major_radius_res = %.2f\n", info.major_radius_res);
- out += StringPrintf("minor_radius_res = %.2f\n", info.minor_radius_res);
- out += StringPrintf("minor_radius_supported = %s\n",
- info.minor_radius_supported ? "true" : "false");
- out += StringPrintf("touch_major_res = %" PRId32 "\n", info.touch_major_res);
- out += StringPrintf("touch_minor_res = %" PRId32 "\n", info.touch_minor_res);
- return out;
+ if (toolType == AMOTION_EVENT_TOOL_TYPE_FINGER) {
+ return BTN_TOOL_FINGER;
+ }
+ ALOGW("Got tool type %" PRId32 ", converting to BTN_TOOL_FINGER", toolType);
+ return BTN_TOOL_FINGER;
}
static int32_t getActionUpForPointerId(const NotifyMotionArgs& args, int32_t pointerId) {
@@ -128,32 +141,6 @@ static int32_t resolveActionForPointer(uint8_t pointerIndex, int32_t action) {
return AMOTION_EVENT_ACTION_MOVE;
}
-std::string toString(const ::ui::InProgressTouchEvdev& touch) {
- return StringPrintf("x=%.1f, y=%.1f, tracking_id=%i, slot=%zu,"
- " pressure=%.1f, major=%i, minor=%i, "
- "tool_type=%i, altered=%s, was_touching=%s, touching=%s",
- touch.x, touch.y, touch.tracking_id, touch.slot, touch.pressure,
- touch.major, touch.minor, touch.tool_type, toString(touch.altered),
- toString(touch.was_touching), toString(touch.touching));
-}
-
-/**
- * Remove the data for the provided pointers from the args. The pointers are identified by their
- * pointerId, not by the index inside the array.
- * Return the new NotifyMotionArgs struct that has the remaining pointers.
- * The only fields that may be different in the returned args from the provided args are:
- * - action
- * - pointerCount
- * - pointerProperties
- * - pointerCoords
- * Action might change because it contains a pointer index. If another pointer is removed, the
- * active pointer index would be shifted.
- * Do not call this function for events with POINTER_UP or POINTER_DOWN events when removed pointer
- * id is the acting pointer id.
- *
- * @param args the args from which the pointers should be removed
- * @param pointerIds the pointer ids of the pointers that should be removed
- */
NotifyMotionArgs removePointerIds(const NotifyMotionArgs& args,
const std::set<int32_t>& pointerIds) {
const uint8_t actionIndex = MotionEvent::getActionIndex(args.action);
@@ -199,6 +186,26 @@ NotifyMotionArgs removePointerIds(const NotifyMotionArgs& args,
return newArgs;
}
+/**
+ * Remove stylus pointers from the provided NotifyMotionArgs.
+ *
+ * Return NotifyMotionArgs where the stylus pointers have been removed.
+ * If this results in removal of the active pointer, then return nullopt.
+ */
+static std::optional<NotifyMotionArgs> removeStylusPointerIds(const NotifyMotionArgs& args) {
+ std::set<int32_t> stylusPointerIds;
+ for (uint32_t i = 0; i < args.pointerCount; i++) {
+ if (args.pointerProperties[i].toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS) {
+ stylusPointerIds.insert(args.pointerProperties[i].id);
+ }
+ }
+ NotifyMotionArgs withoutStylusPointers = removePointerIds(args, stylusPointerIds);
+ if (withoutStylusPointers.pointerCount == 0 || withoutStylusPointers.action == ACTION_UNKNOWN) {
+ return std::nullopt;
+ }
+ return withoutStylusPointers;
+}
+
std::optional<AndroidPalmFilterDeviceInfo> createPalmFilterDeviceInfo(
const InputDeviceInfo& deviceInfo) {
if (!isFromTouchscreen(deviceInfo.getSources())) {
@@ -326,6 +333,7 @@ void UnwantedInteractionBlocker::notifyKey(const NotifyKeyArgs* args) {
}
void UnwantedInteractionBlocker::notifyMotion(const NotifyMotionArgs* args) {
+ ALOGD_IF(DEBUG_INBOUND_MOTION, "%s: %s", __func__, args->dump().c_str());
{ // acquire lock
std::scoped_lock lock(mLock);
const std::vector<NotifyMotionArgs> processedArgs =
@@ -339,17 +347,22 @@ void UnwantedInteractionBlocker::notifyMotion(const NotifyMotionArgs* args) {
mQueuedListener.flush();
}
+void UnwantedInteractionBlocker::enqueueOutboundMotionLocked(const NotifyMotionArgs& args) {
+ ALOGD_IF(DEBUG_OUTBOUND_MOTION, "%s: %s", __func__, args.dump().c_str());
+ mQueuedListener.notifyMotion(&args);
+}
+
void UnwantedInteractionBlocker::notifyMotionLocked(const NotifyMotionArgs* args) {
auto it = mPalmRejectors.find(args->deviceId);
const bool sendToPalmRejector = it != mPalmRejectors.end() && isFromTouchscreen(args->source);
if (!sendToPalmRejector) {
- mQueuedListener.notifyMotion(args);
+ enqueueOutboundMotionLocked(*args);
return;
}
std::vector<NotifyMotionArgs> processedArgs = it->second.processMotion(*args);
for (const NotifyMotionArgs& loopArgs : processedArgs) {
- mQueuedListener.notifyMotion(&loopArgs);
+ enqueueOutboundMotionLocked(loopArgs);
}
}
@@ -428,9 +441,10 @@ void UnwantedInteractionBlocker::dump(std::string& dump) {
dump += "UnwantedInteractionBlocker:\n";
dump += " mPreferStylusOverTouchBlocker:\n";
dump += addLinePrefix(mPreferStylusOverTouchBlocker.dump(), " ");
- dump += StringPrintf(" mEnablePalmRejection: %s\n", toString(mEnablePalmRejection));
+ dump += StringPrintf(" mEnablePalmRejection: %s\n",
+ std::to_string(mEnablePalmRejection).c_str());
dump += StringPrintf(" isPalmRejectionEnabled (flag value): %s\n",
- toString(isPalmRejectionEnabled()));
+ std::to_string(isPalmRejectionEnabled()).c_str());
dump += mPalmRejectors.empty() ? " mPalmRejectors: None\n" : " mPalmRejectors:\n";
for (const auto& [deviceId, palmRejector] : mPalmRejectors) {
dump += StringPrintf(" deviceId = %" PRId32 ":\n", deviceId);
@@ -512,6 +526,15 @@ std::string SlotState::dump() const {
return out;
}
+class AndroidPalmRejectionModel : public ::ui::OneDeviceTrainNeuralStylusPalmDetectionFilterModel {
+public:
+ AndroidPalmRejectionModel()
+ : ::ui::OneDeviceTrainNeuralStylusPalmDetectionFilterModel(/*default version*/ "",
+ std::vector<float>()) {
+ config_.resample_period = ::ui::kResamplePeriod;
+ }
+};
+
PalmRejector::PalmRejector(const AndroidPalmFilterDeviceInfo& info,
std::unique_ptr<::ui::PalmDetectionFilter> filter)
: mSharedPalmState(std::make_unique<::ui::SharedPalmDetectionFilterState>()),
@@ -523,11 +546,9 @@ PalmRejector::PalmRejector(const AndroidPalmFilterDeviceInfo& info,
return;
}
std::unique_ptr<::ui::NeuralStylusPalmDetectionFilterModel> model =
- std::make_unique<::ui::OneDeviceTrainNeuralStylusPalmDetectionFilterModel>(
- std::vector<float>());
- mPalmDetectionFilter =
- std::make_unique<::ui::NeuralStylusPalmDetectionFilter>(mDeviceInfo, std::move(model),
- mSharedPalmState.get());
+ std::make_unique<AndroidPalmRejectionModel>();
+ mPalmDetectionFilter = std::make_unique<PalmFilterImplementation>(mDeviceInfo, std::move(model),
+ mSharedPalmState.get());
}
std::vector<::ui::InProgressTouchEvdev> getTouches(const NotifyMotionArgs& args,
@@ -541,7 +562,7 @@ std::vector<::ui::InProgressTouchEvdev> getTouches(const NotifyMotionArgs& args,
touches.emplace_back(::ui::InProgressTouchEvdev());
touches.back().major = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR);
touches.back().minor = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR);
- touches.back().tool_type = getLinuxToolType(args.pointerProperties[i].toolType);
+ // The field 'tool_type' is not used for palm rejection
// Whether there is new information for the touch.
touches.back().altered = true;
@@ -588,32 +609,16 @@ std::vector<::ui::InProgressTouchEvdev> getTouches(const NotifyMotionArgs& args,
// The fields 'radius_x' and 'radius_x' are not used for palm rejection
touches.back().pressure = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE);
- touches.back().tool_code = BTN_TOOL_FINGER;
+ touches.back().tool_code = getLinuxToolCode(args.pointerProperties[i].toolType);
// The field 'orientation' is not used for palm rejection
// The fields 'tilt_x' and 'tilt_y' are not used for palm rejection
- touches.back().reported_tool_type = ::ui::EventPointerType::kTouch;
+ // The field 'reported_tool_type' is not used for palm rejection
touches.back().stylus_button = false;
}
return touches;
}
-std::vector<NotifyMotionArgs> PalmRejector::processMotion(const NotifyMotionArgs& args) {
- if (mPalmDetectionFilter == nullptr) {
- return {args};
- }
- const bool skipThisEvent = args.action == AMOTION_EVENT_ACTION_HOVER_ENTER ||
- args.action == AMOTION_EVENT_ACTION_HOVER_MOVE ||
- args.action == AMOTION_EVENT_ACTION_HOVER_EXIT ||
- args.action == AMOTION_EVENT_ACTION_BUTTON_PRESS ||
- args.action == AMOTION_EVENT_ACTION_BUTTON_RELEASE ||
- args.action == AMOTION_EVENT_ACTION_SCROLL;
- if (skipThisEvent) {
- // Lets not process hover events, button events, or scroll for now.
- return {args};
- }
- if (args.action == AMOTION_EVENT_ACTION_DOWN) {
- mSuppressedPointerIds.clear();
- }
+std::set<int32_t> PalmRejector::detectPalmPointers(const NotifyMotionArgs& args) {
std::bitset<::ui::kNumTouchEvdevSlots> slotsToHold;
std::bitset<::ui::kNumTouchEvdevSlots> slotsToSuppress;
@@ -621,15 +626,26 @@ std::vector<NotifyMotionArgs> PalmRejector::processMotion(const NotifyMotionArgs
// the slots that have been removed due to the incoming event.
SlotState oldSlotState = mSlotState;
mSlotState.update(args);
+
std::vector<::ui::InProgressTouchEvdev> touches =
getTouches(args, mDeviceInfo, oldSlotState, mSlotState);
::base::TimeTicks chromeTimestamp = toChromeTimestamp(args.eventTime);
+ if (DEBUG_MODEL) {
+ std::stringstream touchesStream;
+ for (const ::ui::InProgressTouchEvdev& touch : touches) {
+ touchesStream << touch.tracking_id << " : " << touch << "\n";
+ }
+ ALOGD("Filter: touches = %s", touchesStream.str().c_str());
+ }
+
mPalmDetectionFilter->Filter(touches, chromeTimestamp, &slotsToHold, &slotsToSuppress);
+ ALOGD_IF(DEBUG_MODEL, "Response: slotsToHold = %s, slotsToSuppress = %s",
+ slotsToHold.to_string().c_str(), slotsToSuppress.to_string().c_str());
+
// Now that we know which slots should be suppressed, let's convert those to pointer id's.
- std::set<int32_t> oldSuppressedIds;
- std::swap(oldSuppressedIds, mSuppressedPointerIds);
+ std::set<int32_t> newSuppressedIds;
for (size_t i = 0; i < args.pointerCount; i++) {
const int32_t pointerId = args.pointerProperties[i].id;
std::optional<size_t> slot = oldSlotState.getSlotForPointerId(pointerId);
@@ -638,9 +654,41 @@ std::vector<NotifyMotionArgs> PalmRejector::processMotion(const NotifyMotionArgs
LOG_ALWAYS_FATAL_IF(!slot, "Could not find slot for pointer id %" PRId32, pointerId);
}
if (slotsToSuppress.test(*slot)) {
- mSuppressedPointerIds.insert(pointerId);
+ newSuppressedIds.insert(pointerId);
}
}
+ return newSuppressedIds;
+}
+
+std::vector<NotifyMotionArgs> PalmRejector::processMotion(const NotifyMotionArgs& args) {
+ if (mPalmDetectionFilter == nullptr) {
+ return {args};
+ }
+ const bool skipThisEvent = args.action == AMOTION_EVENT_ACTION_HOVER_ENTER ||
+ args.action == AMOTION_EVENT_ACTION_HOVER_MOVE ||
+ args.action == AMOTION_EVENT_ACTION_HOVER_EXIT ||
+ args.action == AMOTION_EVENT_ACTION_BUTTON_PRESS ||
+ args.action == AMOTION_EVENT_ACTION_BUTTON_RELEASE ||
+ args.action == AMOTION_EVENT_ACTION_SCROLL;
+ if (skipThisEvent) {
+ // Lets not process hover events, button events, or scroll for now.
+ return {args};
+ }
+ if (args.action == AMOTION_EVENT_ACTION_DOWN) {
+ mSuppressedPointerIds.clear();
+ }
+
+ std::set<int32_t> oldSuppressedIds;
+ std::swap(oldSuppressedIds, mSuppressedPointerIds);
+
+ std::optional<NotifyMotionArgs> touchOnlyArgs = removeStylusPointerIds(args);
+ if (touchOnlyArgs) {
+ mSuppressedPointerIds = detectPalmPointers(*touchOnlyArgs);
+ } else {
+ // This is a stylus-only event.
+ // We can skip this event and just keep the suppressed pointer ids the same as before.
+ mSuppressedPointerIds = oldSuppressedIds;
+ }
std::vector<NotifyMotionArgs> argsWithoutUnwantedPointers =
cancelSuppressedPointers(args, oldSuppressedIds, mSuppressedPointerIds);
@@ -648,29 +696,40 @@ std::vector<NotifyMotionArgs> PalmRejector::processMotion(const NotifyMotionArgs
LOG_ALWAYS_FATAL_IF(checkArgs.action == ACTION_UNKNOWN, "%s", checkArgs.dump().c_str());
}
- if (mSuppressedPointerIds != oldSuppressedIds) {
- if (argsWithoutUnwantedPointers.size() != 1 ||
- argsWithoutUnwantedPointers[0].pointerCount != args.pointerCount) {
- ALOGI("Palm detected, removing pointer ids %s from %s",
- dumpSet(mSuppressedPointerIds).c_str(), args.dump().c_str());
- }
+ // Only log if new pointers are getting rejected. That means mSuppressedPointerIds is not a
+ // subset of oldSuppressedIds.
+ if (!std::includes(oldSuppressedIds.begin(), oldSuppressedIds.end(),
+ mSuppressedPointerIds.begin(), mSuppressedPointerIds.end())) {
+ ALOGI("Palm detected, removing pointer ids %s after %" PRId64 "ms from %s",
+ dumpSet(mSuppressedPointerIds).c_str(), ns2ms(args.eventTime - args.downTime),
+ args.dump().c_str());
}
return argsWithoutUnwantedPointers;
}
-const AndroidPalmFilterDeviceInfo& PalmRejector::getPalmFilterDeviceInfo() {
+const AndroidPalmFilterDeviceInfo& PalmRejector::getPalmFilterDeviceInfo() const {
return mDeviceInfo;
}
std::string PalmRejector::dump() const {
std::string out;
out += "mDeviceInfo:\n";
- out += addLinePrefix(dumpDeviceInfo(mDeviceInfo), " ");
+ std::stringstream deviceInfo;
+ deviceInfo << mDeviceInfo << ", touch_major_res=" << mDeviceInfo.touch_major_res
+ << ", touch_minor_res=" << mDeviceInfo.touch_minor_res << "\n";
+ out += addLinePrefix(deviceInfo.str(), " ");
out += "mSlotState:\n";
out += addLinePrefix(mSlotState.dump(), " ");
out += "mSuppressedPointerIds: ";
out += dumpSet(mSuppressedPointerIds) + "\n";
+ std::stringstream state;
+ state << *mSharedPalmState;
+ out += "mSharedPalmState: " + state.str() + "\n";
+ std::stringstream filter;
+ filter << static_cast<const PalmFilterImplementation&>(*mPalmDetectionFilter);
+ out += "mPalmDetectionFilter:\n";
+ out += addLinePrefix(filter.str(), " ") + "\n";
return out;
}
diff --git a/services/inputflinger/UnwantedInteractionBlocker.h b/services/inputflinger/UnwantedInteractionBlocker.h
index a43376419f..5d0dde8e64 100644
--- a/services/inputflinger/UnwantedInteractionBlocker.h
+++ b/services/inputflinger/UnwantedInteractionBlocker.h
@@ -43,6 +43,25 @@ std::optional<AndroidPalmFilterDeviceInfo> createPalmFilterDeviceInfo(
static constexpr int32_t ACTION_UNKNOWN = -1;
+/**
+ * Remove the data for the provided pointers from the args. The pointers are identified by their
+ * pointerId, not by the index inside the array.
+ * Return the new NotifyMotionArgs struct that has the remaining pointers.
+ * The only fields that may be different in the returned args from the provided args are:
+ * - action
+ * - pointerCount
+ * - pointerProperties
+ * - pointerCoords
+ * Action might change because it contains a pointer index. If another pointer is removed, the
+ * active pointer index would be shifted.
+ *
+ * If the active pointer id is removed (for example, for events like
+ * POINTER_UP or POINTER_DOWN), then the action is set to ACTION_UNKNOWN. It is up to the caller
+ * to set the action appropriately after the call.
+ *
+ * @param args the args from which the pointers should be removed
+ * @param pointerIds the pointer ids of the pointers that should be removed
+ */
NotifyMotionArgs removePointerIds(const NotifyMotionArgs& args,
const std::set<int32_t>& pointerIds);
@@ -101,6 +120,9 @@ private:
std::map<int32_t /*deviceId*/, PalmRejector> mPalmRejectors GUARDED_BY(mLock);
// TODO(b/210159205): delete this when simultaneous stylus and touch is supported
void notifyMotionLocked(const NotifyMotionArgs* args) REQUIRES(mLock);
+
+ // Call this function for outbound events so that they can be logged when logging is enabled.
+ void enqueueOutboundMotionLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
};
class SlotState {
@@ -144,13 +166,21 @@ public:
std::vector<NotifyMotionArgs> processMotion(const NotifyMotionArgs& args);
// Get the device info of this device, for comparison purposes
- const AndroidPalmFilterDeviceInfo& getPalmFilterDeviceInfo();
+ const AndroidPalmFilterDeviceInfo& getPalmFilterDeviceInfo() const;
std::string dump() const;
private:
PalmRejector(const PalmRejector&) = delete;
PalmRejector& operator=(const PalmRejector&) = delete;
+ /**
+ * Update the slot state and send this event to the palm rejection model for palm detection.
+ * Return the pointer ids that should be suppressed.
+ *
+ * This function is not const because it has side-effects. It will update the slot state using
+ * the incoming args! Also, it will call Filter(..), which has side-effects.
+ */
+ std::set<int32_t> detectPalmPointers(const NotifyMotionArgs& args);
std::unique_ptr<::ui::SharedPalmDetectionFilterState> mSharedPalmState;
AndroidPalmFilterDeviceInfo mDeviceInfo;
std::unique_ptr<::ui::PalmDetectionFilter> mPalmDetectionFilter;
diff --git a/services/inputflinger/dispatcher/DebugConfig.h b/services/inputflinger/dispatcher/DebugConfig.h
index 4f8995f43d..9a2aea69ec 100644
--- a/services/inputflinger/dispatcher/DebugConfig.h
+++ b/services/inputflinger/dispatcher/DebugConfig.h
@@ -75,11 +75,8 @@ const bool DEBUG_TOUCH_MODE =
/**
* Log debug messages about touch occlusion
- * Enable this via "adb shell setprop log.tag.InputDispatcherTouchOcclusion DEBUG" (requires
- * restart)
*/
-const bool DEBUG_TOUCH_OCCLUSION =
- __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "TouchOcclusion", ANDROID_LOG_INFO);
+constexpr bool DEBUG_TOUCH_OCCLUSION = true;
/**
* Log debug messages about the app switch latency optimization.
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 5e9427ad87..d8120fcb5e 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -2195,10 +2195,7 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked(
// Update the temporary touch state.
BitSet32 pointerIds;
- if (isSplit) {
- uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
- pointerIds.markBit(pointerId);
- }
+ pointerIds.markBit(entry.pointerProperties[pointerIndex].id);
tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, pointerIds);
}
@@ -2275,9 +2272,7 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked(
}
BitSet32 pointerIds;
- if (isSplit) {
- pointerIds.markBit(entry.pointerProperties[0].id);
- }
+ pointerIds.markBit(entry.pointerProperties[0].id);
tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
}
}
@@ -2453,21 +2448,28 @@ Failed:
}
} else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
// One pointer went up.
- if (isSplit) {
- int32_t pointerIndex = getMotionEventActionPointerIndex(action);
- uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
-
- for (size_t i = 0; i < tempTouchState.windows.size();) {
- TouchedWindow& touchedWindow = tempTouchState.windows[i];
- if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) {
- touchedWindow.pointerIds.clearBit(pointerId);
- if (touchedWindow.pointerIds.isEmpty()) {
- tempTouchState.windows.erase(tempTouchState.windows.begin() + i);
- continue;
- }
- }
- i += 1;
+ int32_t pointerIndex = getMotionEventActionPointerIndex(action);
+ uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
+
+ for (size_t i = 0; i < tempTouchState.windows.size();) {
+ TouchedWindow& touchedWindow = tempTouchState.windows[i];
+ touchedWindow.pointerIds.clearBit(pointerId);
+ if (touchedWindow.pointerIds.isEmpty()) {
+ tempTouchState.windows.erase(tempTouchState.windows.begin() + i);
+ continue;
+ }
+ i += 1;
+ }
+ } else if (!isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN) {
+ // If no split, we suppose all touched windows should receive pointer down.
+ const int32_t pointerIndex = getMotionEventActionPointerIndex(action);
+ for (size_t i = 0; i < tempTouchState.windows.size(); i++) {
+ TouchedWindow& touchedWindow = tempTouchState.windows[i];
+ // Ignore drag window for it should just track one pointer.
+ if (mDragState && mDragState->dragWindow == touchedWindow.windowHandle) {
+ continue;
}
+ touchedWindow.pointerIds.markBit(entry.pointerProperties[pointerIndex].id);
}
}
@@ -4470,6 +4472,14 @@ void InputDispatcher::transformMotionEntryForInjectionLocked(
if (it == mDisplayInfos.end()) return;
const auto& transformToDisplay = it->second.transform.inverse() * injectedTransform;
+ if (entry.xCursorPosition != AMOTION_EVENT_INVALID_CURSOR_POSITION &&
+ entry.yCursorPosition != AMOTION_EVENT_INVALID_CURSOR_POSITION) {
+ const vec2 cursor =
+ MotionEvent::calculateTransformedXY(entry.source, transformToDisplay,
+ {entry.xCursorPosition, entry.yCursorPosition});
+ entry.xCursorPosition = cursor.x;
+ entry.yCursorPosition = cursor.y;
+ }
for (uint32_t i = 0; i < entry.pointerCount; i++) {
entry.pointerCoords[i] =
MotionEvent::calculateTransformedCoords(entry.source, transformToDisplay,
@@ -5079,14 +5089,13 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<
// Store the dragging window.
if (isDragDrop) {
- if (pointerIds.count() > 1) {
- ALOGW("The drag and drop cannot be started when there is more than 1 pointer on the"
- " window.");
+ if (pointerIds.count() != 1) {
+ ALOGW("The drag and drop cannot be started when there is no pointer or more than 1"
+ " pointer on the window.");
return false;
}
- // If the window didn't not support split or the source is mouse, the pointerIds count
- // would be 0, so we have to track the pointer 0.
- const int32_t id = pointerIds.count() == 0 ? 0 : pointerIds.firstMarkedBit();
+ // Track the pointer id for drag window and generate the drag state.
+ const int32_t id = pointerIds.firstMarkedBit();
mDragState = std::make_unique<DragState>(toWindowHandle, id);
}
@@ -6322,6 +6331,13 @@ void InputDispatcher::onWindowInfosChanged(const std::vector<WindowInfo>& window
{ // acquire lock
std::scoped_lock _l(mLock);
+
+ // Ensure that we have an entry created for all existing displays so that if a displayId has
+ // no windows, we can tell that the windows were removed from the display.
+ for (const auto& [displayId, _] : mWindowHandlesByDisplay) {
+ handlesPerDisplay[displayId];
+ }
+
mDisplayInfos.clear();
for (const auto& displayInfo : displayInfos) {
mDisplayInfos.emplace(displayInfo.displayId, displayInfo);
diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp
index d39113b286..2df97d9a12 100644
--- a/services/inputflinger/dispatcher/InputTarget.cpp
+++ b/services/inputflinger/dispatcher/InputTarget.cpp
@@ -43,8 +43,8 @@ std::string dispatchModeToString(int32_t dispatchMode) {
}
void InputTarget::addPointers(BitSet32 newPointerIds, const ui::Transform& transform) {
- // The pointerIds can be empty, but still a valid InputTarget. This can happen for Monitors
- // and non splittable windows since we will just use all the pointers from the input event.
+ // The pointerIds can be empty, but still a valid InputTarget. This can happen when there is no
+ // valid pointer property from the input event.
if (newPointerIds.isEmpty()) {
setDefaultPointerTransform(transform);
return;
diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h
index 4c31ec3acd..6783022ca1 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.h
+++ b/services/inputflinger/dispatcher/TouchedWindow.h
@@ -29,7 +29,7 @@ namespace inputdispatcher {
struct TouchedWindow {
sp<gui::WindowInfoHandle> windowHandle;
int32_t targetFlags;
- BitSet32 pointerIds; // zero unless target flag FLAG_SPLIT is set
+ BitSet32 pointerIds;
};
} // namespace inputdispatcher
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index ba5083bec3..989700f6cf 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -110,7 +110,8 @@ void InputDevice::dump(std::string& dump, const std::string& eventHubDevStr) {
dump += "<none>\n";
}
dump += StringPrintf(INDENT2 "HasMic: %s\n", toString(mHasMic));
- dump += StringPrintf(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources());
+ dump += StringPrintf(INDENT2 "Sources: %s\n",
+ inputEventSourceToString(deviceInfo.getSources()).c_str());
dump += StringPrintf(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType());
dump += StringPrintf(INDENT2 "ControllerNum: %d\n", deviceInfo.getControllerNumber());
@@ -128,10 +129,10 @@ void InputDevice::dump(std::string& dump, const std::string& eventHubDevStr) {
snprintf(name, sizeof(name), "%d", range.axis);
}
dump += StringPrintf(INDENT3
- "%s: source=0x%08x, "
+ "%s: source=%s, "
"min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f, resolution=%0.3f\n",
- name, range.source, range.min, range.max, range.flat, range.fuzz,
- range.resolution);
+ name, inputEventSourceToString(range.source).c_str(), range.min,
+ range.max, range.flat, range.fuzz, range.resolution);
}
}
@@ -231,6 +232,10 @@ void InputDevice::addEventHubDevice(int32_t eventHubId, bool populateMappers) {
}
void InputDevice::removeEventHubDevice(int32_t eventHubId) {
+ if (mController != nullptr && mController->getEventHubId() == eventHubId) {
+ // Delete mController, since the corresponding eventhub device is going away
+ mController = nullptr;
+ }
mDevices.erase(eventHubId);
}
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 9c5a129213..9bcf463c36 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -196,9 +196,9 @@ void InputReader::addDeviceLocked(nsecs_t when, int32_t eventHubId) {
"(ignored non-input device)",
device->getId(), eventHubId, identifier.name.c_str(), identifier.descriptor.c_str());
} else {
- ALOGI("Device added: id=%d, eventHubId=%d, name='%s', descriptor='%s',sources=0x%08x",
+ ALOGI("Device added: id=%d, eventHubId=%d, name='%s', descriptor='%s',sources=%s",
device->getId(), eventHubId, identifier.name.c_str(), identifier.descriptor.c_str(),
- device->getSources());
+ inputEventSourceToString(device->getSources()).c_str());
}
mDevices.emplace(eventHubId, device);
@@ -250,9 +250,10 @@ void InputReader::removeDeviceLocked(nsecs_t when, int32_t eventHubId) {
device->getId(), eventHubId, device->getName().c_str(),
device->getDescriptor().c_str());
} else {
- ALOGI("Device removed: id=%d, eventHubId=%d, name='%s', descriptor='%s', sources=0x%08x",
+ ALOGI("Device removed: id=%d, eventHubId=%d, name='%s', descriptor='%s', sources=%s",
device->getId(), eventHubId, device->getName().c_str(),
- device->getDescriptor().c_str(), device->getSources());
+ device->getDescriptor().c_str(),
+ inputEventSourceToString(device->getSources()).c_str());
}
device->removeEventHubDevice(eventHubId);
diff --git a/services/inputflinger/reader/controller/PeripheralController.cpp b/services/inputflinger/reader/controller/PeripheralController.cpp
index a6934960c9..8065f57524 100644
--- a/services/inputflinger/reader/controller/PeripheralController.cpp
+++ b/services/inputflinger/reader/controller/PeripheralController.cpp
@@ -524,4 +524,8 @@ std::optional<int32_t> PeripheralController::getLightPlayerId(int32_t lightId) {
return light->getLightPlayerId();
}
+int32_t PeripheralController::getEventHubId() const {
+ return getDeviceContext().getEventHubId();
+}
+
} // namespace android
diff --git a/services/inputflinger/reader/controller/PeripheralController.h b/services/inputflinger/reader/controller/PeripheralController.h
index b1bc8c732c..ac951ebe2a 100644
--- a/services/inputflinger/reader/controller/PeripheralController.h
+++ b/services/inputflinger/reader/controller/PeripheralController.h
@@ -31,6 +31,7 @@ public:
explicit PeripheralController(InputDeviceContext& deviceContext);
~PeripheralController() override;
+ int32_t getEventHubId() const override;
void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
void dump(std::string& dump) override;
bool setLightColor(int32_t lightId, int32_t color) override;
@@ -43,6 +44,7 @@ public:
private:
inline int32_t getDeviceId() { return mDeviceContext.getId(); }
inline InputDeviceContext& getDeviceContext() { return mDeviceContext; }
+ inline InputDeviceContext& getDeviceContext() const { return mDeviceContext; }
InputDeviceContext& mDeviceContext;
void configureLights();
diff --git a/services/inputflinger/reader/controller/PeripheralControllerInterface.h b/services/inputflinger/reader/controller/PeripheralControllerInterface.h
index 7688a431d1..306e36119b 100644
--- a/services/inputflinger/reader/controller/PeripheralControllerInterface.h
+++ b/services/inputflinger/reader/controller/PeripheralControllerInterface.h
@@ -33,6 +33,8 @@ public:
PeripheralControllerInterface() {}
virtual ~PeripheralControllerInterface() {}
+ virtual int32_t getEventHubId() const = 0;
+
// Interface methods for Battery
virtual std::optional<int32_t> getBatteryCapacity(int32_t batteryId) = 0;
virtual std::optional<int32_t> getBatteryStatus(int32_t batteryId) = 0;
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index a9a4c71c02..8233682a32 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -25,6 +25,8 @@
#include "PointerControllerInterface.h"
#include "TouchCursorInputMapperCommon.h"
+#include "input/PrintTools.h"
+
namespace android {
// The default velocity control parameters that has no effect.
@@ -76,7 +78,7 @@ uint32_t CursorInputMapper::getSources() const {
void CursorInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
InputMapper::populateDeviceInfo(info);
- if (mParameters.mode == Parameters::MODE_POINTER) {
+ if (mParameters.mode == Parameters::Mode::POINTER) {
float minX, minY, maxX, maxY;
if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) {
info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, minX, maxX, 0.0f, 0.0f, 0.0f);
@@ -113,6 +115,7 @@ void CursorInputMapper::dump(std::string& dump) {
toString(mCursorScrollAccumulator.haveRelativeHWheel()));
dump += StringPrintf(INDENT3 "VWheelScale: %0.3f\n", mVWheelScale);
dump += StringPrintf(INDENT3 "HWheelScale: %0.3f\n", mHWheelScale);
+ dump += StringPrintf(INDENT3 "DisplayId: %s\n", toString(mDisplayId).c_str());
dump += StringPrintf(INDENT3 "Orientation: %d\n", mOrientation);
dump += StringPrintf(INDENT3 "ButtonState: 0x%08x\n", mButtonState);
dump += StringPrintf(INDENT3 "Down: %s\n", toString(isPointerDown(mButtonState)));
@@ -131,12 +134,12 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration*
// Configure device mode.
switch (mParameters.mode) {
- case Parameters::MODE_POINTER_RELATIVE:
+ case Parameters::Mode::POINTER_RELATIVE:
// Should not happen during first time configuration.
ALOGE("Cannot start a device in MODE_POINTER_RELATIVE, starting in MODE_POINTER");
- mParameters.mode = Parameters::MODE_POINTER;
+ mParameters.mode = Parameters::Mode::POINTER;
[[fallthrough]];
- case Parameters::MODE_POINTER:
+ case Parameters::Mode::POINTER:
mSource = AINPUT_SOURCE_MOUSE;
mXPrecision = 1.0f;
mYPrecision = 1.0f;
@@ -144,7 +147,7 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration*
mYScale = 1.0f;
mPointerController = getContext()->getPointerController(getDeviceId());
break;
- case Parameters::MODE_NAVIGATION:
+ case Parameters::Mode::NAVIGATION:
mSource = AINPUT_SOURCE_TRACKBALL;
mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
@@ -157,12 +160,13 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration*
mHWheelScale = 1.0f;
}
- const bool configurePointerCapture = (!changes && config->pointerCaptureRequest.enable) ||
- (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+ const bool configurePointerCapture = mParameters.mode != Parameters::Mode::NAVIGATION &&
+ ((!changes && config->pointerCaptureRequest.enable) ||
+ (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE));
if (configurePointerCapture) {
if (config->pointerCaptureRequest.enable) {
- if (mParameters.mode == Parameters::MODE_POINTER) {
- mParameters.mode = Parameters::MODE_POINTER_RELATIVE;
+ if (mParameters.mode == Parameters::Mode::POINTER) {
+ mParameters.mode = Parameters::Mode::POINTER_RELATIVE;
mSource = AINPUT_SOURCE_MOUSE_RELATIVE;
// Keep PointerController around in order to preserve the pointer position.
mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
@@ -170,8 +174,8 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration*
ALOGE("Cannot request pointer capture, device is not in MODE_POINTER");
}
} else {
- if (mParameters.mode == Parameters::MODE_POINTER_RELATIVE) {
- mParameters.mode = Parameters::MODE_POINTER;
+ if (mParameters.mode == Parameters::Mode::POINTER_RELATIVE) {
+ mParameters.mode = Parameters::Mode::POINTER;
mSource = AINPUT_SOURCE_MOUSE;
} else {
ALOGE("Cannot release pointer capture, device is not in MODE_POINTER_RELATIVE");
@@ -186,8 +190,8 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration*
if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED) ||
configurePointerCapture) {
- if (config->pointerCaptureRequest.enable) {
- // Disable any acceleration or scaling when Pointer Capture is enabled.
+ if (mParameters.mode == Parameters::Mode::POINTER_RELATIVE) {
+ // Disable any acceleration or scaling for the pointer when Pointer Capture is enabled.
mPointerVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS);
mWheelXVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS);
mWheelYVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS);
@@ -198,21 +202,36 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration*
}
}
- if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+ if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO) ||
+ configurePointerCapture) {
+ const bool isPointer = mParameters.mode == Parameters::Mode::POINTER;
+
+ mDisplayId = ADISPLAY_ID_NONE;
+ if (auto viewport = mDeviceContext.getAssociatedViewport(); viewport) {
+ // This InputDevice is associated with a viewport.
+ // Only generate events for the associated display.
+ const bool mismatchedPointerDisplay =
+ isPointer && (viewport->displayId != mPointerController->getDisplayId());
+ mDisplayId = mismatchedPointerDisplay ? std::nullopt
+ : std::make_optional(viewport->displayId);
+ } else if (isPointer) {
+ // The InputDevice is not associated with a viewport, but it controls the mouse pointer.
+ mDisplayId = mPointerController->getDisplayId();
+ }
+
mOrientation = DISPLAY_ORIENTATION_0;
const bool isOrientedDevice =
(mParameters.orientationAware && mParameters.hasAssociatedDisplay);
-
// InputReader works in the un-rotated display coordinate space, so we don't need to do
// anything if the device is already orientation-aware. If the device is not
// orientation-aware, then we need to apply the inverse rotation of the display so that
// when the display rotation is applied later as a part of the per-window transform, we
- // get the expected screen coordinates.
- if (!isOrientedDevice) {
- std::optional<DisplayViewport> internalViewport =
- config->getDisplayViewportByType(ViewportType::INTERNAL);
- if (internalViewport) {
- mOrientation = getInverseRotation(internalViewport->orientation);
+ // get the expected screen coordinates. When pointer capture is enabled, we do not apply any
+ // rotations and report values directly from the input device.
+ if (!isOrientedDevice && mDisplayId &&
+ mParameters.mode != Parameters::Mode::POINTER_RELATIVE) {
+ if (auto viewport = config->getDisplayViewportById(*mDisplayId); viewport) {
+ mOrientation = getInverseRotation(viewport->orientation);
}
}
@@ -221,12 +240,12 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration*
}
void CursorInputMapper::configureParameters() {
- mParameters.mode = Parameters::MODE_POINTER;
+ mParameters.mode = Parameters::Mode::POINTER;
String8 cursorModeString;
if (getDeviceContext().getConfiguration().tryGetProperty(String8("cursor.mode"),
cursorModeString)) {
if (cursorModeString == "navigation") {
- mParameters.mode = Parameters::MODE_NAVIGATION;
+ mParameters.mode = Parameters::Mode::NAVIGATION;
} else if (cursorModeString != "pointer" && cursorModeString != "default") {
ALOGW("Invalid value for cursor.mode: '%s'", cursorModeString.string());
}
@@ -237,7 +256,7 @@ void CursorInputMapper::configureParameters() {
mParameters.orientationAware);
mParameters.hasAssociatedDisplay = false;
- if (mParameters.mode == Parameters::MODE_POINTER || mParameters.orientationAware) {
+ if (mParameters.mode == Parameters::Mode::POINTER || mParameters.orientationAware) {
mParameters.hasAssociatedDisplay = true;
}
}
@@ -246,21 +265,7 @@ void CursorInputMapper::dumpParameters(std::string& dump) {
dump += INDENT3 "Parameters:\n";
dump += StringPrintf(INDENT4 "HasAssociatedDisplay: %s\n",
toString(mParameters.hasAssociatedDisplay));
-
- switch (mParameters.mode) {
- case Parameters::MODE_POINTER:
- dump += INDENT4 "Mode: pointer\n";
- break;
- case Parameters::MODE_POINTER_RELATIVE:
- dump += INDENT4 "Mode: relative pointer\n";
- break;
- case Parameters::MODE_NAVIGATION:
- dump += INDENT4 "Mode: navigation\n";
- break;
- default:
- ALOG_ASSERT(false);
- }
-
+ dump += StringPrintf(INDENT4 "Mode: %s\n", ftl::enum_string(mParameters.mode).c_str());
dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware));
}
@@ -290,6 +295,11 @@ void CursorInputMapper::process(const RawEvent* rawEvent) {
}
void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) {
+ if (!mDisplayId) {
+ // Ignore events when there is no target display configured.
+ return;
+ }
+
int32_t lastButtonState = mButtonState;
int32_t currentButtonState = mCursorButtonAccumulator.getButtonState();
mButtonState = currentButtonState;
@@ -335,7 +345,6 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) {
mPointerVelocityControl.move(when, &deltaX, &deltaY);
- int32_t displayId = ADISPLAY_ID_NONE;
float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
if (mSource == AINPUT_SOURCE_MOUSE) {
@@ -359,7 +368,6 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) {
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
- displayId = mPointerController->getDisplayId();
} else {
// Pointer capture and navigation modes
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX);
@@ -381,7 +389,7 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) {
// Synthesize key down from buttons if needed.
synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, readTime, getDeviceId(),
- mSource, displayId, policyFlags, lastButtonState, currentButtonState);
+ mSource, *mDisplayId, policyFlags, lastButtonState, currentButtonState);
// Send motion event.
if (downChanged || moved || scrolled || buttonsChanged) {
@@ -402,7 +410,7 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) {
int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit());
buttonState &= ~actionButton;
NotifyMotionArgs releaseArgs(getContext()->getNextId(), when, readTime,
- getDeviceId(), mSource, displayId, policyFlags,
+ getDeviceId(), mSource, *mDisplayId, policyFlags,
AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
metaState, buttonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
@@ -414,7 +422,7 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) {
}
NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
- displayId, policyFlags, motionEventAction, 0, 0, metaState,
+ *mDisplayId, policyFlags, motionEventAction, 0, 0, metaState,
currentButtonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords,
mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime,
@@ -427,7 +435,7 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) {
int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit());
buttonState |= actionButton;
NotifyMotionArgs pressArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
- mSource, displayId, policyFlags,
+ mSource, *mDisplayId, policyFlags,
AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0,
metaState, buttonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
@@ -443,7 +451,7 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) {
// Send hover move after UP to tell the application that the mouse is hovering now.
if (motionEventAction == AMOTION_EVENT_ACTION_UP && (mSource == AINPUT_SOURCE_MOUSE)) {
NotifyMotionArgs hoverArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
- mSource, displayId, policyFlags,
+ mSource, *mDisplayId, policyFlags,
AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
currentButtonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
@@ -458,7 +466,7 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) {
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
- mSource, displayId, policyFlags,
+ mSource, *mDisplayId, policyFlags,
AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
currentButtonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
@@ -470,7 +478,7 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) {
// Synthesize key up from buttons if needed.
synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, readTime, getDeviceId(), mSource,
- displayId, policyFlags, lastButtonState, currentButtonState);
+ *mDisplayId, policyFlags, lastButtonState, currentButtonState);
mCursorMotionAccumulator.finishSync();
mCursorScrollAccumulator.finishSync();
@@ -485,16 +493,7 @@ int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCod
}
std::optional<int32_t> CursorInputMapper::getAssociatedDisplayId() {
- if (mParameters.hasAssociatedDisplay) {
- if (mParameters.mode == Parameters::MODE_POINTER) {
- return std::make_optional(mPointerController->getDisplayId());
- } else {
- // If the device is orientationAware and not a mouse,
- // it expects to dispatch events to any display
- return std::make_optional(ADISPLAY_ID_NONE);
- }
- }
- return std::nullopt;
+ return mDisplayId;
}
} // namespace android
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index c84c6c4229..60b3dd9ee0 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -74,10 +74,17 @@ private:
// Immutable configuration parameters.
struct Parameters {
- enum Mode {
- MODE_POINTER,
- MODE_POINTER_RELATIVE,
- MODE_NAVIGATION,
+ enum class Mode {
+ // In POINTER mode, the device is a mouse that controls the mouse cursor on the screen,
+ // reporting absolute screen locations using SOURCE_MOUSE.
+ POINTER,
+ // A mouse device in POINTER mode switches to the POINTER_RELATIVE mode when Pointer
+ // Capture is enabled, and reports relative values only using SOURCE_MOUSE_RELATIVE.
+ POINTER_RELATIVE,
+ // A device in NAVIGATION mode emits relative values using SOURCE_TRACKBALL.
+ NAVIGATION,
+
+ ftl_last = NAVIGATION,
};
Mode mode;
@@ -104,6 +111,10 @@ private:
VelocityControl mWheelXVelocityControl;
VelocityControl mWheelYVelocityControl;
+ // The display that events generated by this mapper should target. This can be set to
+ // ADISPLAY_ID_NONE to target the focused display. If there is no display target (i.e.
+ // std::nullopt), all events will be ignored.
+ std::optional<int32_t> mDisplayId;
int32_t mOrientation;
std::shared_ptr<PointerControllerInterface> mPointerController;
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 637b1cb263..428fe10156 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -44,6 +44,8 @@ static constexpr nsecs_t STYLUS_DATA_LATENCY = ms2ns(10);
// --- Static Definitions ---
+static const DisplayViewport kUninitializedViewport;
+
template <typename T>
inline static void swap(T& a, T& b) {
T temp = a;
@@ -390,6 +392,10 @@ void TouchInputMapper::configure(nsecs_t when, const InputReaderConfiguration* c
}
if (changes && resetNeeded) {
+ // If the device needs to be reset, cancel any ongoing gestures and reset the state.
+ cancelTouch(when, when);
+ reset(when);
+
// Send reset, unless this is the first time the device has been configured,
// in which case the reader will call reset itself after all mappers are ready.
NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId());
@@ -877,7 +883,7 @@ void TouchInputMapper::initializeOrientedRanges() {
}
void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) {
- DeviceMode oldDeviceMode = mDeviceMode;
+ const DeviceMode oldDeviceMode = mDeviceMode;
resolveExternalStylusPresence();
@@ -906,42 +912,37 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded)
mDeviceMode = DeviceMode::UNSCALED;
}
- // Ensure we have valid X and Y axes.
+ const std::optional<DisplayViewport> newViewportOpt = findViewport();
+
+ // Ensure the device is valid and can be used.
if (!mRawPointerAxes.x.valid || !mRawPointerAxes.y.valid) {
ALOGW("Touch device '%s' did not report support for X or Y axis! "
"The device will be inoperable.",
getDeviceName().c_str());
mDeviceMode = DeviceMode::DISABLED;
- return;
- }
-
- // Get associated display dimensions.
- std::optional<DisplayViewport> newViewport = findViewport();
- if (!newViewport) {
+ } else if (!newViewportOpt) {
ALOGI("Touch device '%s' could not query the properties of its associated "
"display. The device will be inoperable until the display size "
"becomes available.",
getDeviceName().c_str());
mDeviceMode = DeviceMode::DISABLED;
- return;
- }
-
- if (!newViewport->isActive) {
+ } else if (!newViewportOpt->isActive) {
ALOGI("Disabling %s (device %i) because the associated viewport is not active",
getDeviceName().c_str(), getDeviceId());
mDeviceMode = DeviceMode::DISABLED;
- return;
}
// Raw width and height in the natural orientation.
const int32_t rawWidth = mRawPointerAxes.getRawWidth();
const int32_t rawHeight = mRawPointerAxes.getRawHeight();
- const bool viewportChanged = mViewport != *newViewport;
+ const DisplayViewport& newViewport = newViewportOpt.value_or(kUninitializedViewport);
+ const bool viewportChanged = mViewport != newViewport;
bool skipViewportUpdate = false;
if (viewportChanged) {
- const bool viewportOrientationChanged = mViewport.orientation != newViewport->orientation;
- mViewport = *newViewport;
+ const bool viewportOrientationChanged = mViewport.orientation != newViewport.orientation;
+ const bool viewportDisplayIdChanged = mViewport.displayId != newViewport.displayId;
+ mViewport = newViewport;
if (mDeviceMode == DeviceMode::DIRECT || mDeviceMode == DeviceMode::POINTER) {
// Convert rotated viewport to the natural orientation.
@@ -1016,8 +1017,9 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded)
: getInverseRotation(mViewport.orientation);
// For orientation-aware devices that work in the un-rotated coordinate space, the
// viewport update should be skipped if it is only a change in the orientation.
- skipViewportUpdate = mParameters.orientationAware && mDisplayWidth == oldDisplayWidth &&
- mDisplayHeight == oldDisplayHeight && viewportOrientationChanged;
+ skipViewportUpdate = !viewportDisplayIdChanged && mParameters.orientationAware &&
+ mDisplayWidth == oldDisplayWidth && mDisplayHeight == oldDisplayHeight &&
+ viewportOrientationChanged;
// Apply the input device orientation for the device.
mInputDeviceOrientation =
@@ -1094,10 +1096,6 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded)
// of the diagonal axis of the touch pad. Touches that are wider than this are
// translated into freeform gestures.
mPointerGestureMaxSwipeWidth = mConfig.pointerGestureSwipeMaxWidthRatio * rawDiagonal;
-
- // Abort current pointer usages because the state has changed.
- const nsecs_t readTime = when; // synthetic event
- abortPointerUsage(when, readTime, 0 /*policyFlags*/);
}
// Inform the dispatcher about the changes.
@@ -1926,6 +1924,10 @@ void TouchInputMapper::dispatchVirtualKey(nsecs_t when, nsecs_t readTime, uint32
}
void TouchInputMapper::abortTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+ if (mCurrentMotionAborted) {
+ // Current motion event was already aborted.
+ return;
+ }
BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits;
if (!currentIdBits.isEmpty()) {
int32_t metaState = getContext()->getGlobalMetaState();
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 76a7c19086..75cd9da782 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -60,6 +60,7 @@ cc_test {
},
static_libs: [
"libc++fs",
+ "libgmock",
],
require_root: true,
test_suites: ["device-tests"],
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index df4307101f..df1a230002 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -2293,6 +2293,35 @@ TEST_F(InputDispatcherTest, ActionOutsideSentOnlyWhenAWindowIsTouched) {
thirdWindow->consumeMotionDown();
}
+TEST_F(InputDispatcherTest, OnWindowInfosChanged_RemoveAllWindowsOnDisplay) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+ window->setFocusable(true);
+
+ mDispatcher->onWindowInfosChanged({*window->getInfo()}, {});
+ setFocusedWindow(window);
+
+ window->consumeFocusEvent(true);
+
+ NotifyKeyArgs keyDown = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+ NotifyKeyArgs keyUp = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyKey(&keyDown);
+ mDispatcher->notifyKey(&keyUp);
+
+ window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+ window->consumeKeyUp(ADISPLAY_ID_DEFAULT);
+
+ // All windows are removed from the display. Ensure that we can no longer dispatch to it.
+ mDispatcher->onWindowInfosChanged({}, {});
+
+ window->consumeFocusEvent(false);
+
+ mDispatcher->notifyKey(&keyDown);
+ mDispatcher->notifyKey(&keyUp);
+ window->assertNoEvents();
+}
+
/**
* Ensure the correct coordinate spaces are used by InputDispatcher.
*
@@ -6096,6 +6125,8 @@ protected:
sp<FakeWindowHandle> mWindow;
sp<FakeWindowHandle> mSecondWindow;
sp<FakeWindowHandle> mDragWindow;
+ // Mouse would force no-split, set the id as non-zero to verify if drag state could track it.
+ static constexpr int32_t MOUSE_POINTER_ID = 1;
void SetUp() override {
InputDispatcherTest::SetUp();
@@ -6110,11 +6141,41 @@ protected:
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mSecondWindow}}});
}
- void injectDown() {
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {50, 50}))
- << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ void injectDown(int fromSource = AINPUT_SOURCE_TOUCHSCREEN) {
+ switch (fromSource) {
+ case AINPUT_SOURCE_TOUCHSCREEN:
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ break;
+ case AINPUT_SOURCE_STYLUS:
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(
+ mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
+ AINPUT_SOURCE_STYLUS)
+ .buttonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_STYLUS)
+ .x(50)
+ .y(50))
+ .build()));
+ break;
+ case AINPUT_SOURCE_MOUSE:
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(
+ mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(MOUSE_POINTER_ID,
+ AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(50)
+ .y(50))
+ .build()));
+ break;
+ default:
+ FAIL() << "Source " << fromSource << " doesn't support drag and drop";
+ }
// Window should receive motion event.
mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -6123,9 +6184,9 @@ protected:
// Start performing drag, we will create a drag window and transfer touch to it.
// @param sendDown : if true, send a motion down on first window before perform drag and drop.
// Returns true on success.
- bool performDrag(bool sendDown = true) {
+ bool startDrag(bool sendDown = true, int fromSource = AINPUT_SOURCE_TOUCHSCREEN) {
if (sendDown) {
- injectDown();
+ injectDown(fromSource);
}
// The drag window covers the entire display
@@ -6143,36 +6204,10 @@ protected:
}
return transferred;
}
-
- // Start performing drag, we will create a drag window and transfer touch to it.
- void performStylusDrag() {
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
- MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_STYLUS)
- .buttonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)
- .pointer(PointerBuilder(0,
- AMOTION_EVENT_TOOL_TYPE_STYLUS)
- .x(50)
- .y(50))
- .build()));
- mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
-
- // The drag window covers the entire display
- mDragWindow = new FakeWindowHandle(mApp, mDispatcher, "DragWindow", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows(
- {{ADISPLAY_ID_DEFAULT, {mDragWindow, mWindow, mSecondWindow}}});
-
- // Transfer touch focus to the drag window
- mDispatcher->transferTouchFocus(mWindow->getToken(), mDragWindow->getToken(),
- true /* isDragDrop */);
- mWindow->consumeMotionCancel();
- mDragWindow->consumeMotionDown();
- }
};
TEST_F(InputDispatcherDragTests, DragEnterAndDragExit) {
- performDrag();
+ startDrag();
// Move on window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -6210,7 +6245,7 @@ TEST_F(InputDispatcherDragTests, DragEnterAndDragExit) {
}
TEST_F(InputDispatcherDragTests, DragAndDrop) {
- performDrag();
+ startDrag();
// Move on window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -6242,7 +6277,7 @@ TEST_F(InputDispatcherDragTests, DragAndDrop) {
}
TEST_F(InputDispatcherDragTests, StylusDragAndDrop) {
- performStylusDrag();
+ startDrag(true, AINPUT_SOURCE_STYLUS);
// Move on window and keep button pressed.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -6289,7 +6324,7 @@ TEST_F(InputDispatcherDragTests, StylusDragAndDrop) {
}
TEST_F(InputDispatcherDragTests, DragAndDropOnInvalidWindow) {
- performDrag();
+ startDrag();
// Set second window invisible.
mSecondWindow->setVisible(false);
@@ -6325,6 +6360,9 @@ TEST_F(InputDispatcherDragTests, DragAndDropOnInvalidWindow) {
}
TEST_F(InputDispatcherDragTests, NoDragAndDropWhenMultiFingers) {
+ // Ensure window could track pointerIds if it didn't support split touch.
+ mWindow->setPreventSplitting(true);
+
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{50, 50}))
@@ -6345,7 +6383,7 @@ TEST_F(InputDispatcherDragTests, NoDragAndDropWhenMultiFingers) {
mWindow->consumeMotionPointerDown(1 /* pointerIndex */);
// Should not perform drag and drop when window has multi fingers.
- ASSERT_FALSE(performDrag(false));
+ ASSERT_FALSE(startDrag(false));
}
TEST_F(InputDispatcherDragTests, DragAndDropWhenSplitTouch) {
@@ -6373,7 +6411,7 @@ TEST_F(InputDispatcherDragTests, DragAndDropWhenSplitTouch) {
mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
// Perform drag and drop from first window.
- ASSERT_TRUE(performDrag(false));
+ ASSERT_TRUE(startDrag(false));
// Move on window.
const MotionEvent secondFingerMoveEvent =
@@ -6408,7 +6446,7 @@ TEST_F(InputDispatcherDragTests, DragAndDropWhenSplitTouch) {
}
TEST_F(InputDispatcherDragTests, DragAndDropWhenMultiDisplays) {
- performDrag();
+ startDrag();
// Update window of second display.
sp<FakeWindowHandle> windowInSecondary =
@@ -6459,6 +6497,55 @@ TEST_F(InputDispatcherDragTests, DragAndDropWhenMultiDisplays) {
mSecondWindow->assertNoEvents();
}
+TEST_F(InputDispatcherDragTests, MouseDragAndDrop) {
+ startDrag(true, AINPUT_SOURCE_MOUSE);
+ // Move on window.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(MOUSE_POINTER_ID,
+ AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(50)
+ .y(50))
+ .build()))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+ mWindow->consumeDragEvent(false, 50, 50);
+ mSecondWindow->assertNoEvents();
+
+ // Move to another window.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(MOUSE_POINTER_ID,
+ AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(150)
+ .y(50))
+ .build()))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+ mWindow->consumeDragEvent(true, 150, 50);
+ mSecondWindow->consumeDragEvent(false, 50, 50);
+
+ // drop to another window.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE)
+ .buttonState(0)
+ .pointer(PointerBuilder(MOUSE_POINTER_ID,
+ AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(150)
+ .y(50))
+ .build()))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+ mFakePolicy->assertDropTargetEquals(mSecondWindow->getToken());
+ mWindow->assertNoEvents();
+ mSecondWindow->assertNoEvents();
+}
+
class InputDispatcherDropInputFeatureTest : public InputDispatcherTest {};
TEST_F(InputDispatcherDropInputFeatureTest, WindowDropsInput) {
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index a26a0bcf67..74d4f3b99f 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -56,7 +56,9 @@ static constexpr nsecs_t READ_TIME = 4321;
// Arbitrary display properties.
static constexpr int32_t DISPLAY_ID = 0;
+static const std::string DISPLAY_UNIQUE_ID = "local:1";
static constexpr int32_t SECONDARY_DISPLAY_ID = DISPLAY_ID + 1;
+static const std::string SECONDARY_DISPLAY_UNIQUE_ID = "local:2";
static constexpr int32_t DISPLAY_WIDTH = 480;
static constexpr int32_t DISPLAY_HEIGHT = 800;
static constexpr int32_t VIRTUAL_DISPLAY_ID = 1;
@@ -91,6 +93,24 @@ static constexpr int32_t ACTION_POINTER_1_UP =
// Error tolerance for floating point assertions.
static const float EPSILON = 0.001f;
+using ::testing::AllOf;
+
+MATCHER_P(WithAction, action, "InputEvent with specified action") {
+ return arg.action == action;
+}
+
+MATCHER_P(WithSource, source, "InputEvent with specified source") {
+ return arg.source == source;
+}
+
+MATCHER_P(WithDisplayId, displayId, "InputEvent with specified displayId") {
+ return arg.displayId == displayId;
+}
+
+MATCHER_P2(WithCoords, x, y, "MotionEvent with specified action") {
+ return arg.pointerCoords[0].getX() == x && arg.pointerCoords[0].getY();
+}
+
template<typename T>
static inline T min(T a, T b) {
return a < b ? a : b;
@@ -2137,6 +2157,8 @@ public:
~FakePeripheralController() override {}
+ int32_t getEventHubId() const { return getDeviceContext().getEventHubId(); }
+
void populateDeviceInfo(InputDeviceInfo* deviceInfo) override {}
void dump(std::string& dump) override {}
@@ -2170,6 +2192,7 @@ private:
InputDeviceContext& mDeviceContext;
inline int32_t getDeviceId() { return mDeviceContext.getId(); }
inline InputDeviceContext& getDeviceContext() { return mDeviceContext; }
+ inline InputDeviceContext& getDeviceContext() const { return mDeviceContext; }
};
TEST_F(InputReaderTest, BatteryGetCapacity) {
@@ -2872,7 +2895,6 @@ TEST_F(InputDeviceTest, Configure_AssignsDisplayUniqueId) {
// Device should be disabled because it is associated with a specific display, but the
// corresponding display is not found.
- const std::string DISPLAY_UNIQUE_ID = "displayUniqueId";
mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID);
mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
InputReaderConfiguration::CHANGE_DISPLAY_INFO);
@@ -2903,7 +2925,6 @@ TEST_F(InputDeviceTest, Configure_UniqueId_CorrectlyMatches) {
mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD);
mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
- const std::string DISPLAY_UNIQUE_ID = "displayUniqueId";
mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID);
mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
DISPLAY_ORIENTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID,
@@ -2913,6 +2934,21 @@ TEST_F(InputDeviceTest, Configure_UniqueId_CorrectlyMatches) {
ASSERT_EQ(DISPLAY_UNIQUE_ID, mDevice->getAssociatedDisplayUniqueId());
}
+/**
+ * This test reproduces a crash caused by a dangling reference that remains after device is added
+ * and removed. The reference is accessed in InputDevice::dump(..);
+ */
+TEST_F(InputDeviceTest, DumpDoesNotCrash) {
+ constexpr int32_t TEST_EVENTHUB_ID = 10;
+ mFakeEventHub->addDevice(TEST_EVENTHUB_ID, "Test EventHub device", InputDeviceClass::BATTERY);
+
+ InputDevice device(mReader->getContext(), 1 /*id*/, 2 /*generation*/, {} /*identifier*/);
+ device.addEventHubDevice(TEST_EVENTHUB_ID, true /*populateMappers*/);
+ device.removeEventHubDevice(TEST_EVENTHUB_ID);
+ std::string dumpStr, eventHubDevStr;
+ device.dump(dumpStr, eventHubDevStr);
+}
+
// --- InputMapperTest ---
class InputMapperTest : public testing::Test {
@@ -2938,6 +2974,8 @@ protected:
mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
*mFakeListener);
mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes);
+ // Consume the device reset notification generated when adding a new device.
+ mFakeListener->assertNotifyDeviceResetWasCalled();
}
void SetUp() override {
@@ -2962,6 +3000,8 @@ protected:
mReader->loopOnce();
}
mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
+ // Loop the reader to flush the input listener queue.
+ mReader->loopOnce();
}
std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
@@ -2985,6 +3025,8 @@ protected:
configureDevice(0);
mDevice->reset(ARBITRARY_TIME);
mapper.reset(ARBITRARY_TIME);
+ // Loop the reader to flush the input listener queue.
+ mReader->loopOnce();
return mapper;
}
@@ -3010,6 +3052,7 @@ protected:
event.code = code;
event.value = value;
mapper.process(&event);
+ // Loop the reader to flush the input listener queue.
mReader->loopOnce();
}
@@ -4181,10 +4224,14 @@ protected:
int32_t rotatedX, int32_t rotatedY);
void prepareDisplay(int32_t orientation) {
- const std::string uniqueId = "local:0";
- const ViewportType viewportType = ViewportType::INTERNAL;
- setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- orientation, uniqueId, NO_PORT, viewportType);
+ setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation,
+ DISPLAY_UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
+ }
+
+ void prepareSecondaryDisplay() {
+ setDisplayInfoAndReconfigure(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_ORIENTATION_0, SECONDARY_DISPLAY_UNIQUE_ID, NO_PORT,
+ ViewportType::EXTERNAL);
}
static void assertCursorPointerCoords(const PointerCoords& coords, float x, float y,
@@ -4461,6 +4508,7 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleCombinedXYAndButtonUpdates) {
}
TEST_F(CursorInputMapperTest, Process_WhenOrientationAware_ShouldNotRotateMotions) {
+ mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID);
addConfigurationProperty("cursor.mode", "navigation");
// InputReader works in the un-rotated coordinate space, so orientation-aware devices do not
// need to be rotated.
@@ -4479,11 +4527,13 @@ TEST_F(CursorInputMapperTest, Process_WhenOrientationAware_ShouldNotRotateMotion
}
TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldRotateMotions) {
+ mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID);
addConfigurationProperty("cursor.mode", "navigation");
// Since InputReader works in the un-rotated coordinate space, only devices that are not
// orientation-aware are affected by display rotation.
CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+ clearViewports();
prepareDisplay(DISPLAY_ORIENTATION_0);
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, 1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, 1));
@@ -4494,6 +4544,7 @@ TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldRotateMotion
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, -1, 0));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, 1));
+ clearViewports();
prepareDisplay(DISPLAY_ORIENTATION_90);
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, -1, 0));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, -1, 1));
@@ -4504,6 +4555,7 @@ TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldRotateMotion
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, 0, -1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, -1));
+ clearViewports();
prepareDisplay(DISPLAY_ORIENTATION_180);
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, -1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, -1, -1));
@@ -4514,6 +4566,7 @@ TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldRotateMotion
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, 1, 0));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, 1, -1));
+ clearViewports();
prepareDisplay(DISPLAY_ORIENTATION_270);
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 1, 0));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, -1));
@@ -4907,7 +4960,6 @@ TEST_F(CursorInputMapperTest, Process_PointerCapture) {
ASSERT_TRUE(mReader->getContext()->getGeneration() != generation);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
- ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
ASSERT_EQ(DEVICE_ID, resetArgs.deviceId);
process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
@@ -4969,33 +5021,118 @@ TEST_F(CursorInputMapperTest, PointerCaptureDisablesVelocityProcessing) {
ASSERT_EQ(20, args.pointerCoords[0].getY());
}
-TEST_F(CursorInputMapperTest, Process_ShouldHandleDisplayId) {
+TEST_F(CursorInputMapperTest, PointerCaptureDisablesOrientationChanges) {
+ addConfigurationProperty("cursor.mode", "pointer");
CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
- // Setup for second display.
- constexpr int32_t SECOND_DISPLAY_ID = 1;
- const std::string SECOND_DISPLAY_UNIQUE_ID = "local:1";
- mFakePolicy->addDisplayViewport(SECOND_DISPLAY_ID, 800, 480, DISPLAY_ORIENTATION_0,
- true /*isActive*/, SECOND_DISPLAY_UNIQUE_ID, NO_PORT,
- ViewportType::EXTERNAL);
- mFakePolicy->setDefaultPointerDisplayId(SECOND_DISPLAY_ID);
- configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ NotifyDeviceResetArgs resetArgs;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+ ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
+ ASSERT_EQ(DEVICE_ID, resetArgs.deviceId);
- mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
- mFakePointerController->setPosition(100, 200);
- mFakePointerController->setButtonState(0);
+ // Ensure the display is rotated.
+ prepareDisplay(DISPLAY_ORIENTATION_90);
NotifyMotionArgs args;
+
+ // Verify that the coordinates are rotated.
process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source);
ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
- 110.0f, 220.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+ ASSERT_EQ(-20, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X));
+ ASSERT_EQ(10, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y));
+
+ // Enable Pointer Capture.
+ mFakePolicy->setPointerCapture(true);
+ configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+ NotifyPointerCaptureChangedArgs captureArgs;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyCaptureWasCalled(&captureArgs));
+ ASSERT_TRUE(captureArgs.request.enable);
+
+ // Move and verify rotation is not applied.
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+ ASSERT_EQ(10, args.pointerCoords[0].getX());
+ ASSERT_EQ(20, args.pointerCoords[0].getY());
+}
+
+TEST_F(CursorInputMapperTest, ConfigureDisplayId_NoAssociatedViewport) {
+ CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+
+ // Set up the default display.
+ prepareDisplay(DISPLAY_ORIENTATION_90);
+
+ // Set up the secondary display as the display on which the pointer should be shown.
+ // The InputDevice is not associated with any display.
+ prepareSecondaryDisplay();
+ mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
+ configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+
+ mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
+ mFakePointerController->setPosition(100, 200);
+ mFakePointerController->setButtonState(0);
+
+ // Ensure input events are generated for the secondary display.
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE),
+ WithDisplayId(SECONDARY_DISPLAY_ID), WithCoords(110.0f, 220.0f))));
+ ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 110.0f, 220.0f));
+}
+
+TEST_F(CursorInputMapperTest, ConfigureDisplayId_WithAssociatedViewport) {
+ CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+
+ // Set up the default display.
+ prepareDisplay(DISPLAY_ORIENTATION_90);
+
+ // Set up the secondary display as the display on which the pointer should be shown,
+ // and associate the InputDevice with the secondary display.
+ prepareSecondaryDisplay();
+ mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
+ mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, SECONDARY_DISPLAY_UNIQUE_ID);
+ configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+
+ mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
+ mFakePointerController->setPosition(100, 200);
+ mFakePointerController->setButtonState(0);
+
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE),
+ WithDisplayId(SECONDARY_DISPLAY_ID), WithCoords(110.0f, 220.0f))));
ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 110.0f, 220.0f));
- ASSERT_EQ(SECOND_DISPLAY_ID, args.displayId);
+}
+
+TEST_F(CursorInputMapperTest, ConfigureDisplayId_IgnoresEventsForMismatchedPointerDisplay) {
+ CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+
+ // Set up the default display as the display on which the pointer should be shown.
+ prepareDisplay(DISPLAY_ORIENTATION_90);
+ mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
+
+ // Associate the InputDevice with the secondary display.
+ prepareSecondaryDisplay();
+ mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, SECONDARY_DISPLAY_UNIQUE_ID);
+ configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+
+ // The mapper should not generate any events because it is associated with a display that is
+ // different from the pointer display.
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
}
// --- TouchInputMapperTest ---
@@ -6648,6 +6785,89 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenAbsPressureIsPresent_HoversIfItsV
toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
}
+TEST_F(SingleTouchInputMapperTest,
+ Process_WhenViewportDisplayIdChanged_TouchIsCanceledAndDeviceIsReset) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareButtons();
+ prepareAxes(POSITION);
+ SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+ NotifyMotionArgs motionArgs;
+
+ // Down.
+ processDown(mapper, 100, 200);
+ processSync(mapper);
+
+ // We should receive a down event
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+
+ // Change display id
+ clearViewports();
+ prepareSecondaryDisplay(ViewportType::INTERNAL);
+
+ // We should receive a cancel event
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionArgs.action);
+ // Then receive reset called
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
+}
+
+TEST_F(SingleTouchInputMapperTest,
+ Process_WhenViewportActiveStatusChanged_TouchIsCanceledAndDeviceIsReset) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareButtons();
+ prepareAxes(POSITION);
+ SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
+ NotifyMotionArgs motionArgs;
+
+ // Start a new gesture.
+ processDown(mapper, 100, 200);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+
+ // Make the viewport inactive. This will put the device in disabled mode.
+ auto viewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
+ viewport->isActive = false;
+ mFakePolicy->updateViewport(*viewport);
+ configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+
+ // We should receive a cancel event for the ongoing gesture.
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionArgs.action);
+ // Then we should be notified that the device was reset.
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
+
+ // No events are generated while the viewport is inactive.
+ processMove(mapper, 101, 201);
+ processSync(mapper);
+ processDown(mapper, 102, 202);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+ // Make the viewport active again. The device should resume processing events.
+ viewport->isActive = true;
+ mFakePolicy->updateViewport(*viewport);
+ configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+
+ // The device is reset because it changes back to direct mode, without generating any events.
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+ // Start a new gesture.
+ processDown(mapper, 100, 200);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+
+ // No more events.
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasNotCalled());
+}
+
// --- TouchDisplayProjectionTest ---
class TouchDisplayProjectionTest : public SingleTouchInputMapperTest {
@@ -8505,27 +8725,27 @@ TEST_F(MultiTouchInputMapperTest, Process_DeactivateViewport_AbortTouches) {
ASSERT_TRUE(mFakePolicy->updateViewport(displayViewport));
configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
- // Finger move
+ // The ongoing touch should be canceled immediately
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ EXPECT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionArgs.action);
+
+ // Finger move is ignored
x += 10, y += 10;
processPosition(mapper, x, y);
processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- EXPECT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionArgs.action);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
// Reactivate display viewport
displayViewport.isActive = true;
ASSERT_TRUE(mFakePolicy->updateViewport(displayViewport));
configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
- // Finger move again
+ // Finger move again starts new gesture
x += 10, y += 10;
processPosition(mapper, x, y);
processSync(mapper);
-
- // Gesture is aborted, so events after display is activated won't be dispatched until there is
- // no pointer on the touch device.
- mFakeListener->assertNotifyMotionWasNotCalled();
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
}
TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) {
@@ -8683,6 +8903,10 @@ TEST_F(MultiTouchInputMapperTest, VideoFrames_WhenNotOrientationAware_AreRotated
// window's coordinate space.
frames[0].rotate(getInverseRotation(orientation));
ASSERT_EQ(frames, motionArgs.videoFrames);
+
+ // Release finger.
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
}
}
diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp
index 6a26c636d9..57b382c718 100644
--- a/services/inputflinger/tests/TestInputListener.cpp
+++ b/services/inputflinger/tests/TestInputListener.cpp
@@ -69,6 +69,13 @@ void TestInputListener::assertNotifyMotionWasCalled(NotifyMotionArgs* outEventAr
"Expected notifyMotion() to have been called."));
}
+void TestInputListener::assertNotifyMotionWasCalled(
+ const ::testing::Matcher<NotifyMotionArgs>& matcher) {
+ NotifyMotionArgs outEventArgs;
+ ASSERT_NO_FATAL_FAILURE(assertNotifyMotionWasCalled(&outEventArgs));
+ ASSERT_THAT(outEventArgs, matcher);
+}
+
void TestInputListener::assertNotifyMotionWasNotCalled() {
ASSERT_NO_FATAL_FAILURE(
assertNotCalled<NotifyMotionArgs>("notifyMotion() should not be called."));
diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h
index 626cdfc8c8..0bdfc6b9fb 100644
--- a/services/inputflinger/tests/TestInputListener.h
+++ b/services/inputflinger/tests/TestInputListener.h
@@ -18,6 +18,7 @@
#define _UI_TEST_INPUT_LISTENER_H
#include <android-base/thread_annotations.h>
+#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "InputListener.h"
@@ -48,6 +49,8 @@ public:
void assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs = nullptr);
+ void assertNotifyMotionWasCalled(const ::testing::Matcher<NotifyMotionArgs>& matcher);
+
void assertNotifyMotionWasNotCalled();
void assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs = nullptr);
diff --git a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
index 0062f426d7..29fa001185 100644
--- a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
+++ b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
@@ -16,13 +16,17 @@
#include "../UnwantedInteractionBlocker.h"
#include <android-base/silent_death_test.h>
+#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <gui/constants.h>
#include <linux/input.h>
#include <thread>
+#include "ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter.h"
#include "TestInputListener.h"
+using ::testing::AllOf;
+
namespace android {
constexpr int32_t DEVICE_ID = 3;
@@ -30,6 +34,8 @@ constexpr int32_t X_RESOLUTION = 11;
constexpr int32_t Y_RESOLUTION = 11;
constexpr int32_t MAJOR_RESOLUTION = 1;
+const nsecs_t RESAMPLE_PERIOD = ::ui::kResamplePeriod.InNanoseconds();
+
constexpr int POINTER_0_DOWN =
AMOTION_EVENT_ACTION_POINTER_DOWN | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
constexpr int POINTER_1_DOWN =
@@ -47,6 +53,27 @@ constexpr int MOVE = AMOTION_EVENT_ACTION_MOVE;
constexpr int UP = AMOTION_EVENT_ACTION_UP;
constexpr int CANCEL = AMOTION_EVENT_ACTION_CANCEL;
+constexpr int32_t FLAG_CANCELED = AMOTION_EVENT_FLAG_CANCELED;
+
+MATCHER_P(WithAction, action, "MotionEvent with specified action") {
+ bool result = true;
+ if (action == CANCEL) {
+ result &= (arg.flags & FLAG_CANCELED) != 0;
+ }
+ result &= arg.action == action;
+ *result_listener << "expected to receive " << MotionEvent::actionToString(action)
+ << " but received " << MotionEvent::actionToString(arg.action) << " instead.";
+ return result;
+}
+
+MATCHER_P(WithFlags, flags, "MotionEvent with specified flags") {
+ return arg.flags == flags;
+}
+
+static nsecs_t toNs(std::chrono::nanoseconds duration) {
+ return duration.count();
+}
+
struct PointerData {
float x;
float y;
@@ -252,7 +279,7 @@ TEST(CancelSuppressedPointersTest, NewlySuppressedPointerIsCanceled) {
/*newSuppressedPointerIds*/ {1});
ASSERT_EQ(2u, result.size());
assertArgs(result[0], POINTER_1_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}, {2, {7, 8, 9}}});
- ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags);
+ ASSERT_EQ(FLAG_CANCELED, result[0].flags);
assertArgs(result[1], MOVE, {{0, {1, 2, 3}}, {2, {7, 8, 9}}});
}
@@ -267,7 +294,7 @@ TEST(CancelSuppressedPointersTest, SingleSuppressedPointerIsCanceled) {
/*newSuppressedPointerIds*/ {0});
ASSERT_EQ(1u, result.size());
assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}});
- ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags);
+ ASSERT_EQ(FLAG_CANCELED, result[0].flags);
}
/**
@@ -282,7 +309,7 @@ TEST(CancelSuppressedPointersTest, SuppressedPointer1GoingUpIsCanceled) {
/*newSuppressedPointerIds*/ {1});
ASSERT_EQ(1u, result.size());
assertArgs(result[0], POINTER_1_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}, {2, {7, 8, 9}}});
- ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags);
+ ASSERT_EQ(FLAG_CANCELED, result[0].flags);
}
/**
@@ -297,7 +324,7 @@ TEST(CancelSuppressedPointersTest, SuppressedPointer0GoingUpIsCanceled) {
/*newSuppressedPointerIds*/ {0});
ASSERT_EQ(1u, result.size());
assertArgs(result[0], POINTER_0_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}});
- ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags);
+ ASSERT_EQ(FLAG_CANCELED, result[0].flags);
}
/**
@@ -312,7 +339,7 @@ TEST(CancelSuppressedPointersTest, TwoNewlySuppressedPointersAreBothCanceled) {
/*newSuppressedPointerIds*/ {0, 1});
ASSERT_EQ(1u, result.size());
assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}, {1, {4, 5, 6}}});
- ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags);
+ ASSERT_EQ(FLAG_CANCELED, result[0].flags);
}
/**
@@ -328,7 +355,7 @@ TEST(CancelSuppressedPointersTest, TwoPointersAreCanceledDuringPointerUp) {
/*newSuppressedPointerIds*/ {0, 1});
ASSERT_EQ(1u, result.size());
assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}});
- ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags);
+ ASSERT_EQ(FLAG_CANCELED, result[0].flags);
}
/**
@@ -384,7 +411,7 @@ TEST(GetTouchesTest, ConvertDownEvent) {
expected.reported_tool_type = ::ui::EventPointerType::kTouch;
expected.stylus_button = false;
- ASSERT_EQ(expected, touches[0]) << toString(touches[0]);
+ ASSERT_EQ(expected, touches[0]) << touches[0];
}
// --- UnwantedInteractionBlockerTest ---
@@ -569,6 +596,114 @@ TEST_F(UnwantedInteractionBlockerTest, DumpCanBeAccessedOnAnotherThread) {
dumpThread.join();
}
+/**
+ * Heuristic filter that's present in the palm rejection model blocks touches early if the size
+ * of the touch is large. This is an integration test that checks that this filter kicks in.
+ */
+TEST_F(UnwantedInteractionBlockerTest, HeuristicFilterWorks) {
+ mBlocker->notifyInputDevicesChanged({generateTestDeviceInfo()});
+ // Small touch down
+ NotifyMotionArgs args1 = generateMotionArgs(0 /*downTime*/, 0 /*eventTime*/, DOWN, {{1, 2, 3}});
+ mBlocker->notifyMotion(&args1);
+ mTestListener.assertNotifyMotionWasCalled(WithAction(DOWN));
+
+ // Large touch oval on the next move
+ NotifyMotionArgs args2 =
+ generateMotionArgs(0 /*downTime*/, RESAMPLE_PERIOD, MOVE, {{4, 5, 200}});
+ mBlocker->notifyMotion(&args2);
+ mTestListener.assertNotifyMotionWasCalled(WithAction(MOVE));
+
+ // Lift up the touch to force the model to decide on whether it's a palm
+ NotifyMotionArgs args3 =
+ generateMotionArgs(0 /*downTime*/, 2 * RESAMPLE_PERIOD, UP, {{4, 5, 200}});
+ mBlocker->notifyMotion(&args3);
+ mTestListener.assertNotifyMotionWasCalled(WithAction(CANCEL));
+}
+
+/**
+ * Send a stylus event that would have triggered the heuristic palm detector if it were a touch
+ * event. However, since it's a stylus event, it should propagate without being canceled through
+ * the blocker.
+ * This is similar to `HeuristicFilterWorks` test, but for stylus tool.
+ */
+TEST_F(UnwantedInteractionBlockerTest, StylusIsNotBlocked) {
+ InputDeviceInfo info = generateTestDeviceInfo();
+ info.addSource(AINPUT_SOURCE_STYLUS);
+ mBlocker->notifyInputDevicesChanged({info});
+ NotifyMotionArgs args1 = generateMotionArgs(0 /*downTime*/, 0 /*eventTime*/, DOWN, {{1, 2, 3}});
+ args1.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+ mBlocker->notifyMotion(&args1);
+ mTestListener.assertNotifyMotionWasCalled(WithAction(DOWN));
+
+ // Move the stylus, setting large TOUCH_MAJOR/TOUCH_MINOR dimensions
+ NotifyMotionArgs args2 =
+ generateMotionArgs(0 /*downTime*/, RESAMPLE_PERIOD, MOVE, {{4, 5, 200}});
+ args2.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+ mBlocker->notifyMotion(&args2);
+ mTestListener.assertNotifyMotionWasCalled(WithAction(MOVE));
+
+ // Lift up the stylus. If it were a touch event, this would force the model to decide on whether
+ // it's a palm.
+ NotifyMotionArgs args3 =
+ generateMotionArgs(0 /*downTime*/, 2 * RESAMPLE_PERIOD, UP, {{4, 5, 200}});
+ args3.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+ mBlocker->notifyMotion(&args3);
+ mTestListener.assertNotifyMotionWasCalled(WithAction(UP));
+}
+
+/**
+ * Send a mixed touch and stylus event.
+ * The touch event goes first, and is a palm. The stylus event goes down after.
+ * Stylus event should continue to work even after touch is detected as a palm.
+ */
+TEST_F(UnwantedInteractionBlockerTest, TouchIsBlockedWhenMixedWithStylus) {
+ InputDeviceInfo info = generateTestDeviceInfo();
+ info.addSource(AINPUT_SOURCE_STYLUS);
+ mBlocker->notifyInputDevicesChanged({info});
+
+ // Touch down
+ NotifyMotionArgs args1 = generateMotionArgs(0 /*downTime*/, 0 /*eventTime*/, DOWN, {{1, 2, 3}});
+ mBlocker->notifyMotion(&args1);
+ mTestListener.assertNotifyMotionWasCalled(WithAction(DOWN));
+
+ // Stylus pointer down
+ NotifyMotionArgs args2 = generateMotionArgs(0 /*downTime*/, RESAMPLE_PERIOD, POINTER_1_DOWN,
+ {{1, 2, 3}, {10, 20, 30}});
+ args2.pointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+ mBlocker->notifyMotion(&args2);
+ mTestListener.assertNotifyMotionWasCalled(WithAction(POINTER_1_DOWN));
+
+ // Large touch oval on the next finger move
+ NotifyMotionArgs args3 = generateMotionArgs(0 /*downTime*/, 2 * RESAMPLE_PERIOD, MOVE,
+ {{1, 2, 300}, {11, 21, 30}});
+ args3.pointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+ mBlocker->notifyMotion(&args3);
+ mTestListener.assertNotifyMotionWasCalled(WithAction(MOVE));
+
+ // Lift up the finger pointer. It should be canceled due to the heuristic filter.
+ NotifyMotionArgs args4 = generateMotionArgs(0 /*downTime*/, 3 * RESAMPLE_PERIOD, POINTER_0_UP,
+ {{1, 2, 300}, {11, 21, 30}});
+ args4.pointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+ mBlocker->notifyMotion(&args4);
+ mTestListener.assertNotifyMotionWasCalled(
+ AllOf(WithAction(POINTER_0_UP), WithFlags(FLAG_CANCELED)));
+
+ NotifyMotionArgs args5 =
+ generateMotionArgs(0 /*downTime*/, 4 * RESAMPLE_PERIOD, MOVE, {{12, 22, 30}});
+ args5.pointerProperties[0].id = args4.pointerProperties[1].id;
+ args5.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+ mBlocker->notifyMotion(&args5);
+ mTestListener.assertNotifyMotionWasCalled(WithAction(MOVE));
+
+ // Lift up the stylus pointer
+ NotifyMotionArgs args6 =
+ generateMotionArgs(0 /*downTime*/, 5 * RESAMPLE_PERIOD, UP, {{4, 5, 200}});
+ args6.pointerProperties[0].id = args4.pointerProperties[1].id;
+ args6.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+ mBlocker->notifyMotion(&args6);
+ mTestListener.assertNotifyMotionWasCalled(WithAction(UP));
+}
+
using UnwantedInteractionBlockerTestDeathTest = UnwantedInteractionBlockerTest;
/**
@@ -630,138 +765,138 @@ TEST_F(PalmRejectorTestDeathTest, InconsistentEventCausesACrash) {
*/
TEST_F(PalmRejectorTest, TwoPointersAreCanceled) {
std::vector<NotifyMotionArgs> argsList;
- constexpr nsecs_t downTime = 255955749837000;
+ const nsecs_t downTime = toNs(0ms);
mPalmRejector->processMotion(
generateMotionArgs(downTime, downTime, DOWN, {{1342.0, 613.0, 79.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955759313000, MOVE, {{1406.0, 650.0, 52.0}}));
+ generateMotionArgs(downTime, toNs(8ms), MOVE, {{1406.0, 650.0, 52.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955766361000, MOVE, {{1429.0, 672.0, 46.0}}));
+ generateMotionArgs(downTime, toNs(16ms), MOVE, {{1429.0, 672.0, 46.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955775989000, MOVE, {{1417.0, 685.0, 41.0}}));
+ generateMotionArgs(downTime, toNs(24ms), MOVE, {{1417.0, 685.0, 41.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955775989000, POINTER_1_DOWN,
+ generateMotionArgs(downTime, toNs(32ms), POINTER_1_DOWN,
{{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955783039000, MOVE,
+ generateMotionArgs(downTime, toNs(40ms), MOVE,
{{1414.0, 702.0, 41.0}, {1059.0, 731.0, 12.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955792536000, MOVE,
+ generateMotionArgs(downTime, toNs(48ms), MOVE,
{{1415.0, 719.0, 44.0}, {1060.0, 760.0, 11.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955799474000, MOVE,
+ generateMotionArgs(downTime, toNs(56ms), MOVE,
{{1421.0, 733.0, 42.0}, {1065.0, 769.0, 13.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955809177000, MOVE,
+ generateMotionArgs(downTime, toNs(64ms), MOVE,
{{1426.0, 742.0, 43.0}, {1068.0, 771.0, 13.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955816131000, MOVE,
+ generateMotionArgs(downTime, toNs(72ms), MOVE,
{{1430.0, 748.0, 45.0}, {1069.0, 772.0, 13.0}}));
argsList = mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955825907000, MOVE,
+ generateMotionArgs(downTime, toNs(80ms), MOVE,
{{1432.0, 750.0, 44.0}, {1069.0, 772.0, 13.0}}));
ASSERT_EQ(1u, argsList.size());
ASSERT_EQ(0 /* No FLAG_CANCELED */, argsList[0].flags);
argsList = mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955832736000, MOVE,
+ generateMotionArgs(downTime, toNs(88ms), MOVE,
{{1433.0, 751.0, 44.0}, {1070.0, 771.0, 13.0}}));
ASSERT_EQ(2u, argsList.size());
ASSERT_EQ(POINTER_0_UP, argsList[0].action);
- ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags);
+ ASSERT_EQ(FLAG_CANCELED, argsList[0].flags);
ASSERT_EQ(MOVE, argsList[1].action);
ASSERT_EQ(1u, argsList[1].pointerCount);
ASSERT_EQ(0, argsList[1].flags);
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955842432000, MOVE,
+ generateMotionArgs(downTime, toNs(96ms), MOVE,
{{1433.0, 751.0, 42.0}, {1071.0, 770.0, 13.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955849380000, MOVE,
+ generateMotionArgs(downTime, toNs(104ms), MOVE,
{{1433.0, 751.0, 45.0}, {1072.0, 769.0, 13.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955859046000, MOVE,
+ generateMotionArgs(downTime, toNs(112ms), MOVE,
{{1433.0, 751.0, 43.0}, {1072.0, 768.0, 13.0}}));
argsList = mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955869823000, MOVE,
+ generateMotionArgs(downTime, toNs(120ms), MOVE,
{{1433.0, 751.0, 45.0}, {1072.0, 767.0, 13.0}}));
ASSERT_EQ(1u, argsList.size());
ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, argsList[0].action);
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955875641000, MOVE,
+ generateMotionArgs(downTime, toNs(128ms), MOVE,
{{1433.0, 751.0, 43.0}, {1072.0, 766.0, 13.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955882693000, MOVE,
+ generateMotionArgs(downTime, toNs(136ms), MOVE,
{{1433.0, 750.0, 44.0}, {1072.0, 765.0, 13.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955892324000, MOVE,
+ generateMotionArgs(downTime, toNs(144ms), MOVE,
{{1433.0, 750.0, 42.0}, {1072.0, 763.0, 14.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955899425000, MOVE,
+ generateMotionArgs(downTime, toNs(152ms), MOVE,
{{1434.0, 750.0, 44.0}, {1073.0, 761.0, 14.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955909400000, MOVE,
+ generateMotionArgs(downTime, toNs(160ms), MOVE,
{{1435.0, 750.0, 43.0}, {1073.0, 759.0, 15.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955915885000, MOVE,
+ generateMotionArgs(downTime, toNs(168ms), MOVE,
{{1436.0, 750.0, 45.0}, {1074.0, 757.0, 15.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955925607000, MOVE,
+ generateMotionArgs(downTime, toNs(176ms), MOVE,
{{1436.0, 750.0, 44.0}, {1074.0, 755.0, 15.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955932580000, MOVE,
+ generateMotionArgs(downTime, toNs(184ms), MOVE,
{{1436.0, 750.0, 45.0}, {1074.0, 753.0, 15.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955942231000, MOVE,
+ generateMotionArgs(downTime, toNs(192ms), MOVE,
{{1436.0, 749.0, 44.0}, {1074.0, 751.0, 15.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955949204000, MOVE,
+ generateMotionArgs(downTime, toNs(200ms), MOVE,
{{1435.0, 748.0, 45.0}, {1074.0, 749.0, 15.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955959103000, MOVE,
+ generateMotionArgs(downTime, toNs(208ms), MOVE,
{{1434.0, 746.0, 44.0}, {1074.0, 747.0, 14.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955965884000, MOVE,
+ generateMotionArgs(downTime, toNs(216ms), MOVE,
{{1433.0, 744.0, 44.0}, {1075.0, 745.0, 14.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955975649000, MOVE,
+ generateMotionArgs(downTime, toNs(224ms), MOVE,
{{1431.0, 741.0, 43.0}, {1075.0, 742.0, 13.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955982537000, MOVE,
+ generateMotionArgs(downTime, toNs(232ms), MOVE,
{{1428.0, 738.0, 43.0}, {1076.0, 739.0, 12.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955992284000, MOVE,
+ generateMotionArgs(downTime, toNs(240ms), MOVE,
{{1400.0, 726.0, 54.0}, {1076.0, 739.0, 13.0}}));
argsList = mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955999348000, POINTER_1_UP,
+ generateMotionArgs(downTime, toNs(248ms), POINTER_1_UP,
{{1362.0, 716.0, 55.0}, {1076.0, 739.0, 13.0}}));
ASSERT_TRUE(argsList.empty());
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955999348000, MOVE, {{1362.0, 716.0, 55.0}}));
+ generateMotionArgs(downTime, toNs(256ms), MOVE, {{1362.0, 716.0, 55.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255956008885000, MOVE, {{1347.0, 707.0, 54.0}}));
+ generateMotionArgs(downTime, toNs(264ms), MOVE, {{1347.0, 707.0, 54.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255956015791000, MOVE, {{1340.0, 698.0, 54.0}}));
+ generateMotionArgs(downTime, toNs(272ms), MOVE, {{1340.0, 698.0, 54.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255956025804000, MOVE, {{1338.0, 694.0, 55.0}}));
+ generateMotionArgs(downTime, toNs(280ms), MOVE, {{1338.0, 694.0, 55.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255956032314000, MOVE, {{1336.0, 690.0, 53.0}}));
+ generateMotionArgs(downTime, toNs(288ms), MOVE, {{1336.0, 690.0, 53.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255956042329000, MOVE, {{1334.0, 685.0, 47.0}}));
+ generateMotionArgs(downTime, toNs(296ms), MOVE, {{1334.0, 685.0, 47.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255956048979000, MOVE, {{1333.0, 679.0, 46.0}}));
+ generateMotionArgs(downTime, toNs(304ms), MOVE, {{1333.0, 679.0, 46.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255956058813000, MOVE, {{1332.0, 672.0, 45.0}}));
+ generateMotionArgs(downTime, toNs(312ms), MOVE, {{1332.0, 672.0, 45.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255956065592000, MOVE, {{1333.0, 666.0, 40.0}}));
+ generateMotionArgs(downTime, toNs(320ms), MOVE, {{1333.0, 666.0, 40.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255956075276000, MOVE, {{1336.0, 661.0, 24.0}}));
+ generateMotionArgs(downTime, toNs(328ms), MOVE, {{1336.0, 661.0, 24.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255956082198000, MOVE, {{1338.0, 656.0, 16.0}}));
+ generateMotionArgs(downTime, toNs(336ms), MOVE, {{1338.0, 656.0, 16.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255956092059000, MOVE, {{1341.0, 649.0, 1.0}}));
+ generateMotionArgs(downTime, toNs(344ms), MOVE, {{1341.0, 649.0, 1.0}}));
argsList = mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255956098764000, UP, {{1341.0, 649.0, 1.0}}));
+ generateMotionArgs(downTime, toNs(352ms), UP, {{1341.0, 649.0, 1.0}}));
ASSERT_TRUE(argsList.empty());
}
@@ -845,7 +980,7 @@ TEST_F(PalmRejectorFakeFilterTest, OneOfTwoPointersIsCanceled) {
ASSERT_EQ(2u, argsList.size());
// First event - cancel pointer 1
ASSERT_EQ(POINTER_1_UP, argsList[0].action);
- ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags);
+ ASSERT_EQ(FLAG_CANCELED, argsList[0].flags);
// Second event - send MOVE for the remaining pointer
ASSERT_EQ(MOVE, argsList[1].action);
ASSERT_EQ(0, argsList[1].flags);
@@ -886,7 +1021,7 @@ TEST_F(PalmRejectorFakeFilterTest, NewDownEventAfterCancel) {
// Cancel all
ASSERT_EQ(CANCEL, argsList[0].action);
ASSERT_EQ(2u, argsList[0].pointerCount);
- ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags);
+ ASSERT_EQ(FLAG_CANCELED, argsList[0].flags);
// Future move events are ignored
argsList = mPalmRejector->processMotion(
@@ -932,7 +1067,7 @@ TEST_F(PalmRejectorFakeFilterTest, TwoPointersCanceledWhenOnePointerGoesUp) {
{{1414.0, 702.0, 41.0}, {1059.0, 731.0, 12.0}}));
ASSERT_EQ(1u, argsList.size());
ASSERT_EQ(CANCEL, argsList[0].action) << MotionEvent::actionToString(argsList[0].action);
- ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags);
+ ASSERT_EQ(FLAG_CANCELED, argsList[0].flags);
// Future move events should not go to the listener.
argsList = mPalmRejector->processMotion(
@@ -966,7 +1101,7 @@ TEST_F(PalmRejectorFakeFilterTest, CancelTwoPointers) {
{{1417.0, 685.0, 41.0}, {1060, 700, 10.0}}));
ASSERT_EQ(2u, argsList.size());
ASSERT_EQ(POINTER_1_UP, argsList[0].action);
- ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags);
+ ASSERT_EQ(FLAG_CANCELED, argsList[0].flags);
ASSERT_EQ(MOVE, argsList[1].action) << MotionEvent::actionToString(argsList[1].action);
ASSERT_EQ(0, argsList[1].flags);
diff --git a/services/sensorservice/AidlSensorHalWrapper.cpp b/services/sensorservice/AidlSensorHalWrapper.cpp
index 4d1de96caa..f67c610550 100644
--- a/services/sensorservice/AidlSensorHalWrapper.cpp
+++ b/services/sensorservice/AidlSensorHalWrapper.cpp
@@ -726,7 +726,7 @@ status_t AidlSensorHalWrapper::registerDirectChannel(const sensors_direct_mem_t
.type = type,
.format = format,
.size = static_cast<int32_t>(memory->size),
- .memoryHandle = makeToAidl(memory->handle),
+ .memoryHandle = dupToAidl(memory->handle),
};
return convertToStatus(mSensors->registerDirectChannel(mem, channelHandle));
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 948692bd47..e0a4f034cb 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -1328,6 +1328,7 @@ Vector<Sensor> SensorService::getSensorList(const String16& opPackageName) {
mSensors.getUserDebugSensors() : mSensors.getUserSensors();
Vector<Sensor> accessibleSensorList;
+ resetTargetSdkVersionCache(opPackageName);
bool isCapped = isRateCappedBasedOnPermission(opPackageName);
for (size_t i = 0; i < initialSensorList.size(); i++) {
Sensor sensor = initialSensorList[i];
@@ -1367,6 +1368,7 @@ sp<ISensorEventConnection> SensorService::createSensorEventConnection(const Stri
if (requestedMode != NORMAL && requestedMode != DATA_INJECTION) {
return nullptr;
}
+ resetTargetSdkVersionCache(opPackageName);
Mutex::Autolock _l(mLock);
// To create a client in DATA_INJECTION mode to inject data, SensorService should already be
@@ -1402,6 +1404,7 @@ int SensorService::isDataInjectionEnabled() {
sp<ISensorEventConnection> SensorService::createSensorDirectConnection(
const String16& opPackageName, uint32_t size, int32_t type, int32_t format,
const native_handle *resource) {
+ resetTargetSdkVersionCache(opPackageName);
ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
// No new direct connections are allowed when sensor privacy is enabled
@@ -1643,14 +1646,6 @@ void SensorService::cleanupConnection(SensorEventConnection* c) {
checkWakeLockStateLocked(&connLock);
}
- {
- Mutex::Autolock packageLock(sPackageTargetVersionLock);
- auto iter = sPackageTargetVersion.find(c->mOpPackageName);
- if (iter != sPackageTargetVersion.end()) {
- sPackageTargetVersion.erase(iter);
- }
- }
-
SensorDevice& dev(SensorDevice::getInstance());
dev.notifyConnectionDestroyed(c);
}
@@ -2091,6 +2086,14 @@ int SensorService::getTargetSdkVersion(const String16& opPackageName) {
return targetSdkVersion;
}
+void SensorService::resetTargetSdkVersionCache(const String16& opPackageName) {
+ Mutex::Autolock packageLock(sPackageTargetVersionLock);
+ auto iter = sPackageTargetVersion.find(opPackageName);
+ if (iter != sPackageTargetVersion.end()) {
+ sPackageTargetVersion.erase(iter);
+ }
+}
+
void SensorService::checkWakeLockState() {
ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
checkWakeLockStateLocked(&connLock);
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 234dc9cd7d..4ba3c51985 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -377,6 +377,7 @@ private:
const String16& opPackageName);
static bool hasPermissionForSensor(const Sensor& sensor);
static int getTargetSdkVersion(const String16& opPackageName);
+ static void resetTargetSdkVersionCache(const String16& opPackageName);
// SensorService acquires a partial wakelock for delivering events from wake up sensors. This
// method checks whether all the events from these wake up sensors have been delivered to the
// corresponding applications, if yes the wakelock is released.
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
index 16cb41b024..5e84be1841 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
@@ -56,6 +56,9 @@ public:
// similar requests if needed.
virtual void createClientCompositionCache(uint32_t cacheSize) = 0;
+ // Sends the brightness setting to HWC
+ virtual void applyDisplayBrightness(const bool applyImmediately) = 0;
+
protected:
~Display() = default;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index db2fd1b500..2203639b1a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -27,6 +27,7 @@
#include <compositionengine/LayerFE.h>
#include <renderengine/LayerSettings.h>
#include <ui/Fence.h>
+#include <ui/FenceTime.h>
#include <ui/GraphicTypes.h>
#include <ui/LayerStack.h>
#include <ui/Region.h>
@@ -311,6 +312,8 @@ protected:
const Region& flashRegion,
std::vector<LayerFE::LayerSettings>& clientCompositionLayers) = 0;
virtual void setExpensiveRenderingExpected(bool enabled) = 0;
+ virtual void setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) = 0;
+ virtual bool isPowerHintSessionEnabled() = 0;
virtual void cacheClientCompositionRequests(uint32_t cacheSize) = 0;
virtual bool canPredictCompositionStrategy(const CompositionRefreshArgs&) = 0;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index 61a0e6a356..33a10a36a7 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -72,6 +72,7 @@ public:
const compositionengine::DisplayColorProfileCreationArgs&) override;
void createRenderSurface(const compositionengine::RenderSurfaceCreationArgs&) override;
void createClientCompositionCache(uint32_t cacheSize) override;
+ void applyDisplayBrightness(const bool applyImmediately) override;
// Internal helpers used by chooseCompositionStrategy()
using ChangedTypes = android::HWComposer::DeviceRequestedChanges::ChangedTypes;
@@ -89,6 +90,8 @@ public:
std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(const sp<LayerFE>&) const;
private:
+ bool isPowerHintSessionEnabled() override;
+ void setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) override;
DisplayId mId;
bool mIsDisconnected = false;
Hwc2::PowerAdvisor* mPowerAdvisor = nullptr;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 31c51e6a8d..df721cdc89 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -140,6 +140,8 @@ protected:
std::vector<LayerFE*> &outLayerFEs) override;
void appendRegionFlashRequests(const Region&, std::vector<LayerFE::LayerSettings>&) override;
void setExpensiveRenderingExpected(bool enabled) override;
+ void setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) override;
+ bool isPowerHintSessionEnabled() override;
void dumpBase(std::string&) const;
// Implemented by the final implementation for the final state it uses.
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
index e65aa7393c..8e4e9af48c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
@@ -115,7 +115,7 @@ public:
// Renders the cached set with the supplied output composition state.
void render(renderengine::RenderEngine& re, TexturePool& texturePool,
- const OutputCompositionState& outputState);
+ const OutputCompositionState& outputState, bool deviceHandlesColorTransform);
void dump(std::string& result) const;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
index 92cc484211..f934cb20a0 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
@@ -106,7 +106,8 @@ public:
// Renders the newest cached sets with the supplied output composition state
void renderCachedSets(const OutputCompositionState& outputState,
- std::optional<std::chrono::steady_clock::time_point> renderDeadline);
+ std::optional<std::chrono::steady_clock::time_point> renderDeadline,
+ bool deviceHandlesColorTransform);
void setTexturePoolEnabled(bool enabled) { mTexturePool.setEnabled(enabled); }
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
index b7ebca60fd..c968df708f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
@@ -65,7 +65,8 @@ public:
// Rendering a pending cached set is optional: if the renderDeadline is not far enough in the
// future then the planner may opt to skip rendering the cached set.
void renderCachedSets(const OutputCompositionState& outputState,
- std::optional<std::chrono::steady_clock::time_point> renderDeadline);
+ std::optional<std::chrono::steady_clock::time_point> renderDeadline,
+ bool deviceHandlesColorTransform);
void setTexturePoolEnabled(bool enabled) { mFlattener.setTexturePoolEnabled(enabled); }
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
index 72e6f3bdbb..7e99ec2f5a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
@@ -41,6 +41,7 @@ public:
MOCK_METHOD1(createDisplayColorProfile, void(const DisplayColorProfileCreationArgs&));
MOCK_METHOD1(createRenderSurface, void(const RenderSurfaceCreationArgs&));
MOCK_METHOD1(createClientCompositionCache, void(uint32_t));
+ MOCK_METHOD1(applyDisplayBrightness, void(const bool));
MOCK_METHOD1(setPredictCompositionStrategy, void(bool));
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index cb9fbad8dd..2a04949cff 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -133,6 +133,8 @@ public:
MOCK_METHOD1(canPredictCompositionStrategy, bool(const CompositionRefreshArgs&));
MOCK_METHOD1(setPredictCompositionStrategy, void(bool));
MOCK_METHOD1(setTreat170mAsSrgb, void(bool));
+ MOCK_METHOD(void, setHintSessionGpuFence, (std::unique_ptr<FenceTime> && gpuFence));
+ MOCK_METHOD(bool, isPowerHintSessionEnabled, ());
};
} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index b79b46b6ee..1ec6449ed0 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -203,23 +203,16 @@ void Display::setReleasedLayers(const compositionengine::CompositionRefreshArgs&
setReleasedLayers(std::move(releasedLayers));
}
-void Display::beginFrame() {
- Output::beginFrame();
-
- // If we don't have a HWC display, then we are done.
- const auto halDisplayId = HalDisplayId::tryCast(mId);
- if (!halDisplayId) {
- return;
- }
-
+void Display::applyDisplayBrightness(const bool applyImmediately) {
auto& hwc = getCompositionEngine().getHwComposer();
+ const auto halDisplayId = HalDisplayId::tryCast(*getDisplayId());
if (const auto physicalDisplayId = PhysicalDisplayId::tryCast(*halDisplayId);
physicalDisplayId && getState().displayBrightness) {
const status_t result =
hwc.setDisplayBrightness(*physicalDisplayId, *getState().displayBrightness,
getState().displayBrightnessNits,
Hwc2::Composer::DisplayBrightnessOptions{
- .applyImmediately = false})
+ .applyImmediately = applyImmediately})
.get();
ALOGE_IF(result != NO_ERROR, "setDisplayBrightness failed for %s: %d, (%s)",
getName().c_str(), result, strerror(-result));
@@ -228,6 +221,18 @@ void Display::beginFrame() {
editState().displayBrightness.reset();
}
+void Display::beginFrame() {
+ Output::beginFrame();
+
+ // If we don't have a HWC display, then we are done.
+ const auto halDisplayId = HalDisplayId::tryCast(mId);
+ if (!halDisplayId) {
+ return;
+ }
+
+ applyDisplayBrightness(false);
+}
+
bool Display::chooseCompositionStrategy(
std::optional<android::HWComposer::DeviceRequestedChanges>* outChanges) {
ATRACE_CALL();
@@ -243,11 +248,14 @@ bool Display::chooseCompositionStrategy(
return false;
}
+ const nsecs_t startTime = systemTime();
+
// Get any composition changes requested by the HWC device, and apply them.
std::optional<android::HWComposer::DeviceRequestedChanges> changes;
auto& hwc = getCompositionEngine().getHwComposer();
+ const bool requiresClientComposition = anyLayersRequireClientComposition();
if (status_t result =
- hwc.getDeviceCompositionChanges(*halDisplayId, anyLayersRequireClientComposition(),
+ hwc.getDeviceCompositionChanges(*halDisplayId, requiresClientComposition,
getState().earliestPresentTime,
getState().previousPresentFence,
getState().expectedPresentTime, outChanges);
@@ -257,6 +265,11 @@ bool Display::chooseCompositionStrategy(
return false;
}
+ if (isPowerHintSessionEnabled()) {
+ mPowerAdvisor->setHwcValidateTiming(mId, startTime, systemTime());
+ mPowerAdvisor->setRequiresClientComposition(mId, requiresClientComposition);
+ }
+
return true;
}
@@ -356,9 +369,24 @@ compositionengine::Output::FrameFences Display::presentAndGetFrameFences() {
}
auto& hwc = getCompositionEngine().getHwComposer();
+
+ const nsecs_t startTime = systemTime();
+
+ if (isPowerHintSessionEnabled()) {
+ if (!getCompositionEngine().getHwComposer().getComposer()->isSupported(
+ Hwc2::Composer::OptionalFeature::ExpectedPresentTime) &&
+ getState().previousPresentFence->getSignalTime() != Fence::SIGNAL_TIME_PENDING) {
+ mPowerAdvisor->setHwcPresentDelayedTime(mId, getState().earliestPresentTime);
+ }
+ }
+
hwc.presentAndGetReleaseFences(*halDisplayIdOpt, getState().earliestPresentTime,
getState().previousPresentFence);
+ if (isPowerHintSessionEnabled()) {
+ mPowerAdvisor->setHwcPresentTiming(mId, startTime, systemTime());
+ }
+
fences.presentFence = hwc.getPresentFence(*halDisplayIdOpt);
// TODO(b/121291683): Change HWComposer call to return entire map
@@ -384,6 +412,14 @@ void Display::setExpensiveRenderingExpected(bool enabled) {
}
}
+bool Display::isPowerHintSessionEnabled() {
+ return mPowerAdvisor != nullptr && mPowerAdvisor->usePowerHintSession();
+}
+
+void Display::setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) {
+ mPowerAdvisor->setGpuFenceTime(mId, std::move(gpuFence));
+}
+
void Display::finishFrame(const compositionengine::CompositionRefreshArgs& refreshArgs,
GpuCompositionResult&& result) {
// We only need to actually compose the display if:
@@ -396,6 +432,13 @@ void Display::finishFrame(const compositionengine::CompositionRefreshArgs& refre
}
impl::Output::finishFrame(refreshArgs, std::move(result));
+
+ if (isPowerHintSessionEnabled()) {
+ auto& hwc = getCompositionEngine().getHwComposer();
+ if (auto halDisplayId = HalDisplayId::tryCast(mId)) {
+ mPowerAdvisor->setSkippedValidate(mId, hwc.getValidateSkipped(*halDisplayId));
+ }
+ }
}
} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index c3385a8a8b..b724daa8ce 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -586,8 +586,29 @@ void Output::ensureOutputLayerIfVisible(sp<compositionengine::LayerFE>& layerFE,
// Remove the transparent area from the visible region
if (!layerFEState->isOpaque) {
if (tr.preserveRects()) {
- // transform the transparent region
- transparentRegion = tr.transform(layerFEState->transparentRegionHint);
+ // Clip the transparent region to geomLayerBounds first
+ // The transparent region may be influenced by applications, for
+ // instance, by overriding ViewGroup#gatherTransparentRegion with a
+ // custom view. Once the layer stack -> display mapping is known, we
+ // must guard against very wrong inputs to prevent underflow or
+ // overflow errors. We do this here by constraining the transparent
+ // region to be within the pre-transform layer bounds, since the
+ // layer bounds are expected to play nicely with the full
+ // transform.
+ const Region clippedTransparentRegionHint =
+ layerFEState->transparentRegionHint.intersect(
+ Rect(layerFEState->geomLayerBounds));
+
+ if (clippedTransparentRegionHint.isEmpty()) {
+ if (!layerFEState->transparentRegionHint.isEmpty()) {
+ ALOGD("Layer: %s had an out of bounds transparent region",
+ layerFE->getDebugName());
+ layerFEState->transparentRegionHint.dump("transparentRegionHint");
+ }
+ transparentRegion.clear();
+ } else {
+ transparentRegion = tr.transform(clippedTransparentRegionHint);
+ }
} else {
// transformation too complex, can't do the
// transparent region optimization.
@@ -1099,6 +1120,10 @@ void Output::finishFrame(const CompositionRefreshArgs& refreshArgs, GpuCompositi
return;
}
+ if (isPowerHintSessionEnabled()) {
+ // get fence end time to know when gpu is complete in display
+ setHintSessionGpuFence(std::make_unique<FenceTime>(new Fence(dup(optReadyFence->get()))));
+ }
// swap buffers (presentation)
mRenderSurface->queueBuffer(std::move(*optReadyFence));
}
@@ -1218,7 +1243,8 @@ std::optional<base::unique_fd> Output::composeSurfaces(
ATRACE_NAME("ClientCompositionCacheHit");
outputCompositionState.reusedClientComposition = true;
setExpensiveRenderingExpected(false);
- return base::unique_fd();
+ // b/239944175 pass the fence associated with the buffer.
+ return base::unique_fd(std::move(fd));
}
ATRACE_NAME("ClientCompositionCacheMiss");
mClientCompositionRequestCache->add(tex->getBuffer()->getId(), clientCompositionDisplay,
@@ -1403,6 +1429,14 @@ void Output::setExpensiveRenderingExpected(bool) {
// The base class does nothing with this call.
}
+void Output::setHintSessionGpuFence(std::unique_ptr<FenceTime>&&) {
+ // The base class does nothing with this call.
+}
+
+bool Output::isPowerHintSessionEnabled() {
+ return false;
+}
+
void Output::postFramebuffer() {
ATRACE_CALL();
ALOGV(__FUNCTION__);
@@ -1460,7 +1494,8 @@ void Output::postFramebuffer() {
void Output::renderCachedSets(const CompositionRefreshArgs& refreshArgs) {
if (mPlanner) {
- mPlanner->renderCachedSets(getState(), refreshArgs.scheduledFrameTime);
+ mPlanner->renderCachedSets(getState(), refreshArgs.scheduledFrameTime,
+ getState().usesDeviceComposition || getSkipColorTransform());
}
}
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index 641b806aec..d6f02ee42a 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -159,7 +159,8 @@ void CachedSet::updateAge(std::chrono::steady_clock::time_point now) {
}
void CachedSet::render(renderengine::RenderEngine& renderEngine, TexturePool& texturePool,
- const OutputCompositionState& outputState) {
+ const OutputCompositionState& outputState,
+ bool deviceHandlesColorTransform) {
ATRACE_CALL();
const Rect& viewport = outputState.layerStackSpace.getContent();
const ui::Dataspace& outputDataspace = outputState.dataspace;
@@ -170,6 +171,8 @@ void CachedSet::render(renderengine::RenderEngine& renderEngine, TexturePool& te
.physicalDisplay = outputState.framebufferSpace.getContent(),
.clip = viewport,
.outputDataspace = outputDataspace,
+ .colorTransform = outputState.colorTransformMatrix,
+ .deviceHandlesColorTransform = deviceHandlesColorTransform,
.orientation = orientation,
.targetLuminanceNits = outputState.displayBrightnessNits,
};
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index 1062b700dd..9175dd01a1 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -99,7 +99,8 @@ NonBufferHash Flattener::flattenLayers(const std::vector<const LayerState*>& lay
void Flattener::renderCachedSets(
const OutputCompositionState& outputState,
- std::optional<std::chrono::steady_clock::time_point> renderDeadline) {
+ std::optional<std::chrono::steady_clock::time_point> renderDeadline,
+ bool deviceHandlesColorTransform) {
ATRACE_CALL();
if (!mNewCachedSet) {
@@ -136,7 +137,7 @@ void Flattener::renderCachedSets(
}
}
- mNewCachedSet->render(mRenderEngine, mTexturePool, outputState);
+ mNewCachedSet->render(mRenderEngine, mTexturePool, outputState, deviceHandlesColorTransform);
}
void Flattener::dumpLayers(std::string& result) const {
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
index c8413eb8bc..54133d92b0 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -201,11 +201,11 @@ void Planner::reportFinalPlan(
finalPlan);
}
-void Planner::renderCachedSets(
- const OutputCompositionState& outputState,
- std::optional<std::chrono::steady_clock::time_point> renderDeadline) {
+void Planner::renderCachedSets(const OutputCompositionState& outputState,
+ std::optional<std::chrono::steady_clock::time_point> renderDeadline,
+ bool deviceHandlesColorTransform) {
ATRACE_CALL();
- mFlattener.renderCachedSets(outputState, renderDeadline);
+ mFlattener.renderCachedSets(outputState, renderDeadline, deviceHandlesColorTransform);
}
void Planner::dump(const Vector<String16>& args, std::string& result) {
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 0e5a7b6a99..344fea3331 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -169,6 +169,7 @@ struct DisplayTestCommon : public testing::Test {
EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine));
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mPowerAdvisor, usePowerHintSession()).WillRepeatedly(Return(false));
}
DisplayCreationArgs getDisplayCreationArgsForPhysicalDisplay() {
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 9b12b08063..d7704a893d 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -137,6 +137,7 @@ public:
MOCK_METHOD(bool, hasDisplayIdleTimerCapability, (PhysicalDisplayId), (const, override));
MOCK_METHOD(Hwc2::AidlTransform, getPhysicalDisplayOrientation, (PhysicalDisplayId),
(const, override));
+ MOCK_METHOD(bool, getValidateSkipped, (HalDisplayId), (const, override));
};
} // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
index 50adcfb827..c8bd5e436c 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
@@ -38,11 +38,33 @@ public:
MOCK_METHOD(bool, usePowerHintSession, (), (override));
MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override));
- MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDurationNanos), (override));
- MOCK_METHOD(void, sendActualWorkDuration, (int64_t actualDurationNanos, nsecs_t timestamp),
- (override));
+ MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDuration), (override));
+ MOCK_METHOD(void, sendActualWorkDuration, (), (override));
+ MOCK_METHOD(void, sendPredictedWorkDuration, (), (override));
MOCK_METHOD(void, enablePowerHint, (bool enabled), (override));
MOCK_METHOD(bool, startPowerHintSession, (const std::vector<int32_t>& threadIds), (override));
+ MOCK_METHOD(void, setGpuFenceTime,
+ (DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime), (override));
+ MOCK_METHOD(void, setHwcValidateTiming,
+ (DisplayId displayId, nsecs_t valiateStartTime, nsecs_t validateEndTime),
+ (override));
+ MOCK_METHOD(void, setHwcPresentTiming,
+ (DisplayId displayId, nsecs_t presentStartTime, nsecs_t presentEndTime),
+ (override));
+ MOCK_METHOD(void, setSkippedValidate, (DisplayId displayId, bool skipped), (override));
+ MOCK_METHOD(void, setRequiresClientComposition,
+ (DisplayId displayId, bool requiresClientComposition), (override));
+ MOCK_METHOD(void, setExpectedPresentTime, (nsecs_t expectedPresentTime), (override));
+ MOCK_METHOD(void, setSfPresentTiming, (nsecs_t presentFenceTime, nsecs_t presentEndTime),
+ (override));
+ MOCK_METHOD(void, setHwcPresentDelayedTime,
+ (DisplayId displayId,
+ std::chrono::steady_clock::time_point earliestFrameStartTime));
+ MOCK_METHOD(void, setFrameDelay, (nsecs_t frameDelayDuration), (override));
+ MOCK_METHOD(void, setCommitStart, (nsecs_t commitStartTime), (override));
+ MOCK_METHOD(void, setCompositeEnd, (nsecs_t compositeEndtime), (override));
+ MOCK_METHOD(void, setDisplays, (std::vector<DisplayId> & displayIds), (override));
+ MOCK_METHOD(void, setTotalFrameTargetWorkDuration, (int64_t targetDuration), (override));
};
} // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 063726b5e2..cf12890310 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -1505,6 +1505,8 @@ struct OutputEnsureOutputLayerIfVisibleTest : public testing::Test {
static const Region kTransparentRegionHint;
static const Region kTransparentRegionHintTwo;
static const Region kTransparentRegionHintTwo90Rotation;
+ static const Region kTransparentRegionHintNegative;
+ static const Region kTransparentRegionHintNegativeIntersectsBounds;
StrictMock<OutputPartialMock> mOutput;
LayerFESet mGeomSnapshots;
@@ -1528,6 +1530,10 @@ const Region OutputEnsureOutputLayerIfVisibleTest::kTransparentRegionHintTwo =
Region(Rect(25, 20, 50, 75));
const Region OutputEnsureOutputLayerIfVisibleTest::kTransparentRegionHintTwo90Rotation =
Region(Rect(125, 25, 180, 50));
+const Region OutputEnsureOutputLayerIfVisibleTest::kTransparentRegionHintNegative =
+ Region(Rect(INT32_MIN, INT32_MIN, INT32_MIN + 100, INT32_MIN + 200));
+const Region OutputEnsureOutputLayerIfVisibleTest::kTransparentRegionHintNegativeIntersectsBounds =
+ Region(Rect(INT32_MIN, INT32_MIN, 100, 100));
TEST_F(OutputEnsureOutputLayerIfVisibleTest, performsGeomLatchBeforeCheckingIfLayerIncluded) {
EXPECT_CALL(mOutput, includesLayer(sp<LayerFE>(mLayer.layerFE))).WillOnce(Return(false));
@@ -1997,6 +2003,41 @@ TEST_F(OutputEnsureOutputLayerIfVisibleTest, blockingRegionIsInOutputSpace) {
RegionEq(kTransparentRegionHintTwo90Rotation));
}
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, transparentRegionExcludesOutputLayer) {
+ mLayer.layerFEState.isOpaque = false;
+ mLayer.layerFEState.contentDirty = true;
+ mLayer.layerFEState.geomLayerBounds = kFullBoundsNoRotation.bounds().toFloatRect();
+ mLayer.layerFEState.transparentRegionHint = kFullBoundsNoRotation;
+
+ EXPECT_CALL(mOutput, ensureOutputLayer(_, _)).Times(0);
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, transparentRegionIgnoredWhenOutsideBounds) {
+ mLayer.layerFEState.isOpaque = false;
+ mLayer.layerFEState.contentDirty = true;
+ mLayer.layerFEState.geomLayerBounds = kFullBoundsNoRotation.bounds().toFloatRect();
+ mLayer.layerFEState.transparentRegionHint = kTransparentRegionHintNegative;
+
+ EXPECT_CALL(mOutput, ensureOutputLayer(_, _)).Times(0);
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, transparentRegionClipsWhenOutsideBounds) {
+ mLayer.layerFEState.isOpaque = false;
+ mLayer.layerFEState.contentDirty = true;
+ mLayer.layerFEState.compositionType =
+ aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION;
+ mLayer.layerFEState.transparentRegionHint = kTransparentRegionHintNegativeIntersectsBounds;
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer.layerFE)))
+ .WillOnce(Return(&mLayer.outputLayer));
+ ensureOutputLayerIfVisible();
+
+ // Check that the blocking region clips an out-of-bounds transparent region.
+ EXPECT_THAT(mLayer.outputLayerState.outputSpaceBlockingRegionHint,
+ RegionEq(kTransparentRegionHint));
+}
+
/*
* Output::present()
*/
@@ -3319,6 +3360,9 @@ struct OutputComposeSurfacesTest : public testing::Test {
MOCK_METHOD2(appendRegionFlashRequests,
void(const Region&, std::vector<LayerFE::LayerSettings>&));
MOCK_METHOD1(setExpensiveRenderingExpected, void(bool));
+ MOCK_METHOD(void, setHintSessionGpuFence, (std::unique_ptr<FenceTime> && gpuFence),
+ (override));
+ MOCK_METHOD(bool, isPowerHintSessionEnabled, (), (override));
};
OutputComposeSurfacesTest() {
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index 8a99e4e2e8..0e9db369e8 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -376,7 +376,7 @@ TEST_F(CachedSetTest, renderUnsecureOutput) {
.WillOnce(Return(clientCompList2));
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
mOutputState.isSecure = false;
- cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
+ cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true);
expectReadyBuffer(cachedSet);
EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace());
@@ -429,7 +429,7 @@ TEST_F(CachedSetTest, renderSecureOutput) {
.WillOnce(Return(clientCompList2));
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
mOutputState.isSecure = true;
- cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
+ cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true);
expectReadyBuffer(cachedSet);
EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace());
@@ -477,7 +477,58 @@ TEST_F(CachedSetTest, renderWhitePoint) {
.WillOnce(Return(clientCompList2));
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
mOutputState.isSecure = true;
- cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
+ cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true);
+ expectReadyBuffer(cachedSet);
+
+ EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace());
+ EXPECT_EQ(Rect(kOutputSize.width, kOutputSize.height), cachedSet.getTextureBounds());
+
+ // Now check that appending a new cached set properly cleans up RenderEngine resources.
+ CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+ cachedSet.append(CachedSet(layer3));
+}
+
+TEST_F(CachedSetTest, renderWhitePointNoColorTransform) {
+ // Skip the 0th layer to ensure that the bounding box of the layers is offset from (0, 0)
+ // This is a duplicate of the "renderWhitePoint" test, but setting "deviceHandlesColorTransform"
+ // to false, in the render call.
+
+ CachedSet::Layer& layer1 = *mTestLayers[1]->cachedSetLayer.get();
+ sp<mock::LayerFE> layerFE1 = mTestLayers[1]->layerFE;
+ CachedSet::Layer& layer2 = *mTestLayers[2]->cachedSetLayer.get();
+ sp<mock::LayerFE> layerFE2 = mTestLayers[2]->layerFE;
+
+ CachedSet cachedSet(layer1);
+ cachedSet.append(CachedSet(layer2));
+
+ std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1;
+ clientCompList1.push_back({});
+
+ std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2;
+ clientCompList2.push_back({});
+
+ mOutputState.displayBrightnessNits = 400.f;
+
+ const auto drawLayers =
+ [&](const renderengine::DisplaySettings& displaySettings,
+ const std::vector<renderengine::LayerSettings>&,
+ const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
+ EXPECT_EQ(mOutputState.displayBrightnessNits, displaySettings.targetLuminanceNits);
+ return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+ };
+
+ EXPECT_CALL(*layerFE1,
+ prepareClientCompositionList(ClientCompositionTargetSettingsWhitePointEq(
+ mOutputState.displayBrightnessNits)))
+ .WillOnce(Return(clientCompList1));
+ EXPECT_CALL(*layerFE2,
+ prepareClientCompositionList(ClientCompositionTargetSettingsWhitePointEq(
+ mOutputState.displayBrightnessNits)))
+ .WillOnce(Return(clientCompList2));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
+ mOutputState.isSecure = true;
+ cachedSet.render(mRenderEngine, mTexturePool, mOutputState, false);
expectReadyBuffer(cachedSet);
EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace());
@@ -527,7 +578,7 @@ TEST_F(CachedSetTest, rendersWithOffsetFramebufferContent) {
EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1));
EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
- cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
+ cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true);
expectReadyBuffer(cachedSet);
EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace());
@@ -767,7 +818,7 @@ TEST_F(CachedSetTest, addHolePunch) {
};
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
- cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
+ cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true);
}
TEST_F(CachedSetTest, addHolePunch_noBuffer) {
@@ -829,7 +880,7 @@ TEST_F(CachedSetTest, addHolePunch_noBuffer) {
};
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
- cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
+ cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true);
}
TEST_F(CachedSetTest, append_removesHolePunch) {
@@ -969,7 +1020,7 @@ TEST_F(CachedSetTest, addBlur) {
};
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
- cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
+ cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true);
}
} // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index 50e3a288cb..96021ec104 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -159,13 +159,13 @@ void FlattenerTest::initializeFlattener(const std::vector<const LayerState*>& la
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
// same geometry, update the internal layer stack
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
}
void FlattenerTest::expectAllLayersFlattened(const std::vector<const LayerState*>& layers) {
@@ -177,7 +177,7 @@ void FlattenerTest::expectAllLayersFlattened(const std::vector<const LayerState*
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
for (const auto layer : layers) {
EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
@@ -187,7 +187,7 @@ void FlattenerTest::expectAllLayersFlattened(const std::vector<const LayerState*
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
const auto buffer = layers[0]->getOutputLayer()->getState().overrideInfo.buffer;
EXPECT_NE(nullptr, buffer);
@@ -222,7 +222,7 @@ TEST_F(FlattenerTest, flattenLayers_ActiveLayersAreNotFlattened) {
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
}
TEST_F(FlattenerTest, flattenLayers_ActiveLayersWithLowFpsAreFlattened) {
@@ -284,7 +284,7 @@ TEST_F(FlattenerTest, flattenLayers_FlattenedLayersStayFlattenWhenNoUpdate) {
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -389,7 +389,7 @@ TEST_F(FlattenerTest, flattenLayers_addLayerToFlattenedCauseReset) {
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
EXPECT_EQ(nullptr, overrideBuffer1);
EXPECT_EQ(nullptr, overrideBuffer2);
@@ -428,7 +428,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateToFlatten) {
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
EXPECT_EQ(nullptr, overrideBuffer1);
EXPECT_EQ(nullptr, overrideBuffer2);
@@ -437,7 +437,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateToFlatten) {
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
EXPECT_EQ(nullptr, overrideBuffer1);
EXPECT_NE(nullptr, overrideBuffer2);
@@ -452,7 +452,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateToFlatten) {
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
EXPECT_EQ(nullptr, overrideBuffer1);
EXPECT_NE(nullptr, overrideBuffer2);
@@ -461,7 +461,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateToFlatten) {
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -505,7 +505,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) {
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
EXPECT_EQ(nullptr, overrideBuffer1);
EXPECT_EQ(nullptr, overrideBuffer2);
@@ -521,7 +521,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) {
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
mOutputState.framebufferSpace.setOrientation(ui::ROTATION_90);
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -534,7 +534,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) {
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
mOutputState.framebufferSpace.setOrientation(ui::ROTATION_180);
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -550,7 +550,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) {
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -562,7 +562,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) {
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
mOutputState.framebufferSpace.setOrientation(ui::ROTATION_270);
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -603,7 +603,7 @@ TEST_F(FlattenerTest, flattenLayers_pipRequiresRoundedCorners) {
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
.WillOnce(Return(ByMove(
futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
// We've rendered a CachedSet, but we haven't merged it in.
EXPECT_EQ(nullptr, overrideBuffer1);
@@ -616,7 +616,7 @@ TEST_F(FlattenerTest, flattenLayers_pipRequiresRoundedCorners) {
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -669,7 +669,7 @@ TEST_F(FlattenerTest, flattenLayers_pip) {
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
.WillOnce(Return(ByMove(
futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
// We've rendered a CachedSet, but we haven't merged it in.
EXPECT_EQ(nullptr, overrideBuffer1);
@@ -682,7 +682,7 @@ TEST_F(FlattenerTest, flattenLayers_pip) {
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -743,7 +743,7 @@ TEST_F(FlattenerTest, flattenLayers_holePunchSingleLayer) {
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
.WillOnce(Return(ByMove(
futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
// We've rendered a CachedSet, but we haven't merged it in.
EXPECT_EQ(nullptr, overrideBuffer0);
@@ -753,7 +753,7 @@ TEST_F(FlattenerTest, flattenLayers_holePunchSingleLayer) {
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
EXPECT_NE(nullptr, overrideBuffer0); // got overridden
EXPECT_EQ(nullptr, overrideBuffer1); // did not
@@ -815,7 +815,7 @@ TEST_F(FlattenerTest, flattenLayers_holePunchSingleColorLayer) {
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
.WillOnce(Return(ByMove(
futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
// We've rendered a CachedSet, but we haven't merged it in.
EXPECT_EQ(nullptr, overrideBuffer0);
@@ -825,7 +825,7 @@ TEST_F(FlattenerTest, flattenLayers_holePunchSingleColorLayer) {
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
EXPECT_NE(nullptr, overrideBuffer0); // got overridden
EXPECT_EQ(nullptr, overrideBuffer1); // did not
@@ -871,7 +871,7 @@ TEST_F(FlattenerTest, flattenLayers_flattensBlurBehindRunIfFirstRun) {
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
for (const auto layer : layers) {
EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
@@ -881,7 +881,7 @@ TEST_F(FlattenerTest, flattenLayers_flattensBlurBehindRunIfFirstRun) {
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
EXPECT_EQ(nullptr, overrideBuffer3);
@@ -917,7 +917,7 @@ TEST_F(FlattenerTest, flattenLayers_doesNotFlattenBlurBehindRun) {
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
for (const auto layer : layers) {
EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
@@ -928,7 +928,7 @@ TEST_F(FlattenerTest, flattenLayers_doesNotFlattenBlurBehindRun) {
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
for (const auto layer : layers) {
EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
}
@@ -971,7 +971,7 @@ TEST_F(FlattenerTest, flattenLayers_flattenSkipsLayerWithBlurBehind) {
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
for (const auto layer : layers) {
EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
@@ -981,7 +981,7 @@ TEST_F(FlattenerTest, flattenLayers_flattenSkipsLayerWithBlurBehind) {
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
EXPECT_EQ(nullptr, overrideBuffer1);
EXPECT_EQ(nullptr, blurOverrideBuffer);
EXPECT_NE(nullptr, overrideBuffer3);
@@ -1020,7 +1020,7 @@ TEST_F(FlattenerTest, flattenLayers_whenBlurLayerIsChanging_appliesBlurToInactiv
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
const auto& cachedSet = mFlattener->getNewCachedSetForTesting();
ASSERT_NE(std::nullopt, cachedSet);
@@ -1034,7 +1034,7 @@ TEST_F(FlattenerTest, flattenLayers_whenBlurLayerIsChanging_appliesBlurToInactiv
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer2, overrideBuffer1);
EXPECT_EQ(nullptr, blurOverrideBuffer);
@@ -1063,7 +1063,7 @@ TEST_F(FlattenerTest, flattenLayers_renderCachedSets_doesNotRenderTwice) {
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
EXPECT_EQ(nullptr, overrideBuffer1);
EXPECT_EQ(nullptr, overrideBuffer2);
@@ -1071,12 +1071,12 @@ TEST_F(FlattenerTest, flattenLayers_renderCachedSets_doesNotRenderTwice) {
// Simulate attempting to render prior to merging the new cached set with the layer stack.
// Here we should not try to re-render.
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
// We provide the override buffer now that it's rendered
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer2, overrideBuffer1);
@@ -1120,7 +1120,8 @@ TEST_F(FlattenerRenderSchedulingTest, flattenLayers_renderCachedSets_defersUpToM
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
mFlattener->renderCachedSets(mOutputState,
std::chrono::steady_clock::now() -
- (kCachedSetRenderDuration + 10ms));
+ (kCachedSetRenderDuration + 10ms),
+ true);
}
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
@@ -1128,7 +1129,8 @@ TEST_F(FlattenerRenderSchedulingTest, flattenLayers_renderCachedSets_defersUpToM
futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
mFlattener->renderCachedSets(mOutputState,
std::chrono::steady_clock::now() -
- (kCachedSetRenderDuration + 10ms));
+ (kCachedSetRenderDuration + 10ms),
+ true);
}
TEST_F(FlattenerTest, flattenLayers_skipsBT601_625) {
@@ -1162,7 +1164,7 @@ TEST_F(FlattenerTest, flattenLayers_skipsBT601_625) {
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
.WillOnce(Return(ByMove(
futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
// We've rendered a CachedSet, but we haven't merged it in.
EXPECT_EQ(nullptr, overrideBuffer1);
@@ -1175,7 +1177,7 @@ TEST_F(FlattenerTest, flattenLayers_skipsBT601_625) {
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -1213,7 +1215,7 @@ TEST_F(FlattenerTest, flattenLayers_skipsHDR) {
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
.WillOnce(Return(ByMove(
futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
// We've rendered a CachedSet, but we haven't merged it in.
EXPECT_EQ(nullptr, overrideBuffer1);
@@ -1226,7 +1228,7 @@ TEST_F(FlattenerTest, flattenLayers_skipsHDR) {
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -1264,7 +1266,7 @@ TEST_F(FlattenerTest, flattenLayers_skipsHDR2) {
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
.WillOnce(Return(ByMove(
futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
// We've rendered a CachedSet, but we haven't merged it in.
EXPECT_EQ(nullptr, overrideBuffer1);
@@ -1277,7 +1279,7 @@ TEST_F(FlattenerTest, flattenLayers_skipsHDR2) {
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -1318,7 +1320,7 @@ TEST_F(FlattenerTest, flattenLayers_skipsColorLayers) {
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
.WillOnce(Return(ByMove(
futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
// We've rendered a CachedSet, but we haven't merged it in.
EXPECT_EQ(nullptr, overrideBuffer1);
@@ -1332,7 +1334,7 @@ TEST_F(FlattenerTest, flattenLayers_skipsColorLayers) {
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
EXPECT_EQ(nullptr, overrideBuffer1);
EXPECT_EQ(nullptr, overrideBuffer2);
@@ -1371,7 +1373,7 @@ TEST_F(FlattenerTest, flattenLayers_includes_DISPLAY_DECORATION) {
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
.WillOnce(Return(ByMove(
futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
// We've rendered a CachedSet, but we haven't merged it in.
EXPECT_EQ(nullptr, overrideBuffer1);
@@ -1384,7 +1386,7 @@ TEST_F(FlattenerTest, flattenLayers_includes_DISPLAY_DECORATION) {
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mOutputState, std::nullopt);
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index a915b615d9..26fbd55d6e 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -104,7 +104,7 @@ DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs& args)
mCompositionDisplay->getRenderSurface()->initialize();
- setPowerMode(args.initialPowerMode);
+ if (args.initialPowerMode.has_value()) setPowerMode(args.initialPowerMode.value());
// initialize the display orientation transform.
setProjection(ui::ROTATION_0, Rect::INVALID_RECT, Rect::INVALID_RECT);
@@ -173,20 +173,31 @@ auto DisplayDevice::getInputInfo() const -> InputInfo {
}
void DisplayDevice::setPowerMode(hal::PowerMode mode) {
+ if (mode == hal::PowerMode::OFF || mode == hal::PowerMode::ON) {
+ if (mStagedBrightness && mBrightness != *mStagedBrightness) {
+ getCompositionDisplay()->setNextBrightness(*mStagedBrightness);
+ mBrightness = *mStagedBrightness;
+ }
+ mStagedBrightness = std::nullopt;
+ getCompositionDisplay()->applyDisplayBrightness(true);
+ }
+
mPowerMode = mode;
- getCompositionDisplay()->setCompositionEnabled(mPowerMode != hal::PowerMode::OFF);
+
+ getCompositionDisplay()->setCompositionEnabled(mPowerMode.has_value() &&
+ *mPowerMode != hal::PowerMode::OFF);
}
void DisplayDevice::enableLayerCaching(bool enable) {
getCompositionDisplay()->setLayerCachingEnabled(enable);
}
-hal::PowerMode DisplayDevice::getPowerMode() const {
+std::optional<hal::PowerMode> DisplayDevice::getPowerMode() const {
return mPowerMode;
}
bool DisplayDevice::isPoweredOn() const {
- return mPowerMode != hal::PowerMode::OFF;
+ return mPowerMode && *mPowerMode != hal::PowerMode::OFF;
}
void DisplayDevice::setActiveMode(DisplayModeId id) {
@@ -211,6 +222,7 @@ status_t DisplayDevice::initiateModeChange(const ActiveModeInfo& info,
to_string(getId()).c_str());
return BAD_VALUE;
}
+ mNumModeSwitchesInPolicy++;
mUpcomingActiveMode = info;
ATRACE_INT(mActiveModeFPSHwcTrace.c_str(), info.mode->getFps().getIntValue());
return mHwComposer.setActiveModeWithConstraints(getPhysicalId(), info.mode->getHwcId(),
@@ -324,8 +336,10 @@ void DisplayDevice::stageBrightness(float brightness) {
}
void DisplayDevice::persistBrightness(bool needsComposite) {
- if (needsComposite && mStagedBrightness && mBrightness != *mStagedBrightness) {
- getCompositionDisplay()->setNextBrightness(*mStagedBrightness);
+ if (mStagedBrightness && mBrightness != *mStagedBrightness) {
+ if (needsComposite) {
+ getCompositionDisplay()->setNextBrightness(*mStagedBrightness);
+ }
mBrightness = *mStagedBrightness;
}
mStagedBrightness = std::nullopt;
@@ -372,7 +386,7 @@ void DisplayDevice::dump(std::string& result) const {
}
result += "\n powerMode="s;
- result += to_string(mPowerMode);
+ result += mPowerMode.has_value() ? to_string(mPowerMode.value()) : "OFF(reset)";
result += '\n';
if (mRefreshRateConfigs) {
@@ -537,6 +551,27 @@ void DisplayDevice::clearDesiredActiveModeState() {
mDesiredActiveModeChanged = false;
}
+status_t DisplayDevice::setRefreshRatePolicy(
+ const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy) {
+ const auto oldPolicy = mRefreshRateConfigs->getCurrentPolicy();
+ const status_t setPolicyResult = overridePolicy
+ ? mRefreshRateConfigs->setOverridePolicy(policy)
+ : mRefreshRateConfigs->setDisplayManagerPolicy(*policy);
+
+ if (setPolicyResult == OK) {
+ const int numModeChanges = mNumModeSwitchesInPolicy.exchange(0);
+
+ ALOGI("Display %s policy changed\n"
+ "Previous: {%s}\n"
+ "Current: {%s}\n"
+ "%d mode changes were performed under the previous policy",
+ to_string(getId()).c_str(), oldPolicy.toString().c_str(),
+ policy ? policy->toString().c_str() : "null", numModeChanges);
+ }
+
+ return setPolicyResult;
+}
+
std::atomic<int32_t> DisplayDeviceState::sNextSequenceId(1);
} // namespace android
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index d5d87b40de..fc24a9ce49 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -181,7 +181,7 @@ public:
/* ------------------------------------------------------------------------
* Display power mode management.
*/
- hardware::graphics::composer::hal::PowerMode getPowerMode() const;
+ std::optional<hardware::graphics::composer::hal::PowerMode> getPowerMode() const;
void setPowerMode(hardware::graphics::composer::hal::PowerMode mode);
bool isPoweredOn() const;
@@ -249,6 +249,10 @@ public:
nsecs_t getVsyncPeriodFromHWC() const;
nsecs_t getRefreshTimestamp() const;
+ status_t setRefreshRatePolicy(
+ const std::optional<scheduler::RefreshRateConfigs::Policy>& policy,
+ bool overridePolicy);
+
// release HWC resources (if any) for removable displays
void disconnect();
@@ -277,8 +281,8 @@ private:
static ui::Transform::RotationFlags sPrimaryDisplayRotationFlags;
- hardware::graphics::composer::hal::PowerMode mPowerMode =
- hardware::graphics::composer::hal::PowerMode::OFF;
+ // allow initial power mode as null.
+ std::optional<hardware::graphics::composer::hal::PowerMode> mPowerMode;
DisplayModePtr mActiveMode;
std::optional<float> mStagedBrightness = std::nullopt;
float mBrightness = -1.f;
@@ -303,6 +307,8 @@ private:
TracedOrdinal<bool> mDesiredActiveModeChanged
GUARDED_BY(mActiveModeLock) = {"DesiredActiveModeChanged", false};
ActiveModeInfo mUpcomingActiveMode GUARDED_BY(kMainThreadContext);
+
+ std::atomic_int mNumModeSwitchesInPolicy = 0;
};
struct DisplayDeviceState {
@@ -360,8 +366,7 @@ struct DisplayDeviceCreationArgs {
HdrCapabilities hdrCapabilities;
int32_t supportedPerFrameMetadata{0};
std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> hwcColorModes;
- hardware::graphics::composer::hal::PowerMode initialPowerMode{
- hardware::graphics::composer::hal::PowerMode::ON};
+ std::optional<hardware::graphics::composer::hal::PowerMode> initialPowerMode;
bool isPrimary{false};
DisplayModes supportedModes;
DisplayModeId activeModeId;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 0da8ecea85..a6aee1f2f5 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -738,6 +738,13 @@ ftl::Future<status_t> HWComposer::setDisplayBrightness(
});
}
+bool HWComposer::getValidateSkipped(HalDisplayId displayId) const {
+ if (mDisplayData.count(displayId) == 0) {
+ return false;
+ }
+ return mDisplayData.at(displayId).validateWasSkipped;
+}
+
status_t HWComposer::setBootDisplayMode(PhysicalDisplayId displayId,
hal::HWConfigId displayModeId) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 4c0ecd8502..92a8f30f1b 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -199,6 +199,9 @@ public:
PhysicalDisplayId, float brightness, float brightnessNits,
const Hwc2::Composer::DisplayBrightnessOptions&) = 0;
+ // Get whether the display skipped validation on the latest present
+ virtual bool getValidateSkipped(HalDisplayId displayId) const = 0;
+
// Events handling ---------------------------------------------------------
// Returns stable display ID (and display name on connection of new or previously disconnected
@@ -397,6 +400,8 @@ public:
status_t setActiveColorMode(PhysicalDisplayId, ui::ColorMode, ui::RenderIntent) override;
+ bool getValidateSkipped(HalDisplayId displayId) const override;
+
// Composer 2.4
ui::DisplayConnectionType getDisplayConnectionType(PhysicalDisplayId) const override;
bool isVsyncPeriodSwitchSupported(PhysicalDisplayId) const override;
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index b5678b49b8..a0350b717a 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -187,7 +187,7 @@ bool PowerAdvisor::supportsPowerHintSession() {
if (!mSupportsPowerHint.has_value()) {
std::lock_guard lock(mPowerHalMutex);
HalWrapper* const halWrapper = getPowerHal();
- mSupportsPowerHint = halWrapper->supportsPowerHintSession();
+ mSupportsPowerHint = halWrapper && halWrapper->supportsPowerHintSession();
}
return *mSupportsPowerHint;
}
@@ -196,7 +196,7 @@ bool PowerAdvisor::isPowerHintSessionRunning() {
return mPowerHintSessionRunning;
}
-void PowerAdvisor::setTargetWorkDuration(int64_t targetDurationNanos) {
+void PowerAdvisor::setTargetWorkDuration(int64_t targetDuration) {
if (!usePowerHintSession()) {
ALOGV("Power hint session target duration cannot be set, skipping");
return;
@@ -205,26 +205,45 @@ void PowerAdvisor::setTargetWorkDuration(int64_t targetDurationNanos) {
std::lock_guard lock(mPowerHalMutex);
HalWrapper* const halWrapper = getPowerHal();
if (halWrapper != nullptr) {
- halWrapper->setTargetWorkDuration(targetDurationNanos - kTargetSafetyMargin.count());
+ halWrapper->setTargetWorkDuration(targetDuration);
}
}
}
-void PowerAdvisor::sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timeStampNanos) {
+void PowerAdvisor::sendActualWorkDuration() {
if (!mBootFinished || !usePowerHintSession()) {
ALOGV("Actual work duration power hint cannot be sent, skipping");
return;
}
- {
+ const std::optional<nsecs_t> actualDuration = estimateWorkDuration(false);
+ if (actualDuration.has_value()) {
std::lock_guard lock(mPowerHalMutex);
HalWrapper* const halWrapper = getPowerHal();
if (halWrapper != nullptr) {
- halWrapper->sendActualWorkDuration(actualDurationNanos, timeStampNanos);
+ halWrapper->sendActualWorkDuration(*actualDuration + kTargetSafetyMargin.count(),
+ systemTime());
+ }
+ }
+}
+
+void PowerAdvisor::sendPredictedWorkDuration() {
+ if (!mBootFinished || !usePowerHintSession()) {
+ ALOGV("Actual work duration power hint cannot be sent, skipping");
+ return;
+ }
+
+ const std::optional<nsecs_t> predictedDuration = estimateWorkDuration(true);
+
+ if (predictedDuration.has_value()) {
+ std::lock_guard lock(mPowerHalMutex);
+ HalWrapper* const halWrapper = getPowerHal();
+ if (halWrapper != nullptr) {
+ halWrapper->sendActualWorkDuration(*predictedDuration + kTargetSafetyMargin.count(),
+ systemTime());
}
}
}
-// needs to be set after the flag is known but before PowerAdvisor enters onBootFinished
void PowerAdvisor::enablePowerHint(bool enabled) {
mPowerHintEnabled = enabled;
}
@@ -244,6 +263,302 @@ bool PowerAdvisor::startPowerHintSession(const std::vector<int32_t>& threadIds)
return mPowerHintSessionRunning;
}
+void PowerAdvisor::setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime) {
+ DisplayTimingData& displayData = mDisplayTimingData[displayId];
+ if (displayData.gpuEndFenceTime) {
+ nsecs_t signalTime = displayData.gpuEndFenceTime->getSignalTime();
+ if (signalTime != Fence::SIGNAL_TIME_INVALID && signalTime != Fence::SIGNAL_TIME_PENDING) {
+ for (auto&& [_, otherDisplayData] : mDisplayTimingData) {
+ // If the previous display started before us but ended after we should have
+ // started, then it likely delayed our start time and we must compensate for that.
+ // Displays finishing earlier should have already made their way through this call
+ // and swapped their timing into "lastValid" from "latest", so we check that here.
+ if (!otherDisplayData.lastValidGpuStartTime.has_value()) continue;
+ if ((*otherDisplayData.lastValidGpuStartTime < *displayData.gpuStartTime) &&
+ (*otherDisplayData.lastValidGpuEndTime > *displayData.gpuStartTime)) {
+ displayData.lastValidGpuStartTime = *otherDisplayData.lastValidGpuEndTime;
+ break;
+ }
+ }
+ displayData.lastValidGpuStartTime = displayData.gpuStartTime;
+ displayData.lastValidGpuEndTime = signalTime;
+ }
+ }
+ displayData.gpuEndFenceTime = std::move(fenceTime);
+ displayData.gpuStartTime = systemTime();
+}
+
+void PowerAdvisor::setHwcValidateTiming(DisplayId displayId, nsecs_t validateStartTime,
+ nsecs_t validateEndTime) {
+ DisplayTimingData& displayData = mDisplayTimingData[displayId];
+ displayData.hwcValidateStartTime = validateStartTime;
+ displayData.hwcValidateEndTime = validateEndTime;
+}
+
+void PowerAdvisor::setHwcPresentTiming(DisplayId displayId, nsecs_t presentStartTime,
+ nsecs_t presentEndTime) {
+ DisplayTimingData& displayData = mDisplayTimingData[displayId];
+ displayData.hwcPresentStartTime = presentStartTime;
+ displayData.hwcPresentEndTime = presentEndTime;
+}
+
+void PowerAdvisor::setSkippedValidate(DisplayId displayId, bool skipped) {
+ mDisplayTimingData[displayId].skippedValidate = skipped;
+}
+
+void PowerAdvisor::setRequiresClientComposition(DisplayId displayId,
+ bool requiresClientComposition) {
+ mDisplayTimingData[displayId].usedClientComposition = requiresClientComposition;
+}
+
+void PowerAdvisor::setExpectedPresentTime(nsecs_t expectedPresentTime) {
+ mExpectedPresentTimes.append(expectedPresentTime);
+}
+
+void PowerAdvisor::setSfPresentTiming(nsecs_t presentFenceTime, nsecs_t presentEndTime) {
+ mLastSfPresentEndTime = presentEndTime;
+ mLastPresentFenceTime = presentFenceTime;
+}
+
+void PowerAdvisor::setFrameDelay(nsecs_t frameDelayDuration) {
+ mFrameDelayDuration = frameDelayDuration;
+}
+
+void PowerAdvisor::setHwcPresentDelayedTime(
+ DisplayId displayId, std::chrono::steady_clock::time_point earliestFrameStartTime) {
+ mDisplayTimingData[displayId].hwcPresentDelayedTime =
+ (earliestFrameStartTime - std::chrono::steady_clock::now()).count() + systemTime();
+}
+
+void PowerAdvisor::setCommitStart(nsecs_t commitStartTime) {
+ mCommitStartTimes.append(commitStartTime);
+}
+
+void PowerAdvisor::setCompositeEnd(nsecs_t compositeEnd) {
+ mLastPostcompDuration = compositeEnd - mLastSfPresentEndTime;
+}
+
+void PowerAdvisor::setDisplays(std::vector<DisplayId>& displayIds) {
+ mDisplayIds = displayIds;
+}
+
+void PowerAdvisor::setTotalFrameTargetWorkDuration(nsecs_t targetDuration) {
+ mTotalFrameTargetDuration = targetDuration;
+}
+
+std::vector<DisplayId> PowerAdvisor::getOrderedDisplayIds(
+ std::optional<nsecs_t> DisplayTimingData::*sortBy) {
+ std::vector<DisplayId> sortedDisplays;
+ std::copy_if(mDisplayIds.begin(), mDisplayIds.end(), std::back_inserter(sortedDisplays),
+ [&](DisplayId id) {
+ return mDisplayTimingData.count(id) &&
+ (mDisplayTimingData[id].*sortBy).has_value();
+ });
+ std::sort(sortedDisplays.begin(), sortedDisplays.end(), [&](DisplayId idA, DisplayId idB) {
+ return *(mDisplayTimingData[idA].*sortBy) < *(mDisplayTimingData[idB].*sortBy);
+ });
+ return sortedDisplays;
+}
+
+std::optional<nsecs_t> PowerAdvisor::estimateWorkDuration(bool earlyHint) {
+ if (earlyHint && (!mExpectedPresentTimes.isFull() || !mCommitStartTimes.isFull())) {
+ return std::nullopt;
+ }
+
+ // Tracks when we finish presenting to hwc
+ nsecs_t estimatedEndTime = mCommitStartTimes[0];
+
+ // How long we spent this frame not doing anything, waiting for fences or vsync
+ nsecs_t idleDuration = 0;
+
+ // Most recent previous gpu end time in the current frame, probably from a prior display, used
+ // as the start time for the next gpu operation if it ran over time since it probably blocked
+ std::optional<nsecs_t> previousValidGpuEndTime;
+
+ // The currently estimated gpu end time for the frame,
+ // used to accumulate gpu time as we iterate over the active displays
+ std::optional<nsecs_t> estimatedGpuEndTime;
+
+ // If we're predicting at the start of the frame, we use last frame as our reference point
+ // If we're predicting at the end of the frame, we use the current frame as a reference point
+ nsecs_t referenceFrameStartTime = (earlyHint ? mCommitStartTimes[-1] : mCommitStartTimes[0]);
+
+ // When the prior frame should be presenting to the display
+ // If we're predicting at the start of the frame, we use last frame's expected present time
+ // If we're predicting at the end of the frame, the present fence time is already known
+ nsecs_t lastFramePresentTime = (earlyHint ? mExpectedPresentTimes[-1] : mLastPresentFenceTime);
+
+ // The timing info for the previously calculated display, if there was one
+ std::optional<DisplayTimeline> previousDisplayReferenceTiming;
+ std::vector<DisplayId>&& displayIds =
+ getOrderedDisplayIds(&DisplayTimingData::hwcPresentStartTime);
+ DisplayTimeline referenceTiming, estimatedTiming;
+
+ // Iterate over the displays that use hwc in the same order they are presented
+ for (DisplayId displayId : displayIds) {
+ if (mDisplayTimingData.count(displayId) == 0) {
+ continue;
+ }
+
+ auto& displayData = mDisplayTimingData.at(displayId);
+
+ // mLastPresentFenceTime should always be the time of the reference frame, since it will be
+ // the previous frame's present fence if called at the start, and current frame's if called
+ // at the end
+ referenceTiming = displayData.calculateDisplayTimeline(mLastPresentFenceTime);
+
+ // If this is the first display, include the duration before hwc present starts
+ if (!previousDisplayReferenceTiming.has_value()) {
+ estimatedEndTime += referenceTiming.hwcPresentStartTime - referenceFrameStartTime;
+ } else { // Otherwise add the time since last display's hwc present finished
+ estimatedEndTime += referenceTiming.hwcPresentStartTime -
+ previousDisplayReferenceTiming->hwcPresentEndTime;
+ }
+
+ // Late hint can re-use reference timing here since it's estimating its own reference frame
+ estimatedTiming = earlyHint
+ ? referenceTiming.estimateTimelineFromReference(lastFramePresentTime,
+ estimatedEndTime)
+ : referenceTiming;
+
+ // Update predicted present finish time with this display's present time
+ estimatedEndTime = estimatedTiming.hwcPresentEndTime;
+
+ // Track how long we spent waiting for the fence, can be excluded from the timing estimate
+ idleDuration += estimatedTiming.probablyWaitsForPresentFence
+ ? lastFramePresentTime - estimatedTiming.presentFenceWaitStartTime
+ : 0;
+
+ // Track how long we spent waiting to present, can be excluded from the timing estimate
+ idleDuration += earlyHint ? 0 : referenceTiming.hwcPresentDelayDuration;
+
+ // Estimate the reference frame's gpu timing
+ auto gpuTiming = displayData.estimateGpuTiming(previousValidGpuEndTime);
+ if (gpuTiming.has_value()) {
+ previousValidGpuEndTime = gpuTiming->startTime + gpuTiming->duration;
+
+ // Estimate the prediction frame's gpu end time from the reference frame
+ estimatedGpuEndTime =
+ std::max(estimatedTiming.hwcPresentStartTime, estimatedGpuEndTime.value_or(0)) +
+ gpuTiming->duration;
+ }
+ previousDisplayReferenceTiming = referenceTiming;
+ }
+ ATRACE_INT64("Idle duration", idleDuration);
+
+ nsecs_t estimatedFlingerEndTime = earlyHint ? estimatedEndTime : mLastSfPresentEndTime;
+
+ // Don't count time spent idly waiting in the estimate as we could do more work in that time
+ estimatedEndTime -= idleDuration;
+ estimatedFlingerEndTime -= idleDuration;
+
+ // We finish the frame when both present and the gpu are done, so wait for the later of the two
+ // Also add the frame delay duration since the target did not move while we were delayed
+ nsecs_t totalDuration = mFrameDelayDuration +
+ std::max(estimatedEndTime, estimatedGpuEndTime.value_or(0)) - mCommitStartTimes[0];
+
+ // We finish SurfaceFlinger when post-composition finishes, so add that in here
+ nsecs_t flingerDuration =
+ estimatedFlingerEndTime + mLastPostcompDuration - mCommitStartTimes[0];
+
+ // Combine the two timings into a single normalized one
+ nsecs_t combinedDuration = combineTimingEstimates(totalDuration, flingerDuration);
+
+ return std::make_optional(combinedDuration);
+}
+
+nsecs_t PowerAdvisor::combineTimingEstimates(nsecs_t totalDuration, nsecs_t flingerDuration) {
+ nsecs_t targetDuration;
+ {
+ std::lock_guard lock(mPowerHalMutex);
+ targetDuration = *getPowerHal()->getTargetWorkDuration();
+ }
+ if (!mTotalFrameTargetDuration.has_value()) return flingerDuration;
+
+ // Normalize total to the flinger target (vsync period) since that's how often we actually send
+ // hints
+ nsecs_t normalizedTotalDuration = (targetDuration * totalDuration) / *mTotalFrameTargetDuration;
+ return std::max(flingerDuration, normalizedTotalDuration);
+}
+
+PowerAdvisor::DisplayTimeline PowerAdvisor::DisplayTimeline::estimateTimelineFromReference(
+ nsecs_t fenceTime, nsecs_t displayStartTime) {
+ DisplayTimeline estimated;
+ estimated.hwcPresentStartTime = displayStartTime;
+
+ // We don't predict waiting for vsync alignment yet
+ estimated.hwcPresentDelayDuration = 0;
+
+ // How long we expect to run before we start waiting for the fence
+ // For now just re-use last frame's post-present duration and assume it will not change much
+ // Excludes time spent waiting for vsync since that's not going to be consistent
+ estimated.presentFenceWaitStartTime = estimated.hwcPresentStartTime +
+ (presentFenceWaitStartTime - (hwcPresentStartTime + hwcPresentDelayDuration));
+ estimated.probablyWaitsForPresentFence = fenceTime > estimated.presentFenceWaitStartTime;
+ estimated.hwcPresentEndTime = postPresentFenceHwcPresentDuration +
+ (estimated.probablyWaitsForPresentFence ? fenceTime
+ : estimated.presentFenceWaitStartTime);
+ return estimated;
+}
+
+PowerAdvisor::DisplayTimeline PowerAdvisor::DisplayTimingData::calculateDisplayTimeline(
+ nsecs_t fenceTime) {
+ DisplayTimeline timeline;
+ // How long between calling hwc present and trying to wait on the fence
+ const nsecs_t fenceWaitStartDelay =
+ (skippedValidate ? kFenceWaitStartDelaySkippedValidate : kFenceWaitStartDelayValidated)
+ .count();
+
+ // Did our reference frame wait for an appropriate vsync before calling into hwc
+ const bool waitedOnHwcPresentTime = hwcPresentDelayedTime.has_value() &&
+ *hwcPresentDelayedTime > *hwcPresentStartTime &&
+ *hwcPresentDelayedTime < *hwcPresentEndTime;
+
+ // Use validate start here if we skipped it because we did validate + present together
+ timeline.hwcPresentStartTime = skippedValidate ? *hwcValidateStartTime : *hwcPresentStartTime;
+
+ // Use validate end here if we skipped it because we did validate + present together
+ timeline.hwcPresentEndTime = skippedValidate ? *hwcValidateEndTime : *hwcPresentEndTime;
+
+ // How long hwc present was delayed waiting for the next appropriate vsync
+ timeline.hwcPresentDelayDuration =
+ (waitedOnHwcPresentTime ? *hwcPresentDelayedTime - *hwcPresentStartTime : 0);
+ // When we started waiting for the present fence after calling into hwc present
+ timeline.presentFenceWaitStartTime =
+ timeline.hwcPresentStartTime + timeline.hwcPresentDelayDuration + fenceWaitStartDelay;
+ timeline.probablyWaitsForPresentFence = fenceTime > timeline.presentFenceWaitStartTime &&
+ fenceTime < timeline.hwcPresentEndTime;
+
+ // How long we ran after we finished waiting for the fence but before hwc present finished
+ timeline.postPresentFenceHwcPresentDuration = timeline.hwcPresentEndTime -
+ (timeline.probablyWaitsForPresentFence ? fenceTime
+ : timeline.presentFenceWaitStartTime);
+ return timeline;
+}
+
+std::optional<PowerAdvisor::GpuTimeline> PowerAdvisor::DisplayTimingData::estimateGpuTiming(
+ std::optional<nsecs_t> previousEnd) {
+ if (!(usedClientComposition && lastValidGpuStartTime.has_value() && gpuEndFenceTime)) {
+ return std::nullopt;
+ }
+ const nsecs_t latestGpuStartTime = std::max(previousEnd.value_or(0), *gpuStartTime);
+ const nsecs_t latestGpuEndTime = gpuEndFenceTime->getSignalTime();
+ nsecs_t gpuDuration = 0;
+ if (latestGpuEndTime != Fence::SIGNAL_TIME_INVALID &&
+ latestGpuEndTime != Fence::SIGNAL_TIME_PENDING) {
+ // If we know how long the most recent gpu duration was, use that
+ gpuDuration = latestGpuEndTime - latestGpuStartTime;
+ } else if (lastValidGpuEndTime.has_value()) {
+ // If we don't have the fence data, use the most recent information we do have
+ gpuDuration = *lastValidGpuEndTime - *lastValidGpuStartTime;
+ if (latestGpuEndTime == Fence::SIGNAL_TIME_PENDING) {
+ // If pending but went over the previous duration, use current time as the end
+ gpuDuration = std::max(gpuDuration, systemTime() - latestGpuStartTime);
+ }
+ }
+ return GpuTimeline{.duration = gpuDuration, .startTime = latestGpuStartTime};
+}
+
class HidlPowerHalWrapper : public PowerAdvisor::HalWrapper {
public:
HidlPowerHalWrapper(sp<V1_3::IPower> powerHal) : mPowerHal(std::move(powerHal)) {}
@@ -325,6 +640,9 @@ AidlPowerHalWrapper::AidlPowerHalWrapper(sp<IPower> powerHal) : mPowerHal(std::m
}
mSupportsPowerHint = checkPowerHintSessionSupported();
+
+ // Currently set to 0 to disable rate limiter by default
+ mAllowedActualDeviation = base::GetIntProperty<nsecs_t>("debug.sf.allowed_actual_deviation", 0);
}
AidlPowerHalWrapper::~AidlPowerHalWrapper() {
@@ -332,7 +650,7 @@ AidlPowerHalWrapper::~AidlPowerHalWrapper() {
mPowerHintSession->close();
mPowerHintSession = nullptr;
}
-};
+}
std::unique_ptr<PowerAdvisor::HalWrapper> AidlPowerHalWrapper::connect() {
// This only waits if the service is actually declared
@@ -370,7 +688,7 @@ bool AidlPowerHalWrapper::notifyDisplayUpdateImminent() {
return ret.isOk();
}
-// only version 2+ of the aidl supports power hint sessions, hidl has no support
+// Only version 2+ of the aidl supports power hint sessions, hidl has no support
bool AidlPowerHalWrapper::supportsPowerHintSession() {
return mSupportsPowerHint;
}
@@ -424,30 +742,14 @@ bool AidlPowerHalWrapper::startPowerHintSession() {
return isPowerHintSessionRunning();
}
-bool AidlPowerHalWrapper::shouldSetTargetDuration(int64_t targetDurationNanos) {
- if (targetDurationNanos <= 0) {
- return false;
- }
- // report if the change in target from our last submission to now exceeds the threshold
- return abs(1.0 -
- static_cast<double>(mLastTargetDurationSent) /
- static_cast<double>(targetDurationNanos)) >= kAllowedTargetDeviationPercent;
-}
-
-void AidlPowerHalWrapper::setTargetWorkDuration(int64_t targetDurationNanos) {
+void AidlPowerHalWrapper::setTargetWorkDuration(int64_t targetDuration) {
ATRACE_CALL();
- mTargetDuration = targetDurationNanos;
- if (sTraceHintSessionData) ATRACE_INT64("Time target", targetDurationNanos);
- if (!sNormalizeTarget && isPowerHintSessionRunning() &&
- shouldSetTargetDuration(targetDurationNanos)) {
- if (mLastActualDurationSent.has_value()) {
- // update the error term here since we are actually sending an update to powerhal
- if (sTraceHintSessionData)
- ATRACE_INT64("Target error term", targetDurationNanos - *mLastActualDurationSent);
- }
- ALOGV("Sending target time: %" PRId64 "ns", targetDurationNanos);
- mLastTargetDurationSent = targetDurationNanos;
- auto ret = mPowerHintSession->updateTargetWorkDuration(targetDurationNanos);
+ mTargetDuration = targetDuration;
+ if (sTraceHintSessionData) ATRACE_INT64("Time target", targetDuration);
+ if (isPowerHintSessionRunning() && (targetDuration != mLastTargetDurationSent)) {
+ ALOGV("Sending target time: %" PRId64 "ns", targetDuration);
+ mLastTargetDurationSent = targetDuration;
+ auto ret = mPowerHintSession->updateTargetWorkDuration(targetDuration);
if (!ret.isOk()) {
ALOGW("Failed to set power hint target work duration with error: %s",
ret.exceptionMessage().c_str());
@@ -456,8 +758,8 @@ void AidlPowerHalWrapper::setTargetWorkDuration(int64_t targetDurationNanos) {
}
}
-bool AidlPowerHalWrapper::shouldReportActualDurationsNow() {
- // report if we have never reported before or are approaching a stale session
+bool AidlPowerHalWrapper::shouldReportActualDurations() {
+ // Report if we have never reported before or are approaching a stale session
if (!mLastActualDurationSent.has_value() ||
(systemTime() - mLastActualReportTimestamp) > kStaleTimeout.count()) {
return true;
@@ -466,65 +768,42 @@ bool AidlPowerHalWrapper::shouldReportActualDurationsNow() {
if (!mActualDuration.has_value()) {
return false;
}
-
- // duration of most recent timing
- const double mostRecentActualDuration = static_cast<double>(*mActualDuration);
- // duration of the last timing actually reported to the powerhal
- const double lastReportedActualDuration = static_cast<double>(*mLastActualDurationSent);
-
- // report if the change in duration from then to now exceeds the threshold
- return abs(1.0 - mostRecentActualDuration / lastReportedActualDuration) >=
- kAllowedActualDeviationPercent;
+ // Report if the change in actual duration exceeds the threshold
+ return abs(*mActualDuration - *mLastActualDurationSent) > mAllowedActualDeviation;
}
-void AidlPowerHalWrapper::sendActualWorkDuration(int64_t actualDurationNanos,
- nsecs_t timeStampNanos) {
+void AidlPowerHalWrapper::sendActualWorkDuration(int64_t actualDuration, nsecs_t timestamp) {
ATRACE_CALL();
- if (actualDurationNanos < 0 || !isPowerHintSessionRunning()) {
+ if (actualDuration < 0 || !isPowerHintSessionRunning()) {
ALOGV("Failed to send actual work duration, skipping");
return;
}
- nsecs_t reportedDuration = actualDurationNanos;
+ const nsecs_t reportedDuration = actualDuration;
- // normalize the sent values to a pre-set target
- if (sNormalizeTarget) {
- reportedDuration += mLastTargetDurationSent - mTargetDuration;
- } else {
- // when target duration change is within deviation and not updated, adjust the actual
- // duration proportionally based on the difference, e.g. if new target is 5ms longer than
- // last reported but actual duration is the same as last target, we want to report a smaller
- // actual work duration now to indicate that we are overshooting
- if (mLastTargetDurationSent != kDefaultTarget.count() && mTargetDuration != 0) {
- reportedDuration =
- static_cast<int64_t>(static_cast<long double>(mLastTargetDurationSent) /
- mTargetDuration * actualDurationNanos);
- mActualDuration = reportedDuration;
- }
- }
mActualDuration = reportedDuration;
WorkDuration duration;
duration.durationNanos = reportedDuration;
- duration.timeStampNanos = timeStampNanos;
+ duration.timeStampNanos = timestamp;
mPowerHintQueue.push_back(duration);
if (sTraceHintSessionData) {
- ATRACE_INT64("Measured duration", actualDurationNanos);
- ATRACE_INT64("Target error term", mTargetDuration - actualDurationNanos);
+ ATRACE_INT64("Measured duration", actualDuration);
+ ATRACE_INT64("Target error term", actualDuration - mTargetDuration);
ATRACE_INT64("Reported duration", reportedDuration);
ATRACE_INT64("Reported target", mLastTargetDurationSent);
- ATRACE_INT64("Reported target error term", mLastTargetDurationSent - reportedDuration);
+ ATRACE_INT64("Reported target error term", reportedDuration - mLastTargetDurationSent);
}
ALOGV("Sending actual work duration of: %" PRId64 " on reported target: %" PRId64
" with error: %" PRId64,
- reportedDuration, mLastTargetDurationSent, mLastTargetDurationSent - reportedDuration);
+ reportedDuration, mLastTargetDurationSent, reportedDuration - mLastTargetDurationSent);
// This rate limiter queues similar duration reports to the powerhal into
// batches to avoid excessive binder calls. The criteria to send a given batch
// are outlined in shouldReportActualDurationsNow()
- if (shouldReportActualDurationsNow()) {
+ if (shouldReportActualDurations()) {
ALOGV("Sending hint update batch");
mLastActualReportTimestamp = systemTime();
auto ret = mPowerHintSession->reportActualWorkDuration(mPowerHintQueue);
@@ -534,8 +813,8 @@ void AidlPowerHalWrapper::sendActualWorkDuration(int64_t actualDurationNanos,
mShouldReconnectHal = true;
}
mPowerHintQueue.clear();
- // we save the non-normalized value here to detect % changes
- mLastActualDurationSent = reportedDuration;
+ // We save the actual duration here for rate limiting
+ mLastActualDurationSent = actualDuration;
}
}
@@ -551,76 +830,73 @@ std::optional<int64_t> AidlPowerHalWrapper::getTargetWorkDuration() {
return mTargetDuration;
}
+void AidlPowerHalWrapper::setAllowedActualDeviation(nsecs_t allowedDeviation) {
+ mAllowedActualDeviation = allowedDeviation;
+}
+
const bool AidlPowerHalWrapper::sTraceHintSessionData =
base::GetBoolProperty(std::string("debug.sf.trace_hint_sessions"), false);
-const bool AidlPowerHalWrapper::sNormalizeTarget =
- base::GetBoolProperty(std::string("debug.sf.normalize_hint_session_durations"), false);
-
PowerAdvisor::HalWrapper* PowerAdvisor::getPowerHal() {
- static std::unique_ptr<HalWrapper> sHalWrapper = nullptr;
- static bool sHasHal = true;
-
- if (!sHasHal) {
+ if (!mHasHal) {
return nullptr;
}
- // grab old hint session values before we destroy any existing wrapper
+ // Grab old hint session values before we destroy any existing wrapper
std::vector<int32_t> oldPowerHintSessionThreadIds;
std::optional<int64_t> oldTargetWorkDuration;
- if (sHalWrapper != nullptr) {
- oldPowerHintSessionThreadIds = sHalWrapper->getPowerHintSessionThreadIds();
- oldTargetWorkDuration = sHalWrapper->getTargetWorkDuration();
+ if (mHalWrapper != nullptr) {
+ oldPowerHintSessionThreadIds = mHalWrapper->getPowerHintSessionThreadIds();
+ oldTargetWorkDuration = mHalWrapper->getTargetWorkDuration();
}
// If we used to have a HAL, but it stopped responding, attempt to reconnect
if (mReconnectPowerHal) {
- sHalWrapper = nullptr;
+ mHalWrapper = nullptr;
mReconnectPowerHal = false;
}
- if (sHalWrapper != nullptr) {
- auto wrapper = sHalWrapper.get();
- // if the wrapper is fine, return it, but if it indicates a reconnect, remake it
+ if (mHalWrapper != nullptr) {
+ auto wrapper = mHalWrapper.get();
+ // If the wrapper is fine, return it, but if it indicates a reconnect, remake it
if (!wrapper->shouldReconnectHAL()) {
return wrapper;
}
ALOGD("Reconnecting Power HAL");
- sHalWrapper = nullptr;
+ mHalWrapper = nullptr;
}
- // at this point, we know for sure there is no running session
+ // At this point, we know for sure there is no running session
mPowerHintSessionRunning = false;
// First attempt to connect to the AIDL Power HAL
- sHalWrapper = AidlPowerHalWrapper::connect();
+ mHalWrapper = AidlPowerHalWrapper::connect();
// If that didn't succeed, attempt to connect to the HIDL Power HAL
- if (sHalWrapper == nullptr) {
- sHalWrapper = HidlPowerHalWrapper::connect();
+ if (mHalWrapper == nullptr) {
+ mHalWrapper = HidlPowerHalWrapper::connect();
} else {
ALOGD("Successfully connecting AIDL Power HAL");
- // if AIDL, pass on any existing hint session values
- // thread ids always safe to set
- sHalWrapper->setPowerHintSessionThreadIds(oldPowerHintSessionThreadIds);
- // only set duration and start if duration is defined
+ // If AIDL, pass on any existing hint session values
+ mHalWrapper->setPowerHintSessionThreadIds(oldPowerHintSessionThreadIds);
+ // Only set duration and start if duration is defined
if (oldTargetWorkDuration.has_value()) {
- sHalWrapper->setTargetWorkDuration(*oldTargetWorkDuration);
- // only start if possible to run and both threadids and duration are defined
+ mHalWrapper->setTargetWorkDuration(*oldTargetWorkDuration);
+ // Only start if possible to run and both threadids and duration are defined
if (usePowerHintSession() && !oldPowerHintSessionThreadIds.empty()) {
- mPowerHintSessionRunning = sHalWrapper->startPowerHintSession();
+ mPowerHintSessionRunning = mHalWrapper->startPowerHintSession();
}
}
}
// If we make it to this point and still don't have a HAL, it's unlikely we
// will, so stop trying
- if (sHalWrapper == nullptr) {
- sHasHal = false;
+ if (mHalWrapper == nullptr) {
+ mHasHal = false;
}
- return sHalWrapper.get();
+ return mHalWrapper.get();
}
} // namespace impl
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index 7c10e19e11..6e25f787d7 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -18,11 +18,15 @@
#include <atomic>
#include <chrono>
+#include <unordered_map>
#include <unordered_set>
+#include <ui/DisplayId.h>
+#include <ui/FenceTime.h>
#include <utils/Mutex.h>
#include <android/hardware/power/IPower.h>
+#include <compositionengine/impl/OutputCompositionState.h>
#include <ui/DisplayIdentification.h>
#include "../Scheduler/OneShotTimer.h"
@@ -44,13 +48,50 @@ public:
virtual void setExpensiveRenderingExpected(DisplayId displayId, bool expected) = 0;
virtual bool isUsingExpensiveRendering() = 0;
virtual void notifyDisplayUpdateImminent() = 0;
+ // Checks both if it supports and if it's enabled
virtual bool usePowerHintSession() = 0;
virtual bool supportsPowerHintSession() = 0;
virtual bool isPowerHintSessionRunning() = 0;
- virtual void setTargetWorkDuration(int64_t targetDurationNanos) = 0;
- virtual void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timestamp) = 0;
+ // Sends a power hint that updates to the target work duration for the frame
+ virtual void setTargetWorkDuration(nsecs_t targetDuration) = 0;
+ // Sends a power hint for the actual known work duration at the end of the frame
+ virtual void sendActualWorkDuration() = 0;
+ // Sends a power hint for the upcoming frame predicted from previous frame timing
+ virtual void sendPredictedWorkDuration() = 0;
+ // Sets whether the power hint session is enabled
virtual void enablePowerHint(bool enabled) = 0;
+ // Initializes the power hint session
virtual bool startPowerHintSession(const std::vector<int32_t>& threadIds) = 0;
+ // Provides PowerAdvisor with a copy of the gpu fence so it can determine the gpu end time
+ virtual void setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime) = 0;
+ // Reports the start and end times of a hwc validate call this frame for a given display
+ virtual void setHwcValidateTiming(DisplayId displayId, nsecs_t validateStartTime,
+ nsecs_t validateEndTime) = 0;
+ // Reports the start and end times of a hwc present call this frame for a given display
+ virtual void setHwcPresentTiming(DisplayId displayId, nsecs_t presentStartTime,
+ nsecs_t presentEndTime) = 0;
+ // Reports the expected time that the current frame will present to the display
+ virtual void setExpectedPresentTime(nsecs_t expectedPresentTime) = 0;
+ // Reports the most recent present fence time and end time once known
+ virtual void setSfPresentTiming(nsecs_t presentFenceTime, nsecs_t presentEndTime) = 0;
+ // Reports whether a display used client composition this frame
+ virtual void setRequiresClientComposition(DisplayId displayId,
+ bool requiresClientComposition) = 0;
+ // Reports whether a given display skipped validation this frame
+ virtual void setSkippedValidate(DisplayId displayId, bool skipped) = 0;
+ // Reports when a hwc present is delayed, and the time that it will resume
+ virtual void setHwcPresentDelayedTime(
+ DisplayId displayId, std::chrono::steady_clock::time_point earliestFrameStartTime) = 0;
+ // Reports the start delay for SurfaceFlinger this frame
+ virtual void setFrameDelay(nsecs_t frameDelayDuration) = 0;
+ // Reports the SurfaceFlinger commit start time this frame
+ virtual void setCommitStart(nsecs_t commitStartTime) = 0;
+ // Reports the SurfaceFlinger composite end time this frame
+ virtual void setCompositeEnd(nsecs_t compositeEndTime) = 0;
+ // Reports the list of the currently active displays
+ virtual void setDisplays(std::vector<DisplayId>& displayIds) = 0;
+ // Sets the target duration for the entire pipeline including the gpu
+ virtual void setTotalFrameTargetWorkDuration(nsecs_t targetDuration) = 0;
};
namespace impl {
@@ -70,12 +111,11 @@ public:
virtual void restartPowerHintSession() = 0;
virtual void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) = 0;
virtual bool startPowerHintSession() = 0;
- virtual void setTargetWorkDuration(int64_t targetDurationNanos) = 0;
- virtual void sendActualWorkDuration(int64_t actualDurationNanos,
- nsecs_t timeStampNanos) = 0;
+ virtual void setTargetWorkDuration(nsecs_t targetDuration) = 0;
+ virtual void sendActualWorkDuration(nsecs_t actualDuration, nsecs_t timestamp) = 0;
virtual bool shouldReconnectHAL() = 0;
virtual std::vector<int32_t> getPowerHintSessionThreadIds() = 0;
- virtual std::optional<int64_t> getTargetWorkDuration() = 0;
+ virtual std::optional<nsecs_t> getTargetWorkDuration() = 0;
};
PowerAdvisor(SurfaceFlinger& flinger);
@@ -89,26 +129,43 @@ public:
bool usePowerHintSession() override;
bool supportsPowerHintSession() override;
bool isPowerHintSessionRunning() override;
- void setTargetWorkDuration(int64_t targetDurationNanos) override;
- void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timestamp) override;
+ void setTargetWorkDuration(nsecs_t targetDuration) override;
+ void sendActualWorkDuration() override;
+ void sendPredictedWorkDuration() override;
void enablePowerHint(bool enabled) override;
bool startPowerHintSession(const std::vector<int32_t>& threadIds) override;
+ void setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime);
+ void setHwcValidateTiming(DisplayId displayId, nsecs_t valiateStartTime,
+ nsecs_t validateEndTime) override;
+ void setHwcPresentTiming(DisplayId displayId, nsecs_t presentStartTime,
+ nsecs_t presentEndTime) override;
+ void setSkippedValidate(DisplayId displayId, bool skipped) override;
+ void setRequiresClientComposition(DisplayId displayId, bool requiresClientComposition) override;
+ void setExpectedPresentTime(nsecs_t expectedPresentTime) override;
+ void setSfPresentTiming(nsecs_t presentFenceTime, nsecs_t presentEndTime) override;
+ void setHwcPresentDelayedTime(
+ DisplayId displayId,
+ std::chrono::steady_clock::time_point earliestFrameStartTime) override;
+
+ void setFrameDelay(nsecs_t frameDelayDuration) override;
+ void setCommitStart(nsecs_t commitStartTime) override;
+ void setCompositeEnd(nsecs_t compositeEndTime) override;
+ void setDisplays(std::vector<DisplayId>& displayIds) override;
+ void setTotalFrameTargetWorkDuration(nsecs_t targetDuration) override;
private:
+ friend class PowerAdvisorTest;
+
+ // Tracks if powerhal exists
+ bool mHasHal = true;
+ // Holds the hal wrapper for getPowerHal
+ std::unique_ptr<HalWrapper> mHalWrapper GUARDED_BY(mPowerHalMutex) = nullptr;
+
HalWrapper* getPowerHal() REQUIRES(mPowerHalMutex);
bool mReconnectPowerHal GUARDED_BY(mPowerHalMutex) = false;
std::mutex mPowerHalMutex;
std::atomic_bool mBootFinished = false;
- std::optional<bool> mPowerHintEnabled;
- std::optional<bool> mSupportsPowerHint;
- bool mPowerHintSessionRunning = false;
-
- // An adjustable safety margin which moves the "target" earlier to allow flinger to
- // go a bit over without dropping a frame, especially since we can't measure
- // the exact time HWC finishes composition so "actual" durations are measured
- // from the end of present() instead, which is a bit later.
- static constexpr const std::chrono::nanoseconds kTargetSafetyMargin = 2ms;
std::unordered_set<DisplayId> mExpensiveDisplays;
bool mNotifiedExpensiveRendering = false;
@@ -117,6 +174,110 @@ private:
std::atomic_bool mSendUpdateImminent = true;
std::atomic<nsecs_t> mLastScreenUpdatedTime = 0;
std::optional<scheduler::OneShotTimer> mScreenUpdateTimer;
+
+ // Higher-level timing data used for estimation
+ struct DisplayTimeline {
+ // The start of hwc present, or the start of validate if it happened there instead
+ nsecs_t hwcPresentStartTime = -1;
+ // The end of hwc present or validate, whichever one actually presented
+ nsecs_t hwcPresentEndTime = -1;
+ // How long the actual hwc present was delayed after hwcPresentStartTime
+ nsecs_t hwcPresentDelayDuration = 0;
+ // When we think we started waiting for the present fence after calling into hwc present and
+ // after potentially waiting for the earliest present time
+ nsecs_t presentFenceWaitStartTime = -1;
+ // How long we ran after we finished waiting for the fence but before hwc present finished
+ nsecs_t postPresentFenceHwcPresentDuration = 0;
+ // Are we likely to have waited for the present fence during composition
+ bool probablyWaitsForPresentFence = false;
+ // Estimate one frame's timeline from that of a previous frame
+ DisplayTimeline estimateTimelineFromReference(nsecs_t fenceTime, nsecs_t displayStartTime);
+ };
+
+ struct GpuTimeline {
+ nsecs_t duration = 0;
+ nsecs_t startTime = -1;
+ };
+
+ // Power hint session data recorded from the pipeline
+ struct DisplayTimingData {
+ std::unique_ptr<FenceTime> gpuEndFenceTime;
+ std::optional<nsecs_t> gpuStartTime;
+ std::optional<nsecs_t> lastValidGpuEndTime;
+ std::optional<nsecs_t> lastValidGpuStartTime;
+ std::optional<nsecs_t> hwcPresentStartTime;
+ std::optional<nsecs_t> hwcPresentEndTime;
+ std::optional<nsecs_t> hwcValidateStartTime;
+ std::optional<nsecs_t> hwcValidateEndTime;
+ std::optional<nsecs_t> hwcPresentDelayedTime;
+ bool usedClientComposition = false;
+ bool skippedValidate = false;
+ // Calculate high-level timing milestones from more granular display timing data
+ DisplayTimeline calculateDisplayTimeline(nsecs_t fenceTime);
+ // Estimate the gpu duration for a given display from previous gpu timing data
+ std::optional<GpuTimeline> estimateGpuTiming(std::optional<nsecs_t> previousEnd);
+ };
+
+ template <class T, size_t N>
+ class RingBuffer {
+ std::array<T, N> elements = {};
+ size_t mIndex = 0;
+ size_t numElements = 0;
+
+ public:
+ void append(T item) {
+ mIndex = (mIndex + 1) % N;
+ numElements = std::min(N, numElements + 1);
+ elements[mIndex] = item;
+ }
+ bool isFull() const { return numElements == N; }
+ // Allows access like [0] == current, [-1] = previous, etc..
+ T& operator[](int offset) {
+ size_t positiveOffset =
+ static_cast<size_t>((offset % static_cast<int>(N)) + static_cast<int>(N));
+ return elements[(mIndex + positiveOffset) % N];
+ }
+ };
+
+ // Filter and sort the display ids by a given property
+ std::vector<DisplayId> getOrderedDisplayIds(std::optional<nsecs_t> DisplayTimingData::*sortBy);
+ // Estimates a frame's total work duration including gpu time.
+ // Runs either at the beginning or end of a frame, using the most recent data available
+ std::optional<nsecs_t> estimateWorkDuration(bool earlyHint);
+ // There are two different targets and actual work durations we care about,
+ // this normalizes them together and takes the max of the two
+ nsecs_t combineTimingEstimates(nsecs_t totalDuration, nsecs_t flingerDuration);
+
+ std::unordered_map<DisplayId, DisplayTimingData> mDisplayTimingData;
+
+ // Current frame's delay
+ nsecs_t mFrameDelayDuration = 0;
+ // Last frame's post-composition duration
+ nsecs_t mLastPostcompDuration = 0;
+ // Buffer of recent commit start times
+ RingBuffer<nsecs_t, 2> mCommitStartTimes;
+ // Buffer of recent expected present times
+ RingBuffer<nsecs_t, 2> mExpectedPresentTimes;
+ // Most recent present fence time, set at the end of the frame once known
+ nsecs_t mLastPresentFenceTime = -1;
+ // Most recent present fence time, set at the end of the frame once known
+ nsecs_t mLastSfPresentEndTime = -1;
+ // Target for the entire pipeline including gpu
+ std::optional<nsecs_t> mTotalFrameTargetDuration;
+ // Updated list of display IDs
+ std::vector<DisplayId> mDisplayIds;
+
+ std::optional<bool> mPowerHintEnabled;
+ std::optional<bool> mSupportsPowerHint;
+ bool mPowerHintSessionRunning = false;
+
+ // An adjustable safety margin which pads the "actual" value sent to PowerHAL,
+ // encouraging more aggressive boosting to give SurfaceFlinger a larger margin for error
+ static constexpr const std::chrono::nanoseconds kTargetSafetyMargin = 1ms;
+
+ // How long we expect hwc to run after the present call until it waits for the fence
+ static constexpr const std::chrono::nanoseconds kFenceWaitStartDelayValidated = 150us;
+ static constexpr const std::chrono::nanoseconds kFenceWaitStartDelaySkippedValidate = 250us;
};
class AidlPowerHalWrapper : public PowerAdvisor::HalWrapper {
@@ -133,50 +294,50 @@ public:
void restartPowerHintSession() override;
void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) override;
bool startPowerHintSession() override;
- void setTargetWorkDuration(int64_t targetDurationNanos) override;
- void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timeStampNanos) override;
+ void setTargetWorkDuration(nsecs_t targetDuration) override;
+ void sendActualWorkDuration(nsecs_t actualDuration, nsecs_t timestamp) override;
bool shouldReconnectHAL() override;
std::vector<int32_t> getPowerHintSessionThreadIds() override;
- std::optional<int64_t> getTargetWorkDuration() override;
+ std::optional<nsecs_t> getTargetWorkDuration() override;
private:
+ friend class AidlPowerHalWrapperTest;
+
bool checkPowerHintSessionSupported();
void closePowerHintSession();
- bool shouldReportActualDurationsNow();
- bool shouldSetTargetDuration(int64_t targetDurationNanos);
+ bool shouldReportActualDurations();
+
+ // Used for testing
+ void setAllowedActualDeviation(nsecs_t);
const sp<hardware::power::IPower> mPowerHal = nullptr;
bool mHasExpensiveRendering = false;
bool mHasDisplayUpdateImminent = false;
// Used to indicate an error state and need for reconstruction
bool mShouldReconnectHal = false;
- // This is not thread safe, but is currently protected by mPowerHalMutex so it needs no lock
+
+ // Power hint session data
+
+ // Concurrent access for this is protected by mPowerHalMutex
sp<hardware::power::IPowerHintSession> mPowerHintSession = nullptr;
// Queue of actual durations saved to report
std::vector<hardware::power::WorkDuration> mPowerHintQueue;
- // The latest un-normalized values we have received for target and actual
- int64_t mTargetDuration = kDefaultTarget.count();
- std::optional<int64_t> mActualDuration;
+ // The latest values we have received for target and actual
+ nsecs_t mTargetDuration = kDefaultTarget.count();
+ std::optional<nsecs_t> mActualDuration;
// The list of thread ids, stored so we can restart the session from this class if needed
std::vector<int32_t> mPowerHintThreadIds;
- bool mSupportsPowerHint;
+ bool mSupportsPowerHint = false;
// Keep track of the last messages sent for rate limiter change detection
- std::optional<int64_t> mLastActualDurationSent;
- // timestamp of the last report we sent, used to avoid stale sessions
- int64_t mLastActualReportTimestamp = 0;
- int64_t mLastTargetDurationSent = kDefaultTarget.count();
- // Whether to normalize all the actual values as error terms relative to a constant target
- // This saves a binder call by not setting the target, and should not affect the pid values
- static const bool sNormalizeTarget;
+ std::optional<nsecs_t> mLastActualDurationSent;
+ // Timestamp of the last report we sent, used to avoid stale sessions
+ nsecs_t mLastActualReportTimestamp = 0;
+ nsecs_t mLastTargetDurationSent = kDefaultTarget.count();
+ // Max amount the error term can vary without causing an actual value report
+ nsecs_t mAllowedActualDeviation = -1;
// Whether we should emit ATRACE_INT data for hint sessions
static const bool sTraceHintSessionData;
-
- // Max percent the actual duration can vary without causing a report (eg: 0.1 = 10%)
- static constexpr double kAllowedActualDeviationPercent = 0.1;
- // Max percent the target duration can vary without causing a report (eg: 0.1 = 10%)
- static constexpr double kAllowedTargetDeviationPercent = 0.1;
- // Target used for init and normalization, the actual value does not really matter
- static constexpr const std::chrono::nanoseconds kDefaultTarget = 50ms;
+ static constexpr const std::chrono::nanoseconds kDefaultTarget = 16ms;
// Amount of time after the last message was sent before the session goes stale
// actually 100ms but we use 80 here to ideally avoid going stale
static constexpr const std::chrono::nanoseconds kStaleTimeout = 80ms;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index be16942d40..bcc94d3146 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -81,6 +81,7 @@
namespace android {
namespace {
constexpr int kDumpTableRowLength = 159;
+const ui::Transform kIdentityTransform;
} // namespace
using namespace ftl::flag_operators;
@@ -2162,7 +2163,8 @@ void Layer::writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet
if ((traceFlags & LayerTracing::TRACE_INPUT) && needsInputInfo()) {
WindowInfo info;
if (useDrawing) {
- info = fillInputInfo(ui::Transform(), /* displayIsSecure */ true);
+ info = fillInputInfo(
+ InputDisplayArgs{.transform = &kIdentityTransform, .isSecure = true});
} else {
info = state.inputInfo;
}
@@ -2369,7 +2371,7 @@ void Layer::handleDropInputMode(gui::WindowInfo& info) const {
}
}
-WindowInfo Layer::fillInputInfo(const ui::Transform& displayTransform, bool displayIsSecure) {
+WindowInfo Layer::fillInputInfo(const InputDisplayArgs& displayArgs) {
if (!hasInputInfo()) {
mDrawingState.inputInfo.name = getName();
mDrawingState.inputInfo.ownerUid = mOwnerUid;
@@ -2378,12 +2380,21 @@ WindowInfo Layer::fillInputInfo(const ui::Transform& displayTransform, bool disp
mDrawingState.inputInfo.displayId = getLayerStack().id;
}
+ const ui::Transform& displayTransform =
+ displayArgs.transform != nullptr ? *displayArgs.transform : kIdentityTransform;
+
WindowInfo info = mDrawingState.inputInfo;
info.id = sequence;
info.displayId = getLayerStack().id;
fillInputFrameInfo(info, displayTransform);
+ if (displayArgs.transform == nullptr) {
+ // Do not let the window receive touches if it is not associated with a valid display
+ // transform. We still allow the window to receive keys and prevent ANRs.
+ info.inputConfig |= WindowInfo::InputConfig::NOT_TOUCHABLE;
+ }
+
// For compatibility reasons we let layers which can receive input
// receive input before they have actually submitted a buffer. Because
// of this we use canReceiveInput instead of isVisible to check the
@@ -2401,7 +2412,7 @@ WindowInfo Layer::fillInputInfo(const ui::Transform& displayTransform, bool disp
// If the window will be blacked out on a display because the display does not have the secure
// flag and the layer has the secure flag set, then drop input.
- if (!displayIsSecure && isSecure()) {
+ if (!displayArgs.isSecure && isSecure()) {
info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT;
}
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 24abad9b45..c547da06cb 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -844,7 +844,11 @@ public:
bool getPremultipledAlpha() const;
void setInputInfo(const gui::WindowInfo& info);
- gui::WindowInfo fillInputInfo(const ui::Transform& displayTransform, bool displayIsSecure);
+ struct InputDisplayArgs {
+ const ui::Transform* transform = nullptr;
+ bool isSecure = false;
+ };
+ gui::WindowInfo fillInputInfo(const InputDisplayArgs& displayArgs);
/**
* Returns whether this layer has an explicitly set input-info.
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index d4435c2818..a9180d4483 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -16,9 +16,10 @@
#include <algorithm>
-#include "RefreshRateOverlay.h"
+#include "BackgroundExecutor.h"
#include "Client.h"
#include "Layer.h"
+#include "RefreshRateOverlay.h"
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
@@ -56,6 +57,14 @@ SurfaceComposerClient::Transaction createTransaction(const sp<SurfaceControl>& s
} // namespace
+SurfaceControlHolder::~SurfaceControlHolder() {
+ // Hand the sp<SurfaceControl> to the helper thread to release the last
+ // reference. This makes sure that the SurfaceControl is destructed without
+ // SurfaceFlinger::mStateLock held.
+ BackgroundExecutor::getInstance().sendCallbacks(
+ {[sc = std::move(mSurfaceControl)]() mutable { sc.clear(); }});
+}
+
void RefreshRateOverlay::SevenSegmentDrawer::drawSegment(Segment segment, int left, SkColor color,
SkCanvas& canvas) {
const SkRect rect = [&]() {
@@ -210,21 +219,27 @@ auto RefreshRateOverlay::SevenSegmentDrawer::draw(int number, SkColor color,
return buffers;
}
+std::unique_ptr<SurfaceControlHolder> createSurfaceControlHolder() {
+ sp<SurfaceControl> surfaceControl =
+ SurfaceComposerClient::getDefault()
+ ->createSurface(String8("RefreshRateOverlay"), kBufferWidth, kBufferHeight,
+ PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceBufferState);
+ return std::make_unique<SurfaceControlHolder>(std::move(surfaceControl));
+}
+
RefreshRateOverlay::RefreshRateOverlay(FpsRange fpsRange, bool showSpinner)
: mFpsRange(fpsRange),
mShowSpinner(showSpinner),
- mSurfaceControl(SurfaceComposerClient::getDefault()
- ->createSurface(String8("RefreshRateOverlay"), kBufferWidth,
- kBufferHeight, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceBufferState)) {
+ mSurfaceControl(createSurfaceControlHolder()) {
if (!mSurfaceControl) {
ALOGE("%s: Failed to create buffer state layer", __func__);
return;
}
- createTransaction(mSurfaceControl)
- .setLayer(mSurfaceControl, INT32_MAX - 2)
- .setTrustedOverlay(mSurfaceControl, true)
+ createTransaction(mSurfaceControl->get())
+ .setLayer(mSurfaceControl->get(), INT32_MAX - 2)
+ .setTrustedOverlay(mSurfaceControl->get(), true)
.apply();
}
@@ -233,7 +248,7 @@ auto RefreshRateOverlay::getOrCreateBuffers(Fps fps) -> const Buffers& {
if (!mSurfaceControl) return kNoBuffers;
const auto transformHint =
- static_cast<ui::Transform::RotationFlags>(mSurfaceControl->getTransformHint());
+ static_cast<ui::Transform::RotationFlags>(mSurfaceControl->get()->getTransformHint());
// Tell SurfaceFlinger about the pre-rotation on the buffer.
const auto transform = [&] {
@@ -247,7 +262,9 @@ auto RefreshRateOverlay::getOrCreateBuffers(Fps fps) -> const Buffers& {
}
}();
- createTransaction(mSurfaceControl).setTransform(mSurfaceControl, transform).apply();
+ createTransaction(mSurfaceControl->get())
+ .setTransform(mSurfaceControl->get(), transform)
+ .apply();
BufferCache::const_iterator it = mBufferCache.find({fps.getIntValue(), transformHint});
if (it == mBufferCache.end()) {
@@ -289,21 +306,21 @@ void RefreshRateOverlay::setViewport(ui::Size viewport) {
Rect frame((3 * width) >> 4, height >> 5);
frame.offsetBy(width >> 5, height >> 4);
- createTransaction(mSurfaceControl)
- .setMatrix(mSurfaceControl, frame.getWidth() / static_cast<float>(kBufferWidth), 0, 0,
- frame.getHeight() / static_cast<float>(kBufferHeight))
- .setPosition(mSurfaceControl, frame.left, frame.top)
+ createTransaction(mSurfaceControl->get())
+ .setMatrix(mSurfaceControl->get(), frame.getWidth() / static_cast<float>(kBufferWidth),
+ 0, 0, frame.getHeight() / static_cast<float>(kBufferHeight))
+ .setPosition(mSurfaceControl->get(), frame.left, frame.top)
.apply();
}
void RefreshRateOverlay::setLayerStack(ui::LayerStack stack) {
- createTransaction(mSurfaceControl).setLayerStack(mSurfaceControl, stack).apply();
+ createTransaction(mSurfaceControl->get()).setLayerStack(mSurfaceControl->get(), stack).apply();
}
void RefreshRateOverlay::changeRefreshRate(Fps fps) {
mCurrentFps = fps;
const auto buffer = getOrCreateBuffers(fps)[mFrame];
- createTransaction(mSurfaceControl).setBuffer(mSurfaceControl, buffer).apply();
+ createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply();
}
void RefreshRateOverlay::animate() {
@@ -312,7 +329,7 @@ void RefreshRateOverlay::animate() {
const auto& buffers = getOrCreateBuffers(*mCurrentFps);
mFrame = (mFrame + 1) % buffers.size();
const auto buffer = buffers[mFrame];
- createTransaction(mSurfaceControl).setBuffer(mSurfaceControl, buffer).apply();
+ createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply();
}
} // namespace android
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index a465a36747..a2966e654e 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -33,6 +33,20 @@ namespace android {
class GraphicBuffer;
class SurfaceControl;
+class SurfaceFlinger;
+
+// Helper class to delete the SurfaceControl on a helper thread as
+// SurfaceControl assumes its destruction happens without SurfaceFlinger::mStateLock held.
+class SurfaceControlHolder {
+public:
+ explicit SurfaceControlHolder(sp<SurfaceControl> sc) : mSurfaceControl(std::move(sc)){};
+ ~SurfaceControlHolder();
+
+ const sp<SurfaceControl>& get() const { return mSurfaceControl; }
+
+private:
+ sp<SurfaceControl> mSurfaceControl;
+};
class RefreshRateOverlay {
public:
@@ -75,7 +89,7 @@ private:
const FpsRange mFpsRange; // For color interpolation.
const bool mShowSpinner;
- const sp<SurfaceControl> mSurfaceControl;
+ const std::unique_ptr<SurfaceControlHolder> mSurfaceControl;
};
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
index 747032be0d..4af1f5c65f 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
@@ -188,10 +188,10 @@ void DispSyncSource::onVsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime
VSyncSource::VSyncData DispSyncSource::getLatestVSyncData() const {
std::lock_guard lock(mVsyncMutex);
- nsecs_t expectedPresentTime = mVSyncTracker.nextAnticipatedVSyncTimeFrom(
+ nsecs_t expectedPresentationTime = mVSyncTracker.nextAnticipatedVSyncTimeFrom(
systemTime() + mWorkDuration.get().count() + mReadyDuration.count());
- nsecs_t deadline = expectedPresentTime - mWorkDuration.get().count() - mReadyDuration.count();
- return {expectedPresentTime, deadline};
+ nsecs_t deadline = expectedPresentationTime - mReadyDuration.count();
+ return {expectedPresentationTime, deadline};
}
void DispSyncSource::dump(std::string& result) const {
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index ca8349636b..a48c921378 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -40,22 +40,26 @@ namespace {
struct RefreshRateScore {
DisplayModeIterator modeIt;
- float score;
+ float overallScore;
+ struct {
+ float modeBelowThreshold;
+ float modeAboveThreshold;
+ } fixedRateBelowThresholdLayersScore;
};
template <typename Iterator>
const DisplayModePtr& getMaxScoreRefreshRate(Iterator begin, Iterator end) {
const auto it =
std::max_element(begin, end, [](RefreshRateScore max, RefreshRateScore current) {
- const auto& [modeIt, score] = current;
+ const auto& [modeIt, overallScore, _] = current;
std::string name = to_string(modeIt->second->getFps());
- ALOGV("%s scores %.2f", name.c_str(), score);
+ ALOGV("%s scores %.2f", name.c_str(), overallScore);
- ATRACE_INT(name.c_str(), static_cast<int>(std::round(score * 100)));
+ ATRACE_INT(name.c_str(), static_cast<int>(std::round(overallScore * 100)));
constexpr float kEpsilon = 0.0001f;
- return score > max.score * (1 + kEpsilon);
+ return overallScore > max.overallScore * (1 + kEpsilon);
});
return it->modeIt->second;
@@ -151,31 +155,6 @@ std::pair<nsecs_t, nsecs_t> RefreshRateConfigs::getDisplayFrames(nsecs_t layerPe
return {quotient, remainder};
}
-bool RefreshRateConfigs::isVoteAllowed(const LayerRequirement& layer, Fps refreshRate) const {
- using namespace fps_approx_ops;
-
- switch (layer.vote) {
- case LayerVoteType::ExplicitExactOrMultiple:
- case LayerVoteType::Heuristic:
- if (mConfig.frameRateMultipleThreshold != 0 &&
- refreshRate >= Fps::fromValue(mConfig.frameRateMultipleThreshold) &&
- layer.desiredRefreshRate < Fps::fromValue(mConfig.frameRateMultipleThreshold / 2)) {
- // Don't vote high refresh rates past the threshold for layers with a low desired
- // refresh rate. For example, desired 24 fps with 120 Hz threshold means no vote for
- // 120 Hz, but desired 60 fps should have a vote.
- return false;
- }
- break;
- case LayerVoteType::ExplicitDefault:
- case LayerVoteType::ExplicitExact:
- case LayerVoteType::Max:
- case LayerVoteType::Min:
- case LayerVoteType::NoVote:
- break;
- }
- return true;
-}
-
float RefreshRateConfigs::calculateNonExactMatchingLayerScoreLocked(const LayerRequirement& layer,
Fps refreshRate) const {
constexpr float kScoreForFractionalPairs = .8f;
@@ -240,10 +219,6 @@ float RefreshRateConfigs::calculateNonExactMatchingLayerScoreLocked(const LayerR
float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& layer, Fps refreshRate,
bool isSeamlessSwitch) const {
- if (!isVoteAllowed(layer, refreshRate)) {
- return 0;
- }
-
// Slightly prefer seamless switches.
constexpr float kSeamedSwitchPenalty = 0.95f;
const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty;
@@ -300,6 +275,7 @@ auto RefreshRateConfigs::getBestRefreshRate(const std::vector<LayerRequirement>&
auto RefreshRateConfigs::getBestRefreshRateLocked(const std::vector<LayerRequirement>& layers,
GlobalSignals signals) const
-> std::pair<DisplayModePtr, GlobalSignals> {
+ using namespace fps_approx_ops;
ATRACE_CALL();
ALOGV("%s: %zu layers", __func__, layers.size());
@@ -409,7 +385,7 @@ auto RefreshRateConfigs::getBestRefreshRateLocked(const std::vector<LayerRequire
const auto weight = layer.weight;
- for (auto& [modeIt, score] : scores) {
+ for (auto& [modeIt, overallScore, fixedRateBelowThresholdLayersScore] : scores) {
const auto& [id, mode] = *modeIt;
const bool isSeamlessSwitch = mode->getGroup() == mActiveModeIt->second->getGroup();
@@ -451,18 +427,92 @@ auto RefreshRateConfigs::getBestRefreshRateLocked(const std::vector<LayerRequire
continue;
}
- const auto layerScore =
+ const float layerScore =
calculateLayerScoreLocked(layer, mode->getFps(), isSeamlessSwitch);
- ALOGV("%s gives %s score of %.4f", formatLayerInfo(layer, weight).c_str(),
- to_string(mode->getFps()).c_str(), layerScore);
+ const float weightedLayerScore = weight * layerScore;
+
+ // Layer with fixed source has a special consideration which depends on the
+ // mConfig.frameRateMultipleThreshold. We don't want these layers to score
+ // refresh rates above the threshold, but we also don't want to favor the lower
+ // ones by having a greater number of layers scoring them. Instead, we calculate
+ // the score independently for these layers and later decide which
+ // refresh rates to add it. For example, desired 24 fps with 120 Hz threshold should not
+ // score 120 Hz, but desired 60 fps should contribute to the score.
+ const bool fixedSourceLayer = [](LayerVoteType vote) {
+ switch (vote) {
+ case LayerVoteType::ExplicitExactOrMultiple:
+ case LayerVoteType::Heuristic:
+ return true;
+ case LayerVoteType::NoVote:
+ case LayerVoteType::Min:
+ case LayerVoteType::Max:
+ case LayerVoteType::ExplicitDefault:
+ case LayerVoteType::ExplicitExact:
+ return false;
+ }
+ }(layer.vote);
+ const bool layerBelowThreshold = mConfig.frameRateMultipleThreshold != 0 &&
+ layer.desiredRefreshRate <
+ Fps::fromValue(mConfig.frameRateMultipleThreshold / 2);
+ if (fixedSourceLayer && layerBelowThreshold) {
+ const bool modeAboveThreshold =
+ mode->getFps() >= Fps::fromValue(mConfig.frameRateMultipleThreshold);
+ if (modeAboveThreshold) {
+ ALOGV("%s gives %s fixed source (above threshold) score of %.4f",
+ formatLayerInfo(layer, weight).c_str(), to_string(mode->getFps()).c_str(),
+ layerScore);
+ fixedRateBelowThresholdLayersScore.modeAboveThreshold += weightedLayerScore;
+ } else {
+ ALOGV("%s gives %s fixed source (below threshold) score of %.4f",
+ formatLayerInfo(layer, weight).c_str(), to_string(mode->getFps()).c_str(),
+ layerScore);
+ fixedRateBelowThresholdLayersScore.modeBelowThreshold += weightedLayerScore;
+ }
+ } else {
+ ALOGV("%s gives %s score of %.4f", formatLayerInfo(layer, weight).c_str(),
+ to_string(mode->getFps()).c_str(), layerScore);
+ overallScore += weightedLayerScore;
+ }
+ }
+ }
+
+ // We want to find the best refresh rate without the fixed source layers,
+ // so we could know whether we should add the modeAboveThreshold scores or not.
+ // If the best refresh rate is already above the threshold, it means that
+ // some non-fixed source layers already scored it, so we can just add the score
+ // for all fixed source layers, even the ones that are above the threshold.
+ const bool maxScoreAboveThreshold = [&] {
+ if (mConfig.frameRateMultipleThreshold == 0 || scores.empty()) {
+ return false;
+ }
+
+ const auto maxScoreIt =
+ std::max_element(scores.begin(), scores.end(),
+ [](RefreshRateScore max, RefreshRateScore current) {
+ const auto& [modeIt, overallScore, _] = current;
+ return overallScore > max.overallScore;
+ });
+ ALOGV("%s is the best refresh rate without fixed source layers. It is %s the threshold for "
+ "refresh rate multiples",
+ to_string(maxScoreIt->modeIt->second->getFps()).c_str(),
+ maxScoreAboveThreshold ? "above" : "below");
+ return maxScoreIt->modeIt->second->getFps() >=
+ Fps::fromValue(mConfig.frameRateMultipleThreshold);
+ }();
- score += weight * layerScore;
+ // Now we can add the fixed rate layers score
+ for (auto& [modeIt, overallScore, fixedRateBelowThresholdLayersScore] : scores) {
+ overallScore += fixedRateBelowThresholdLayersScore.modeBelowThreshold;
+ if (maxScoreAboveThreshold) {
+ overallScore += fixedRateBelowThresholdLayersScore.modeAboveThreshold;
}
+ ALOGV("%s adjusted overallScore is %.4f", to_string(modeIt->second->getFps()).c_str(),
+ overallScore);
}
- // Now that we scored all the refresh rates we need to pick the one that got the highest score.
- // In case of a tie we will pick the higher refresh rate if any of the layers wanted Max,
- // or the lower otherwise.
+ // Now that we scored all the refresh rates we need to pick the one that got the highest
+ // overallScore. In case of a tie we will pick the higher refresh rate if any of the layers
+ // wanted Max, or the lower otherwise.
const DisplayModePtr& bestRefreshRate = maxVoteLayers > 0
? getMaxScoreRefreshRate(scores.rbegin(), scores.rend())
: getMaxScoreRefreshRate(scores.begin(), scores.end());
@@ -471,7 +521,7 @@ auto RefreshRateConfigs::getBestRefreshRateLocked(const std::vector<LayerRequire
// If we never scored any layers, then choose the rate from the primary
// range instead of picking a random score from the app range.
if (std::all_of(scores.begin(), scores.end(),
- [](RefreshRateScore score) { return score.score == 0; })) {
+ [](RefreshRateScore score) { return score.overallScore == 0; })) {
const DisplayModePtr& max = getMaxRefreshRateByPolicyLocked(anchorGroup);
ALOGV("layers not scored - choose %s", to_string(max->getFps()).c_str());
return {max, kNoSignals};
@@ -575,7 +625,7 @@ RefreshRateConfigs::UidToFrameRateOverride RefreshRateConfigs::getFrameRateOverr
continue;
}
- for (auto& [_, score] : scores) {
+ for (auto& [_, score, _1] : scores) {
score = 0;
}
@@ -587,7 +637,7 @@ RefreshRateConfigs::UidToFrameRateOverride RefreshRateConfigs::getFrameRateOverr
LOG_ALWAYS_FATAL_IF(layer->vote != LayerVoteType::ExplicitDefault &&
layer->vote != LayerVoteType::ExplicitExactOrMultiple &&
layer->vote != LayerVoteType::ExplicitExact);
- for (auto& [modeIt, score] : scores) {
+ for (auto& [modeIt, score, _] : scores) {
constexpr bool isSeamlessSwitch = true;
const auto layerScore = calculateLayerScoreLocked(*layer, modeIt->second->getFps(),
isSeamlessSwitch);
@@ -605,7 +655,7 @@ RefreshRateConfigs::UidToFrameRateOverride RefreshRateConfigs::getFrameRateOverr
// If we never scored any layers, we don't have a preferred frame rate
if (std::all_of(scores.begin(), scores.end(),
- [](RefreshRateScore score) { return score.score == 0; })) {
+ [](RefreshRateScore score) { return score.overallScore == 0; })) {
continue;
}
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 05a8692f51..a79002e959 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -353,9 +353,6 @@ private:
const Policy* getCurrentPolicyLocked() const REQUIRES(mLock);
bool isPolicyValidLocked(const Policy& policy) const REQUIRES(mLock);
- // Returns whether the layer is allowed to vote for the given refresh rate.
- bool isVoteAllowed(const LayerRequirement&, Fps) const;
-
// calculates a score for a layer. Used to determine the display refresh rate
// and the frame rate override for certains applications.
float calculateLayerScoreLocked(const LayerRequirement&, Fps refreshRate,
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 37f0fec425..727cb0817e 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -25,6 +25,7 @@
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
#include <configstore/Utils.h>
+#include <ftl/fake_guard.h>
#include <gui/WindowInfo.h>
#include <system/window.h>
#include <ui/DisplayStatInfo.h>
@@ -94,9 +95,13 @@ void Scheduler::startTimers() {
}
void Scheduler::setRefreshRateConfigs(std::shared_ptr<RefreshRateConfigs> configs) {
+ // The current RefreshRateConfigs instance may outlive this call, so unbind its idle timer.
{
- // The current RefreshRateConfigs instance may outlive this call, so unbind its idle timer.
- std::scoped_lock lock(mRefreshRateConfigsLock);
+ // mRefreshRateConfigsLock is not locked here to avoid the deadlock
+ // as the callback can attempt to acquire the lock before stopIdleTimer can finish
+ // the execution. It's safe to FakeGuard as main thread is the only thread that
+ // writes to the mRefreshRateConfigs.
+ ftl::FakeGuard guard(mRefreshRateConfigsLock);
if (mRefreshRateConfigs) {
mRefreshRateConfigs->stopIdleTimer();
mRefreshRateConfigs->clearIdleTimerCallbacks();
@@ -554,11 +559,12 @@ void Scheduler::onTouchHint() {
}
}
-void Scheduler::setDisplayPowerState(bool normal) {
+void Scheduler::setDisplayPowerMode(hal::PowerMode powerMode) {
{
std::lock_guard<std::mutex> lock(mPolicyLock);
- mPolicy.isDisplayPowerStateNormal = normal;
+ mPolicy.displayPowerMode = powerMode;
}
+ mVsyncSchedule->getController().setDisplayPowerMode(powerMode);
if (mDisplayPowerTimer) {
mDisplayPowerTimer->reset();
@@ -706,7 +712,8 @@ auto Scheduler::chooseDisplayMode() -> std::pair<DisplayModePtr, GlobalSignals>
// If Display Power is not in normal operation we want to be in performance mode. When coming
// back to normal mode, a grace period is given with DisplayPowerTimer.
if (mDisplayPowerTimer &&
- (!mPolicy.isDisplayPowerStateNormal || mPolicy.displayPowerTimer == TimerState::Reset)) {
+ (mPolicy.displayPowerMode != hal::PowerMode::ON ||
+ mPolicy.displayPowerTimer == TimerState::Reset)) {
constexpr GlobalSignals kNoSignals;
return {configs->getMaxRefreshRateByPolicy(), kNoSignals};
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 0c72124119..a8043bf94c 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -186,7 +186,7 @@ public:
// Indicates that touch interaction is taking place.
void onTouchHint();
- void setDisplayPowerState(bool normal);
+ void setDisplayPowerMode(hal::PowerMode powerMode);
VSyncDispatch& getVsyncDispatch() { return mVsyncSchedule->getDispatch(); }
@@ -325,7 +325,7 @@ private:
TimerState idleTimer = TimerState::Reset;
TouchState touch = TouchState::Inactive;
TimerState displayPowerTimer = TimerState::Expired;
- bool isDisplayPowerStateNormal = true;
+ hal::PowerMode displayPowerMode = hal::PowerMode::ON;
// Chosen display mode.
DisplayModePtr mode;
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index bdcab515f2..13cd304a5f 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -146,6 +146,11 @@ bool VSyncReactor::periodConfirmed(nsecs_t vsync_timestamp, std::optional<nsecs_
return false;
}
+ if (mDisplayPowerMode == hal::PowerMode::DOZE ||
+ mDisplayPowerMode == hal::PowerMode::DOZE_SUSPEND) {
+ return true;
+ }
+
if (!mLastHwVsync && !HwcVsyncPeriod) {
return false;
}
@@ -206,6 +211,11 @@ bool VSyncReactor::addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t>
return mMoreSamplesNeeded;
}
+void VSyncReactor::setDisplayPowerMode(hal::PowerMode powerMode) {
+ std::scoped_lock lock(mMutex);
+ mDisplayPowerMode = powerMode;
+}
+
void VSyncReactor::dump(std::string& result) const {
std::lock_guard lock(mMutex);
StringAppendF(&result, "VsyncReactor in use\n");
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index 6a1950ac77..4501487392 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -49,6 +49,8 @@ public:
bool addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
bool* periodFlushed) final;
+ void setDisplayPowerMode(hal::PowerMode powerMode) final;
+
void dump(std::string& result) const final;
private:
@@ -73,6 +75,8 @@ private:
std::optional<nsecs_t> mPeriodTransitioningTo GUARDED_BY(mMutex);
std::optional<nsecs_t> mLastHwVsync GUARDED_BY(mMutex);
+ hal::PowerMode mDisplayPowerMode GUARDED_BY(mMutex) = hal::PowerMode::ON;
+
const bool mSupportKernelIdleTimer = false;
};
diff --git a/services/surfaceflinger/Scheduler/VsyncController.h b/services/surfaceflinger/Scheduler/VsyncController.h
index 59f65372a9..726a420649 100644
--- a/services/surfaceflinger/Scheduler/VsyncController.h
+++ b/services/surfaceflinger/Scheduler/VsyncController.h
@@ -18,7 +18,10 @@
#include <cstddef>
#include <memory>
+#include <mutex>
+#include <DisplayHardware/HWComposer.h>
+#include <DisplayHardware/Hal.h>
#include <ui/FenceTime.h>
#include <utils/Mutex.h>
#include <utils/RefBase.h>
@@ -70,6 +73,13 @@ public:
*/
virtual void setIgnorePresentFences(bool ignore) = 0;
+ /*
+ * Sets the primary display power mode to the controller.
+ *
+ * \param [in] powerMode
+ */
+ virtual void setDisplayPowerMode(hal::PowerMode powerMode) = 0;
+
virtual void dump(std::string& result) const = 0;
protected:
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 6a17cd8881..0e1acb4154 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -444,6 +444,11 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI
}
mIgnoreHdrCameraLayers = ignore_hdr_camera_layers(false);
+
+ // Power hint session mode, representing which hint(s) to send: early, late, or both)
+ mPowerHintSessionMode =
+ {.late = base::GetBoolProperty("debug.sf.send_late_power_session_hint"s, true),
+ .early = base::GetBoolProperty("debug.sf.send_early_power_session_hint"s, false)};
}
LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() {
@@ -1991,12 +1996,6 @@ nsecs_t SurfaceFlinger::calculateExpectedPresentTime(DisplayStatInfo stats) cons
bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expectedVsyncTime)
FTL_FAKE_GUARD(kMainThreadContext) {
- // we set this once at the beginning of commit to ensure consistency throughout the whole frame
- mPowerHintSessionData.sessionEnabled = mPowerAdvisor->usePowerHintSession();
- if (mPowerHintSessionData.sessionEnabled) {
- mPowerHintSessionData.commitStart = systemTime();
- }
-
// calculate the expected present time once and use the cached
// value throughout this frame to make sure all layers are
// seeing this same value.
@@ -2010,10 +2009,6 @@ bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expected
const nsecs_t lastScheduledPresentTime = mScheduledPresentTime;
mScheduledPresentTime = expectedVsyncTime;
- if (mPowerHintSessionData.sessionEnabled) {
- mPowerAdvisor->setTargetWorkDuration(mExpectedPresentTime -
- mPowerHintSessionData.commitStart);
- }
const auto vsyncIn = [&] {
if (!ATRACE_ENABLED()) return 0.f;
return (mExpectedPresentTime - systemTime()) / 1e6f;
@@ -2089,6 +2084,32 @@ bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expected
}
}
+ // Save this once per commit + composite to ensure consistency
+ // TODO (b/240619471): consider removing active display check once AOD is fixed
+ const auto activeDisplay =
+ FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(mActiveDisplayToken));
+ mPowerHintSessionEnabled = mPowerAdvisor->usePowerHintSession() && activeDisplay &&
+ activeDisplay->getPowerMode() == hal::PowerMode::ON;
+ if (mPowerHintSessionEnabled) {
+ const auto& display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()).get();
+ // get stable vsync period from display mode
+ const nsecs_t vsyncPeriod = display->getActiveMode()->getVsyncPeriod();
+ mPowerAdvisor->setCommitStart(frameTime);
+ mPowerAdvisor->setExpectedPresentTime(mExpectedPresentTime);
+ const nsecs_t idealSfWorkDuration =
+ mVsyncModulator->getVsyncConfig().sfWorkDuration.count();
+ // Frame delay is how long we should have minus how long we actually have
+ mPowerAdvisor->setFrameDelay(idealSfWorkDuration - (mExpectedPresentTime - frameTime));
+ mPowerAdvisor->setTotalFrameTargetWorkDuration(idealSfWorkDuration);
+ mPowerAdvisor->setTargetWorkDuration(vsyncPeriod);
+
+ // Send early hint here to make sure there's not another frame pending
+ if (mPowerHintSessionMode.early) {
+ // Send a rough prediction for this frame based on last frame's timing info
+ mPowerAdvisor->sendPredictedWorkDuration();
+ }
+ }
+
if (mTracingEnabledChanged) {
mLayerTracingEnabled = mLayerTracing.isEnabled();
mTracingEnabledChanged = false;
@@ -2165,16 +2186,15 @@ void SurfaceFlinger::composite(nsecs_t frameTime, int64_t vsyncId)
FTL_FAKE_GUARD(kMainThreadContext) {
ATRACE_FORMAT("%s %" PRId64, __func__, vsyncId);
- if (mPowerHintSessionData.sessionEnabled) {
- mPowerHintSessionData.compositeStart = systemTime();
- }
-
compositionengine::CompositionRefreshArgs refreshArgs;
const auto& displays = FTL_FAKE_GUARD(mStateLock, mDisplays);
refreshArgs.outputs.reserve(displays.size());
+ std::vector<DisplayId> displayIds;
for (const auto& [_, display] : displays) {
refreshArgs.outputs.push_back(display->getCompositionDisplay());
+ displayIds.push_back(display->getId());
}
+ mPowerAdvisor->setDisplays(displayIds);
mDrawingState.traverseInZOrder([&refreshArgs](Layer* layer) {
if (auto layerFE = layer->getCompositionEngineLayerFE())
refreshArgs.layers.push_back(layerFE);
@@ -2222,12 +2242,17 @@ void SurfaceFlinger::composite(nsecs_t frameTime, int64_t vsyncId)
mCompositionEngine->present(refreshArgs);
- if (mPowerHintSessionData.sessionEnabled) {
- mPowerHintSessionData.presentEnd = systemTime();
- }
-
mTimeStats->recordFrameDuration(frameTime, systemTime());
+ // Send a power hint hint after presentation is finished
+ if (mPowerHintSessionEnabled) {
+ mPowerAdvisor->setSfPresentTiming(mPreviousPresentFences[0].fenceTime->getSignalTime(),
+ systemTime());
+ if (mPowerHintSessionMode.late) {
+ mPowerAdvisor->sendActualWorkDuration();
+ }
+ }
+
if (mScheduler->onPostComposition(presentTime)) {
scheduleComposite(FrameHint::kNone);
}
@@ -2272,11 +2297,8 @@ void SurfaceFlinger::composite(nsecs_t frameTime, int64_t vsyncId)
scheduleCommit(FrameHint::kNone);
}
- // calculate total render time for performance hinting if adpf cpu hint is enabled,
- if (mPowerHintSessionData.sessionEnabled) {
- const nsecs_t flingerDuration =
- (mPowerHintSessionData.presentEnd - mPowerHintSessionData.commitStart);
- mPowerAdvisor->sendActualWorkDuration(flingerDuration, mPowerHintSessionData.presentEnd);
+ if (mPowerHintSessionEnabled) {
+ mPowerAdvisor->setCompositeEnd(systemTime());
}
}
@@ -2867,7 +2889,8 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal(
ALOGV("Display Orientation: %s", toCString(creationArgs.physicalOrientation));
// virtual displays are always considered enabled
- creationArgs.initialPowerMode = state.isVirtual() ? hal::PowerMode::ON : hal::PowerMode::OFF;
+ creationArgs.initialPowerMode =
+ state.isVirtual() ? std::make_optional(hal::PowerMode::ON) : std::nullopt;
sp<DisplayDevice> display = getFactory().createDisplayDevice(creationArgs);
@@ -3310,11 +3333,11 @@ void SurfaceFlinger::buildWindowInfos(std::vector<WindowInfo>& outWindowInfos,
mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
if (!layer->needsInputInfo()) return;
- // Do not create WindowInfos for windows on displays that cannot receive input.
- if (const auto opt = displayInputInfos.get(layer->getLayerStack())) {
- const auto& info = opt->get();
- outWindowInfos.push_back(layer->fillInputInfo(info.transform, info.isSecure));
- }
+ const auto opt = displayInputInfos.get(layer->getLayerStack(),
+ [](const auto& info) -> Layer::InputDisplayArgs {
+ return {&info.transform, info.isSecure};
+ });
+ outWindowInfos.push_back(layer->fillInputInfo(opt.value_or(Layer::InputDisplayArgs{})));
});
sNumWindowInfos = outWindowInfos.size();
@@ -4848,8 +4871,8 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal:
const auto displayId = display->getPhysicalId();
ALOGD("Setting power mode %d on display %s", mode, to_string(displayId).c_str());
- const hal::PowerMode currentMode = display->getPowerMode();
- if (mode == currentMode) {
+ std::optional<hal::PowerMode> currentMode = display->getPowerMode();
+ if (currentMode.has_value() && mode == *currentMode) {
return;
}
@@ -4865,7 +4888,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal:
mInterceptor->savePowerModeUpdate(display->getSequenceId(), static_cast<int32_t>(mode));
}
const auto refreshRate = display->refreshRateConfigs().getActiveMode()->getFps();
- if (currentMode == hal::PowerMode::OFF) {
+ if (*currentMode == hal::PowerMode::OFF) {
// Turn on the display
if (display->isInternal() && (!activeDisplay || !activeDisplay->isPoweredOn())) {
onActiveDisplayChangedLocked(display);
@@ -4896,7 +4919,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal:
if (SurfaceFlinger::setSchedAttr(false) != NO_ERROR) {
ALOGW("Couldn't set uclamp.min on display off: %s\n", strerror(errno));
}
- if (isDisplayActiveLocked(display) && currentMode != hal::PowerMode::DOZE_SUSPEND) {
+ if (isDisplayActiveLocked(display) && *currentMode != hal::PowerMode::DOZE_SUSPEND) {
mScheduler->disableHardwareVsync(true);
mScheduler->onScreenReleased(mAppConnectionHandle);
}
@@ -4910,7 +4933,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal:
} else if (mode == hal::PowerMode::DOZE || mode == hal::PowerMode::ON) {
// Update display while dozing
getHwComposer().setPowerMode(displayId, mode);
- if (isDisplayActiveLocked(display) && currentMode == hal::PowerMode::DOZE_SUSPEND) {
+ if (isDisplayActiveLocked(display) && *currentMode == hal::PowerMode::DOZE_SUSPEND) {
mScheduler->onScreenAcquired(mAppConnectionHandle);
mScheduler->resyncToHardwareVsync(true, refreshRate);
}
@@ -4929,7 +4952,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal:
if (isDisplayActiveLocked(display)) {
mTimeStats->setPowerMode(mode);
mRefreshRateStats->setPowerMode(mode);
- mScheduler->setDisplayPowerState(mode == hal::PowerMode::ON);
+ mScheduler->setDisplayPowerMode(mode);
}
ALOGD("Finished setting power mode %d on display %s", mode, to_string(displayId).c_str());
@@ -4982,6 +5005,25 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) {
const auto flag = args.empty() ? ""s : std::string(String8(args[0]));
+ // Traversal of drawing state must happen on the main thread.
+ // Otherwise, SortedVector may have shared ownership during concurrent
+ // traversals, which can result in use-after-frees.
+ std::string compositionLayers;
+ mScheduler
+ ->schedule([&] {
+ StringAppendF(&compositionLayers, "Composition layers\n");
+ mDrawingState.traverseInZOrder([&](Layer* layer) {
+ auto* compositionState = layer->getCompositionState();
+ if (!compositionState || !compositionState->isVisible) return;
+
+ android::base::StringAppendF(&compositionLayers, "* Layer %p (%s)\n", layer,
+ layer->getDebugName() ? layer->getDebugName()
+ : "<unknown>");
+ compositionState->dump(compositionLayers);
+ });
+ })
+ .get();
+
bool dumpLayers = true;
{
TimedLock lock(mStateLock, s2ns(1), __func__);
@@ -4994,7 +5036,7 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) {
(it->second)(args, asProto, result);
dumpLayers = false;
} else if (!asProto) {
- dumpAllLocked(args, result);
+ dumpAllLocked(args, compositionLayers, result);
}
}
@@ -5293,7 +5335,8 @@ void SurfaceFlinger::dumpOffscreenLayers(std::string& result) {
result.append(future.get());
}
-void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) const {
+void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, const std::string& compositionLayers,
+ std::string& result) const {
const bool colorize = !args.empty() && args[0] == String16("--color");
Colorizer colorizer(colorize);
@@ -5344,18 +5387,7 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co
StringAppendF(&result, "Visible layers (count = %zu)\n", mNumLayers.load());
colorizer.reset(result);
- {
- StringAppendF(&result, "Composition layers\n");
- mDrawingState.traverseInZOrder([&](Layer* layer) {
- auto* compositionState = layer->getCompositionState();
- if (!compositionState || !compositionState->isVisible) return;
-
- android::base::StringAppendF(&result, "* Layer %p (%s)\n", layer,
- layer->getDebugName() ? layer->getDebugName()
- : "<unknown>");
- compositionState->dump(result);
- });
- }
+ result.append(compositionLayers);
colorizer.bold(result);
StringAppendF(&result, "Displays (%zu entries)\n", mDisplays.size());
@@ -6620,8 +6652,13 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenCommon(
1 /* layerCount */, usage, "screenshot");
const status_t bufferStatus = buffer->initCheck();
- LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "captureScreenCommon: Buffer failed to allocate: %d",
- bufferStatus);
+ if (bufferStatus != OK) {
+ // Animations may end up being really janky, but don't crash here.
+ // Otherwise an irreponsible process may cause an SF crash by allocating
+ // too much.
+ ALOGE("%s: Buffer failed to allocate: %d", __func__, bufferStatus);
+ return ftl::yield<FenceResult>(base::unexpected(bufferStatus)).share();
+ }
const std::shared_ptr<renderengine::ExternalTexture> texture = std::make_shared<
renderengine::impl::ExternalTexture>(buffer, getRenderEngine(),
renderengine::impl::ExternalTexture::Usage::
@@ -6895,9 +6932,7 @@ status_t SurfaceFlinger::setDesiredDisplayModeSpecsInternal(
return NO_ERROR;
}
- status_t setPolicyResult = overridePolicy
- ? display->refreshRateConfigs().setOverridePolicy(policy)
- : display->refreshRateConfigs().setDisplayManagerPolicy(*policy);
+ const status_t setPolicyResult = display->setRefreshRatePolicy(policy, overridePolicy);
if (setPolicyResult < 0) {
return BAD_VALUE;
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index f14c755c2b..9e0cee8fd4 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -1085,7 +1085,8 @@ private:
/*
* Debugging & dumpsys
*/
- void dumpAllLocked(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock);
+ void dumpAllLocked(const DumpArgs& args, const std::string& compositionLayers,
+ std::string& result) const REQUIRES(mStateLock);
void appendSfConfigString(std::string& result) const;
void listLayersLocked(std::string& result) const;
@@ -1435,12 +1436,12 @@ private:
return mScheduler->getLayerFramerate(now, id);
}
+ bool mPowerHintSessionEnabled;
+
struct {
- bool sessionEnabled = false;
- nsecs_t commitStart;
- nsecs_t compositeStart;
- nsecs_t presentEnd;
- } mPowerHintSessionData GUARDED_BY(kMainThreadContext);
+ bool late = false;
+ bool early = false;
+ } mPowerHintSessionMode;
nsecs_t mAnimationTransactionTimeout = s2ns(5);
diff --git a/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp b/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp
index 9ab35d741a..53de4a6e56 100644
--- a/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp
+++ b/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp
@@ -52,6 +52,8 @@ protected:
void verifyAndClearExpectations();
void sendActualWorkDurationGroup(std::vector<WorkDuration> durations,
std::chrono::nanoseconds sleepBeforeLastSend);
+ std::chrono::nanoseconds mAllowedDeviation;
+ std::chrono::nanoseconds mStaleTimeout;
};
void AidlPowerHalWrapperTest::SetUp() {
@@ -59,6 +61,9 @@ void AidlPowerHalWrapperTest::SetUp() {
mMockSession = new NiceMock<MockIPowerHintSession>();
ON_CALL(*mMockHal.get(), getHintSessionPreferredRate(_)).WillByDefault(Return(Status::ok()));
mWrapper = std::make_unique<AidlPowerHalWrapper>(mMockHal);
+ mWrapper->setAllowedActualDeviation(std::chrono::nanoseconds{10ms}.count());
+ mAllowedDeviation = std::chrono::nanoseconds{mWrapper->mAllowedActualDeviation};
+ mStaleTimeout = AidlPowerHalWrapper::kStaleTimeout;
}
void AidlPowerHalWrapperTest::verifyAndClearExpectations() {
@@ -76,6 +81,7 @@ void AidlPowerHalWrapperTest::sendActualWorkDurationGroup(
mWrapper->sendActualWorkDuration(duration.durationNanos, duration.timeStampNanos);
}
}
+
WorkDuration toWorkDuration(std::chrono::nanoseconds durationNanos, int64_t timeStampNanos) {
WorkDuration duration;
duration.durationNanos = durationNanos.count();
@@ -83,6 +89,10 @@ WorkDuration toWorkDuration(std::chrono::nanoseconds durationNanos, int64_t time
return duration;
}
+WorkDuration toWorkDuration(std::pair<std::chrono::nanoseconds, nsecs_t> timePair) {
+ return toWorkDuration(timePair.first, timePair.second);
+}
+
std::string printWorkDurations(const ::std::vector<WorkDuration>& durations) {
std::ostringstream os;
for (auto duration : durations) {
@@ -112,7 +122,7 @@ TEST_F(AidlPowerHalWrapperTest, startPowerHintSession) {
EXPECT_FALSE(mWrapper->startPowerHintSession());
}
-TEST_F(AidlPowerHalWrapperTest, restartNewPoserHintSessionWithNewThreadIds) {
+TEST_F(AidlPowerHalWrapperTest, restartNewPowerHintSessionWithNewThreadIds) {
ASSERT_TRUE(mWrapper->supportsPowerHintSession());
std::vector<int32_t> threadIds = {1, 2};
@@ -149,12 +159,8 @@ TEST_F(AidlPowerHalWrapperTest, setTargetWorkDuration) {
std::chrono::nanoseconds base = 100ms;
// test cases with target work duration and whether it should update hint against baseline 100ms
- const std::vector<std::pair<std::chrono::nanoseconds, bool>> testCases = {{0ms, false},
- {-1ms, false},
- {200ms, true},
- {2ms, true},
- {91ms, false},
- {109ms, false}};
+ const std::vector<std::pair<std::chrono::nanoseconds, bool>> testCases =
+ {{0ms, true}, {-1ms, true}, {200ms, true}, {2ms, true}, {100ms, false}, {109ms, true}};
for (const auto& test : testCases) {
// reset to 100ms baseline
@@ -200,21 +206,21 @@ TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration) {
// 100ms
const std::vector<std::pair<std::vector<std::pair<std::chrono::nanoseconds, nsecs_t>>, bool>>
testCases = {{{{-1ms, 100}}, false},
- {{{91ms, 100}}, false},
- {{{109ms, 100}}, false},
+ {{{100ms - (mAllowedDeviation / 2), 100}}, false},
+ {{{100ms + (mAllowedDeviation / 2), 100}}, false},
+ {{{100ms + (mAllowedDeviation + 1ms), 100}}, true},
+ {{{100ms - (mAllowedDeviation + 1ms), 100}}, true},
{{{100ms, 100}, {200ms, 200}}, true},
{{{100ms, 500}, {100ms, 600}, {3ms, 600}}, true}};
for (const auto& test : testCases) {
// reset actual duration
- sendActualWorkDurationGroup({base}, 80ms);
+ sendActualWorkDurationGroup({base}, mStaleTimeout);
auto raw = test.first;
std::vector<WorkDuration> durations(raw.size());
std::transform(raw.begin(), raw.end(), durations.begin(),
- [](std::pair<std::chrono::nanoseconds, nsecs_t> d) {
- return toWorkDuration(d.first, d.second);
- });
+ [](auto d) { return toWorkDuration(d); });
EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(durations))
.Times(test.second ? 1 : 0);
sendActualWorkDurationGroup(durations, 0ms);
@@ -222,40 +228,6 @@ TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration) {
}
}
-TEST_F(AidlPowerHalWrapperTest, sendAdjustedActualWorkDuration) {
- ASSERT_TRUE(mWrapper->supportsPowerHintSession());
-
- std::vector<int32_t> threadIds = {1, 2};
- mWrapper->setPowerHintSessionThreadIds(threadIds);
- EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _))
- .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok())));
- ASSERT_TRUE(mWrapper->startPowerHintSession());
- verifyAndClearExpectations();
-
- std::chrono::nanoseconds lastTarget = 100ms;
- EXPECT_CALL(*mMockSession.get(), updateTargetWorkDuration(lastTarget.count())).Times(1);
- mWrapper->setTargetWorkDuration(lastTarget.count());
- std::chrono::nanoseconds newTarget = 105ms;
- mWrapper->setTargetWorkDuration(newTarget.count());
- EXPECT_CALL(*mMockSession.get(), updateTargetWorkDuration(newTarget.count())).Times(0);
- std::chrono::nanoseconds actual = 21ms;
- // 100 / 105 * 21ms = 20ms
- std::chrono::nanoseconds expectedActualSent = 20ms;
- std::vector<WorkDuration> expectedDurations = {toWorkDuration(expectedActualSent, 1)};
-
- EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(_))
- .WillOnce(DoAll(
- [expectedDurations](const ::std::vector<WorkDuration>& durationsSent) {
- EXPECT_EQ(expectedDurations, durationsSent)
- << base::StringPrintf("actual sent: %s vs expected: %s",
- printWorkDurations(durationsSent).c_str(),
- printWorkDurations(expectedDurations)
- .c_str());
- },
- Return(Status::ok())));
- mWrapper->sendActualWorkDuration(actual.count(), 1);
-}
-
TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration_exceedsStaleTime) {
ASSERT_TRUE(mWrapper->supportsPowerHintSession());
@@ -269,22 +241,23 @@ TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration_exceedsStaleTime) {
auto base = toWorkDuration(100ms, 0);
// test cases with actual work durations and whether it should update hint against baseline
// 100ms
- const std::vector<std::pair<std::vector<std::pair<std::chrono::nanoseconds, nsecs_t>>, bool>>
- testCases = {{{{91ms, 100}}, true}, {{{109ms, 100}}, true}};
+ const std::vector<std::tuple<std::vector<std::pair<std::chrono::nanoseconds, nsecs_t>>,
+ std::chrono::nanoseconds, bool>>
+ testCases = {{{{100ms, 100}}, mStaleTimeout, true},
+ {{{100ms + (mAllowedDeviation / 2), 100}}, mStaleTimeout, true},
+ {{{100ms, 100}}, mStaleTimeout / 2, false}};
for (const auto& test : testCases) {
// reset actual duration
- sendActualWorkDurationGroup({base}, 80ms);
+ sendActualWorkDurationGroup({base}, mStaleTimeout);
- auto raw = test.first;
+ auto raw = std::get<0>(test);
std::vector<WorkDuration> durations(raw.size());
std::transform(raw.begin(), raw.end(), durations.begin(),
- [](std::pair<std::chrono::nanoseconds, nsecs_t> d) {
- return toWorkDuration(d.first, d.second);
- });
+ [](auto d) { return toWorkDuration(d); });
EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(durations))
- .Times(test.second ? 1 : 0);
- sendActualWorkDurationGroup(durations, 80ms);
+ .Times(std::get<2>(test) ? 1 : 0);
+ sendActualWorkDurationGroup(durations, std::get<1>(test));
verifyAndClearExpectations();
}
}
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 7823363e1e..004f31c562 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -24,6 +24,7 @@ package {
filegroup {
name: "libsurfaceflinger_mock_sources",
srcs: [
+ "mock/DisplayHardware/MockAidlPowerHalWrapper.cpp",
"mock/DisplayHardware/MockComposer.cpp",
"mock/DisplayHardware/MockHWC2.cpp",
"mock/DisplayHardware/MockIPower.cpp",
@@ -95,6 +96,7 @@ cc_test {
"LayerTest.cpp",
"LayerTestUtils.cpp",
"MessageQueueTest.cpp",
+ "PowerAdvisorTest.cpp",
"SurfaceFlinger_CreateDisplayTest.cpp",
"SurfaceFlinger_DestroyDisplayTest.cpp",
"SurfaceFlinger_DisplayModeSwitching.cpp",
diff --git a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
index ec27edac6e..67ace1af83 100644
--- a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
@@ -292,9 +292,10 @@ TEST_F(DispSyncSourceTest, waitForCallbacksWithDurationChange) {
TEST_F(DispSyncSourceTest, getLatestVsyncData) {
const nsecs_t now = systemTime();
- const nsecs_t vsyncInternalDuration = mWorkDuration.count() + mReadyDuration.count();
+ const nsecs_t expectedPresentationTime =
+ now + mWorkDuration.count() + mReadyDuration.count() + 1;
EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_))
- .WillOnce(Return(now + vsyncInternalDuration + 1));
+ .WillOnce(Return(expectedPresentationTime));
{
InSequence seq;
EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1);
@@ -306,10 +307,8 @@ TEST_F(DispSyncSourceTest, getLatestVsyncData) {
EXPECT_TRUE(mDispSyncSource);
const auto vsyncData = mDispSyncSource->getLatestVSyncData();
- ASSERT_GT(vsyncData.deadlineTimestamp, now);
- ASSERT_GT(vsyncData.expectedPresentationTime, vsyncData.deadlineTimestamp);
- EXPECT_EQ(vsyncData.deadlineTimestamp,
- vsyncData.expectedPresentationTime - vsyncInternalDuration);
+ ASSERT_EQ(vsyncData.expectedPresentationTime, expectedPresentationTime);
+ EXPECT_EQ(vsyncData.deadlineTimestamp, expectedPresentationTime - mReadyDuration.count());
}
} // namespace
diff --git a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
new file mode 100644
index 0000000000..8711a42b2b
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2022 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 "PowerAdvisorTest"
+
+#include <DisplayHardware/PowerAdvisor.h>
+#include <compositionengine/Display.h>
+#include <ftl/fake_guard.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <ui/DisplayId.h>
+#include <chrono>
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockAidlPowerHalWrapper.h"
+
+using namespace android;
+using namespace android::Hwc2::mock;
+using namespace android::hardware::power;
+using namespace std::chrono_literals;
+using namespace testing;
+
+namespace android::Hwc2::impl {
+
+class PowerAdvisorTest : public testing::Test {
+public:
+ void SetUp() override;
+ void startPowerHintSession();
+ void fakeBasicFrameTiming(nsecs_t startTime, nsecs_t vsyncPeriod);
+ void setExpectedTiming(nsecs_t startTime, nsecs_t vsyncPeriod);
+ nsecs_t getFenceWaitDelayDuration(bool skipValidate);
+
+protected:
+ TestableSurfaceFlinger mFlinger;
+ std::unique_ptr<PowerAdvisor> mPowerAdvisor;
+ NiceMock<MockAidlPowerHalWrapper>* mMockAidlWrapper;
+ nsecs_t kErrorMargin = std::chrono::nanoseconds(1ms).count();
+};
+
+void PowerAdvisorTest::SetUp() FTL_FAKE_GUARD(mPowerAdvisor->mPowerHalMutex) {
+ std::unique_ptr<MockAidlPowerHalWrapper> mockAidlWrapper =
+ std::make_unique<NiceMock<MockAidlPowerHalWrapper>>();
+ mPowerAdvisor = std::make_unique<PowerAdvisor>(*mFlinger.flinger());
+ ON_CALL(*mockAidlWrapper.get(), supportsPowerHintSession()).WillByDefault(Return(true));
+ ON_CALL(*mockAidlWrapper.get(), startPowerHintSession()).WillByDefault(Return(true));
+ mPowerAdvisor->mHalWrapper = std::move(mockAidlWrapper);
+ mMockAidlWrapper =
+ reinterpret_cast<NiceMock<MockAidlPowerHalWrapper>*>(mPowerAdvisor->mHalWrapper.get());
+}
+
+void PowerAdvisorTest::startPowerHintSession() {
+ const std::vector<int32_t> threadIds = {1, 2, 3};
+ mPowerAdvisor->enablePowerHint(true);
+ mPowerAdvisor->startPowerHintSession(threadIds);
+}
+
+void PowerAdvisorTest::setExpectedTiming(nsecs_t totalFrameTarget, nsecs_t expectedPresentTime) {
+ mPowerAdvisor->setTotalFrameTargetWorkDuration(totalFrameTarget);
+ mPowerAdvisor->setExpectedPresentTime(expectedPresentTime);
+}
+
+void PowerAdvisorTest::fakeBasicFrameTiming(nsecs_t startTime, nsecs_t vsyncPeriod) {
+ mPowerAdvisor->setCommitStart(startTime);
+ mPowerAdvisor->setFrameDelay(0);
+ mPowerAdvisor->setTargetWorkDuration(vsyncPeriod);
+}
+
+nsecs_t PowerAdvisorTest::getFenceWaitDelayDuration(bool skipValidate) {
+ return (skipValidate ? PowerAdvisor::kFenceWaitStartDelaySkippedValidate
+ : PowerAdvisor::kFenceWaitStartDelayValidated)
+ .count();
+}
+
+namespace {
+
+TEST_F(PowerAdvisorTest, hintSessionUseHwcDisplay) {
+ mPowerAdvisor->onBootFinished();
+ startPowerHintSession();
+
+ std::vector<DisplayId> displayIds{PhysicalDisplayId::fromPort(42u)};
+
+ // 60hz
+ const nsecs_t vsyncPeriod = std::chrono::nanoseconds(1s).count() / 60;
+ const nsecs_t presentDuration = std::chrono::nanoseconds(5ms).count();
+ const nsecs_t postCompDuration = std::chrono::nanoseconds(1ms).count();
+
+ nsecs_t startTime = 100;
+
+ // advisor only starts on frame 2 so do an initial no-op frame
+ fakeBasicFrameTiming(startTime, vsyncPeriod);
+ setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
+ mPowerAdvisor->setDisplays(displayIds);
+ mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
+ mPowerAdvisor->setCompositeEnd(startTime + presentDuration + postCompDuration);
+
+ // increment the frame
+ startTime += vsyncPeriod;
+
+ const nsecs_t expectedDuration = kErrorMargin + presentDuration + postCompDuration;
+ EXPECT_CALL(*mMockAidlWrapper, sendActualWorkDuration(Eq(expectedDuration), _)).Times(1);
+
+ fakeBasicFrameTiming(startTime, vsyncPeriod);
+ setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
+ mPowerAdvisor->setDisplays(displayIds);
+ mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime + 1000000, startTime + 1500000);
+ mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime + 2000000, startTime + 2500000);
+ mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
+ mPowerAdvisor->sendActualWorkDuration();
+}
+
+TEST_F(PowerAdvisorTest, hintSessionSubtractsHwcFenceTime) {
+ mPowerAdvisor->onBootFinished();
+ startPowerHintSession();
+
+ std::vector<DisplayId> displayIds{PhysicalDisplayId::fromPort(42u)};
+
+ // 60hz
+ const nsecs_t vsyncPeriod = std::chrono::nanoseconds(1s).count() / 60;
+ const nsecs_t presentDuration = std::chrono::nanoseconds(5ms).count();
+ const nsecs_t postCompDuration = std::chrono::nanoseconds(1ms).count();
+ const nsecs_t hwcBlockedDuration = std::chrono::nanoseconds(500us).count();
+
+ nsecs_t startTime = 100;
+
+ // advisor only starts on frame 2 so do an initial no-op frame
+ fakeBasicFrameTiming(startTime, vsyncPeriod);
+ setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
+ mPowerAdvisor->setDisplays(displayIds);
+ mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
+ mPowerAdvisor->setCompositeEnd(startTime + presentDuration + postCompDuration);
+
+ // increment the frame
+ startTime += vsyncPeriod;
+
+ const nsecs_t expectedDuration = kErrorMargin + presentDuration +
+ getFenceWaitDelayDuration(false) - hwcBlockedDuration + postCompDuration;
+ EXPECT_CALL(*mMockAidlWrapper, sendActualWorkDuration(Eq(expectedDuration), _)).Times(1);
+
+ fakeBasicFrameTiming(startTime, vsyncPeriod);
+ setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
+ mPowerAdvisor->setDisplays(displayIds);
+ mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime + 1000000, startTime + 1500000);
+ mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime + 2000000, startTime + 3000000);
+ // now report the fence as having fired during the display HWC time
+ mPowerAdvisor->setSfPresentTiming(startTime + 2000000 + hwcBlockedDuration,
+ startTime + presentDuration);
+ mPowerAdvisor->sendActualWorkDuration();
+}
+
+TEST_F(PowerAdvisorTest, hintSessionUsingSecondaryVirtualDisplays) {
+ mPowerAdvisor->onBootFinished();
+ startPowerHintSession();
+
+ std::vector<DisplayId> displayIds{PhysicalDisplayId::fromPort(42u), GpuVirtualDisplayId(0),
+ GpuVirtualDisplayId(1)};
+
+ // 60hz
+ const nsecs_t vsyncPeriod = std::chrono::nanoseconds(1s).count() / 60;
+ // make present duration much later than the hwc display by itself will account for
+ const nsecs_t presentDuration = std::chrono::nanoseconds(10ms).count();
+ const nsecs_t postCompDuration = std::chrono::nanoseconds(1ms).count();
+
+ nsecs_t startTime = 100;
+
+ // advisor only starts on frame 2 so do an initial no-op frame
+ fakeBasicFrameTiming(startTime, vsyncPeriod);
+ setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
+ mPowerAdvisor->setDisplays(displayIds);
+ mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
+ mPowerAdvisor->setCompositeEnd(startTime + presentDuration + postCompDuration);
+
+ // increment the frame
+ startTime += vsyncPeriod;
+
+ const nsecs_t expectedDuration = kErrorMargin + presentDuration + postCompDuration;
+ EXPECT_CALL(*mMockAidlWrapper, sendActualWorkDuration(Eq(expectedDuration), _)).Times(1);
+
+ fakeBasicFrameTiming(startTime, vsyncPeriod);
+ setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
+ mPowerAdvisor->setDisplays(displayIds);
+
+ // don't report timing for the gpu displays since they don't use hwc
+ mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime + 1000000, startTime + 1500000);
+ mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime + 2000000, startTime + 2500000);
+ mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
+ mPowerAdvisor->sendActualWorkDuration();
+}
+
+} // namespace
+} // namespace android::Hwc2::impl
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index fcde532b85..188fd58dea 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -564,9 +564,10 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_mu
TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60,
{.frameRateMultipleThreshold = 120});
- std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}, {.weight = 1.f}};
auto& lr1 = layers[0];
auto& lr2 = layers[1];
+ auto& lr3 = layers[2];
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitDefault;
@@ -639,6 +640,48 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_mu
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.name = "90Hz ExplicitExactOrMultiple";
EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+
+ lr1.desiredRefreshRate = 24_Hz;
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.name = "24Hz ExplicitExactOrMultiple";
+ lr2.vote = LayerVoteType::Max;
+ lr2.name = "Max";
+ EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
+
+ lr1.desiredRefreshRate = 24_Hz;
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.name = "24Hz ExplicitExactOrMultiple";
+ lr2.desiredRefreshRate = 120_Hz;
+ lr2.vote = LayerVoteType::ExplicitDefault;
+ lr2.name = "120Hz ExplicitDefault";
+ EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
+
+ lr1.desiredRefreshRate = 24_Hz;
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.name = "24Hz ExplicitExactOrMultiple";
+ lr2.desiredRefreshRate = 120_Hz;
+ lr2.vote = LayerVoteType::ExplicitExact;
+ lr2.name = "120Hz ExplicitExact";
+ EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
+
+ lr1.desiredRefreshRate = 10_Hz;
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.name = "30Hz ExplicitExactOrMultiple";
+ lr2.desiredRefreshRate = 120_Hz;
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.name = "120Hz ExplicitExact";
+ EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
+
+ lr1.desiredRefreshRate = 30_Hz;
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.name = "30Hz ExplicitExactOrMultiple";
+ lr2.desiredRefreshRate = 30_Hz;
+ lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr2.name = "30Hz ExplicitExactOrMultiple";
+ lr3.vote = LayerVoteType::Heuristic;
+ lr3.desiredRefreshRate = 120_Hz;
+ lr3.name = "120Hz Heuristic";
+ EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60) {
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index aab27957c3..93c809e2fd 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -159,8 +159,8 @@ TEST_F(SchedulerTest, chooseRefreshRateForContentIsNoopWhenModeSwitchingIsNotSup
mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
- constexpr bool kPowerStateNormal = true;
- mScheduler->setDisplayPowerState(kPowerStateNormal);
+ constexpr hal::PowerMode kPowerModeOn = hal::PowerMode::ON;
+ mScheduler->setDisplayPowerMode(kPowerModeOn);
constexpr uint32_t kDisplayArea = 999'999;
mScheduler->onActiveDisplayAreaChanged(kDisplayArea);
@@ -226,8 +226,8 @@ TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) {
mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
- constexpr bool kPowerStateNormal = true;
- mScheduler->setDisplayPowerState(kPowerStateNormal);
+ constexpr hal::PowerMode kPowerModeOn = hal::PowerMode::ON;
+ mScheduler->setDisplayPowerMode(kPowerModeOn);
constexpr uint32_t kDisplayArea = 999'999;
mScheduler->onActiveDisplayAreaChanged(kDisplayArea);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
index 8de9e4be29..2c9888dd8e 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
@@ -74,6 +74,7 @@ void SurfaceFlingerPowerHintTest::SetUp() {
mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
mFlinger.setupTimeStats(std::shared_ptr<TimeStats>(mTimeStats));
mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
+ mFlinger.setPowerHintSessionMode(true, true);
mFlinger.setupPowerAdvisor(std::unique_ptr<Hwc2::PowerAdvisor>(mPowerAdvisor));
static constexpr bool kIsPrimary = true;
FakeHwcDisplayInjector(DEFAULT_DISPLAY_ID, hal::DisplayType::PHYSICAL, kIsPrimary)
@@ -96,6 +97,7 @@ void SurfaceFlingerPowerHintTest::SetUp() {
.setNativeWindow(mNativeWindow)
.setPowerMode(hal::PowerMode::ON)
.inject();
+ mFlinger.mutableActiveDisplayToken() = mDisplay->getDisplayToken();
}
void SurfaceFlingerPowerHintTest::setupScheduler() {
@@ -142,10 +144,30 @@ TEST_F(SurfaceFlingerPowerHintTest, sendDurationsIncludingHwcWaitTime) {
std::this_thread::sleep_for(mockHwcRunTime);
return hardware::graphics::composer::V2_1::Error::NONE;
});
- EXPECT_CALL(*mPowerAdvisor,
- sendActualWorkDuration(Gt(mockHwcRunTime.count()),
- Gt(now + mockHwcRunTime.count())))
+ EXPECT_CALL(*mPowerAdvisor, sendActualWorkDuration()).Times(1);
+ static constexpr bool kVsyncId = 123; // arbitrary
+ mFlinger.commitAndComposite(now, kVsyncId, now + mockVsyncPeriod.count());
+}
+
+TEST_F(SurfaceFlingerPowerHintTest, inactiveOnDisplayDoze) {
+ ON_CALL(*mPowerAdvisor, usePowerHintSession()).WillByDefault(Return(true));
+
+ mDisplay->setPowerMode(hal::PowerMode::DOZE);
+
+ const std::chrono::nanoseconds mockVsyncPeriod = 15ms;
+ EXPECT_CALL(*mPowerAdvisor, setTargetWorkDuration(_)).Times(0);
+
+ const nsecs_t now = systemTime();
+ const std::chrono::nanoseconds mockHwcRunTime = 20ms;
+ EXPECT_CALL(*mDisplaySurface,
+ prepareFrame(compositionengine::DisplaySurface::CompositionType::Hwc))
.Times(1);
+ EXPECT_CALL(*mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _))
+ .WillOnce([mockHwcRunTime] {
+ std::this_thread::sleep_for(mockHwcRunTime);
+ return hardware::graphics::composer::V2_1::Error::NONE;
+ });
+ EXPECT_CALL(*mPowerAdvisor, sendActualWorkDuration()).Times(0);
static constexpr bool kVsyncId = 123; // arbitrary
mFlinger.commitAndComposite(now, kVsyncId, now + mockVsyncPeriod.count());
}
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index f1a69fb46e..283f9ca77b 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -330,6 +330,10 @@ public:
layer->mDrawingParent = drawingParent;
}
+ void setPowerHintSessionMode(bool early, bool late) {
+ mFlinger->mPowerHintSessionMode = {.late = late, .early = early};
+ }
+
/* ------------------------------------------------------------------------
* Forwarding for functions being tested
*/
@@ -741,6 +745,7 @@ public:
mHwcDisplayId(hwcDisplayId) {
mCreationArgs.connectionType = connectionType;
mCreationArgs.isPrimary = isPrimary;
+ mCreationArgs.initialPowerMode = hal::PowerMode::ON;
}
sp<IBinder> token() const { return mDisplayToken; }
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index 4eb90558ab..30a3f9a4a7 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -349,6 +349,23 @@ TEST_F(VSyncReactorTest, addResyncSamplePeriodChanges) {
}
}
+TEST_F(VSyncReactorTest, addHwVsyncTimestampDozePreempt) {
+ bool periodFlushed = false;
+ nsecs_t const newPeriod = 4000;
+
+ mReactor.startPeriodTransition(newPeriod);
+
+ auto time = 0;
+ // If the power mode is not DOZE or DOZE_SUSPEND, it is still collecting timestamps.
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed));
+ EXPECT_FALSE(periodFlushed);
+
+ // Set power mode to DOZE to trigger period flushing.
+ mReactor.setDisplayPowerMode(hal::PowerMode::DOZE);
+ EXPECT_FALSE(mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(periodFlushed);
+}
+
TEST_F(VSyncReactorTest, addPresentFenceWhileAwaitingPeriodConfirmationRequestsHwVsync) {
auto time = 0;
bool periodFlushed = false;
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.cpp
new file mode 100644
index 0000000000..5049b1d367
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MockAidlPowerHalWrapper.h"
+#include "MockIPower.h"
+
+namespace android::Hwc2::mock {
+
+MockAidlPowerHalWrapper::MockAidlPowerHalWrapper()
+ : AidlPowerHalWrapper(sp<testing::NiceMock<MockIPower>>::make()){};
+MockAidlPowerHalWrapper::~MockAidlPowerHalWrapper() = default;
+
+} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h
new file mode 100644
index 0000000000..657ced3d08
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "DisplayHardware/PowerAdvisor.h"
+
+namespace android {
+namespace hardware {
+namespace power {
+class IPower;
+}
+} // namespace hardware
+} // namespace android
+
+namespace android::Hwc2::mock {
+
+class MockAidlPowerHalWrapper : public Hwc2::impl::AidlPowerHalWrapper {
+public:
+ MockAidlPowerHalWrapper();
+ ~MockAidlPowerHalWrapper() override;
+ MOCK_METHOD(bool, setExpensiveRendering, (bool enabled), (override));
+ MOCK_METHOD(bool, notifyDisplayUpdateImminent, (), (override));
+ MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
+ MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override));
+ MOCK_METHOD(void, restartPowerHintSession, (), (override));
+ MOCK_METHOD(void, setPowerHintSessionThreadIds, (const std::vector<int32_t>& threadIds),
+ (override));
+ MOCK_METHOD(bool, startPowerHintSession, (), (override));
+ MOCK_METHOD(void, setTargetWorkDuration, (nsecs_t targetDuration), (override));
+ MOCK_METHOD(void, sendActualWorkDuration, (nsecs_t actualDuration, nsecs_t timestamp),
+ (override));
+ MOCK_METHOD(bool, shouldReconnectHAL, (), (override));
+};
+
+} // namespace android::Hwc2::mock \ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
index c598cbc28e..aede250db5 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
@@ -36,11 +36,33 @@ public:
MOCK_METHOD(bool, usePowerHintSession, (), (override));
MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override));
- MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDurationNanos), (override));
- MOCK_METHOD(void, sendActualWorkDuration, (int64_t actualDurationNanos, nsecs_t timestamp),
- (override));
+ MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDuration), (override));
+ MOCK_METHOD(void, sendActualWorkDuration, (), (override));
+ MOCK_METHOD(void, sendPredictedWorkDuration, (), (override));
MOCK_METHOD(void, enablePowerHint, (bool enabled), (override));
MOCK_METHOD(bool, startPowerHintSession, (const std::vector<int32_t>& threadIds), (override));
+ MOCK_METHOD(void, setGpuFenceTime,
+ (DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime), (override));
+ MOCK_METHOD(void, setHwcValidateTiming,
+ (DisplayId displayId, nsecs_t valiateStartTime, nsecs_t validateEndTime),
+ (override));
+ MOCK_METHOD(void, setHwcPresentTiming,
+ (DisplayId displayId, nsecs_t presentStartTime, nsecs_t presentEndTime),
+ (override));
+ MOCK_METHOD(void, setSkippedValidate, (DisplayId displayId, bool skipped), (override));
+ MOCK_METHOD(void, setRequiresClientComposition,
+ (DisplayId displayId, bool requiresClientComposition), (override));
+ MOCK_METHOD(void, setExpectedPresentTime, (nsecs_t expectedPresentTime), (override));
+ MOCK_METHOD(void, setSfPresentTiming, (nsecs_t presentFenceTime, nsecs_t presentEndTime),
+ (override));
+ MOCK_METHOD(void, setHwcPresentDelayedTime,
+ (DisplayId displayId,
+ std::chrono::steady_clock::time_point earliestFrameStartTime));
+ MOCK_METHOD(void, setFrameDelay, (nsecs_t frameDelayDuration), (override));
+ MOCK_METHOD(void, setCommitStart, (nsecs_t commitStartTime), (override));
+ MOCK_METHOD(void, setCompositeEnd, (nsecs_t compositeEndtime), (override));
+ MOCK_METHOD(void, setDisplays, (std::vector<DisplayId> & displayIds), (override));
+ MOCK_METHOD(void, setTotalFrameTargetWorkDuration, (int64_t targetDuration), (override));
};
} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
index 314f681545..4ef91dacb2 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
@@ -31,6 +31,7 @@ public:
MOCK_METHOD3(addHwVsyncTimestamp, bool(nsecs_t, std::optional<nsecs_t>, bool*));
MOCK_METHOD1(startPeriodTransition, void(nsecs_t));
MOCK_METHOD1(setIgnorePresentFences, void(bool));
+ MOCK_METHOD(void, setDisplayPowerMode, (hal::PowerMode), (override));
MOCK_CONST_METHOD1(dump, void(std::string&));
};