summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-10-03 23:24:32 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-10-03 23:24:32 +0000
commit3ca328fc3a24e3772d8a72d9e317c8e778091b4e (patch)
treeffebaaf45ab249d55507d07426c68fe221f76794
parent1cf6d7f4cacfed91900c1af5d1a2872ae04dc8ff (diff)
parent293041cac77386d5a782de721a1ee63a34556965 (diff)
downloadnative-3ca328fc3a24e3772d8a72d9e317c8e778091b4e.tar.gz
Snap for 9133013 from 293041cac77386d5a782de721a1ee63a34556965 to tm-qpr2-release
Change-Id: Ifb27e6503863a60f0ad1a7aa0eddb8b3e5c041b9
-rw-r--r--include/android/choreographer.h62
-rw-r--r--include/android/surface_control.h18
-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/gui/BLASTBufferQueue.cpp28
-rw-r--r--libs/gui/SurfaceComposerClient.cpp6
-rw-r--r--libs/gui/include/gui/BLASTBufferQueue.h9
-rw-r--r--libs/gui/include/gui/SurfaceComposerClient.h7
-rw-r--r--libs/gui/include/gui/test/CallbackUtils.h3
-rw-r--r--libs/input/Android.bp1
-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/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/UnwantedInteractionBlocker.cpp123
-rw-r--r--services/inputflinger/UnwantedInteractionBlocker.h29
-rw-r--r--services/inputflinger/dispatcher/DebugConfig.h5
-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.cpp60
-rw-r--r--services/inputflinger/reader/mapper/CursorInputMapper.h4
-rw-r--r--services/inputflinger/reader/mapper/TouchInputMapper.cpp46
-rw-r--r--services/inputflinger/tests/Android.bp1
-rw-r--r--services/inputflinger/tests/InputReader_test.cpp246
-rw-r--r--services/inputflinger/tests/TestInputListener.cpp7
-rw-r--r--services/inputflinger/tests/TestInputListener.h3
-rw-r--r--services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp153
-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/impl/Display.h1
-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/src/Display.cpp25
-rw-r--r--services/surfaceflinger/CompositionEngine/src/Output.cpp3
-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/MockPowerAdvisor.h3
-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.cpp27
-rw-r--r--services/surfaceflinger/DisplayDevice.h9
-rw-r--r--services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp63
-rw-r--r--services/surfaceflinger/DisplayHardware/PowerAdvisor.h17
-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/SurfaceFlinger.cpp59
-rw-r--r--services/surfaceflinger/SurfaceFlinger.h3
-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/SurfaceFlinger_PowerHintTest.cpp24
-rw-r--r--services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h1
-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.h3
70 files changed, 1622 insertions, 519 deletions
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/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/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index aba81f60d8..a51bbb1553 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -307,7 +307,6 @@ void BLASTBufferQueue::transactionCommittedCallback(nsecs_t /*latchTime*/,
BQA_LOGE("No matching SurfaceControls found: mSurfaceControlsWithPendingCallback was "
"empty.");
}
-
decStrong((void*)transactionCommittedCallbackThunk);
}
}
@@ -350,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");
}
@@ -390,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
@@ -408,7 +428,11 @@ 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) {
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 9358e29030..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]
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 9d287910a5..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);
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index b598b43403..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 =
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/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/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/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/UnwantedInteractionBlocker.cpp b/services/inputflinger/UnwantedInteractionBlocker.cpp
index 4e0f0c3bf5..ec41025e9d 100644
--- a/services/inputflinger/UnwantedInteractionBlocker.cpp
+++ b/services/inputflinger/UnwantedInteractionBlocker.cpp
@@ -77,8 +77,7 @@ 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) {
@@ -99,17 +98,15 @@ static bool isPalmRejectionEnabled() {
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;
+ 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) {
@@ -144,23 +141,6 @@ static int32_t resolveActionForPointer(uint8_t pointerIndex, int32_t action) {
return AMOTION_EVENT_ACTION_MOVE;
}
-/**
- * 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);
@@ -206,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())) {
@@ -562,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;
@@ -609,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;
@@ -642,6 +626,7 @@ 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);
@@ -653,14 +638,14 @@ std::vector<NotifyMotionArgs> PalmRejector::processMotion(const NotifyMotionArgs
}
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);
@@ -669,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);
@@ -691,7 +708,7 @@ std::vector<NotifyMotionArgs> PalmRejector::processMotion(const NotifyMotionArgs
return argsWithoutUnwantedPointers;
}
-const AndroidPalmFilterDeviceInfo& PalmRejector::getPalmFilterDeviceInfo() {
+const AndroidPalmFilterDeviceInfo& PalmRejector::getPalmFilterDeviceInfo() const {
return mDeviceInfo;
}
diff --git a/services/inputflinger/UnwantedInteractionBlocker.h b/services/inputflinger/UnwantedInteractionBlocker.h
index 8ff965851f..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);
@@ -147,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/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 91dc61923b..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.
@@ -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)));
@@ -201,21 +204,34 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration*
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. When pointer capture is enabled, we do not apply any
// rotations and report values directly from the input device.
- if (!isOrientedDevice && mParameters.mode != Parameters::Mode::POINTER_RELATIVE) {
- std::optional<DisplayViewport> internalViewport =
- config->getDisplayViewportByType(ViewportType::INTERNAL);
- if (internalViewport) {
- mOrientation = getInverseRotation(internalViewport->orientation);
+ if (!isOrientedDevice && mDisplayId &&
+ mParameters.mode != Parameters::Mode::POINTER_RELATIVE) {
+ if (auto viewport = config->getDisplayViewportById(*mDisplayId); viewport) {
+ mOrientation = getInverseRotation(viewport->orientation);
}
}
@@ -279,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;
@@ -324,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) {
@@ -348,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);
@@ -370,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) {
@@ -391,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,
@@ -403,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,
@@ -416,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,
@@ -432,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,
@@ -447,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,
@@ -459,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();
@@ -474,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 75aeffb846..60b3dd9ee0 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -111,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/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index c36704bf15..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);
@@ -5011,33 +5063,76 @@ TEST_F(CursorInputMapperTest, PointerCaptureDisablesOrientationChanges) {
ASSERT_EQ(20, args.pointerCoords[0].getY());
}
-TEST_F(CursorInputMapperTest, Process_ShouldHandleDisplayId) {
+TEST_F(CursorInputMapperTest, ConfigureDisplayId_NoAssociatedViewport) {
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);
+ // 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, 800 - 1, 480 - 1);
+ mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
mFakePointerController->setPosition(100, 200);
mFakePointerController->setButtonState(0);
- NotifyMotionArgs args;
+ // 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(&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_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_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));
+}
+
+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 ---
@@ -6690,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 {
@@ -8547,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) {
@@ -8725,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 8af387174a..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,23 @@ 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();
}
@@ -256,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}}});
}
@@ -271,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);
}
/**
@@ -286,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);
}
/**
@@ -301,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);
}
/**
@@ -316,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);
}
/**
@@ -332,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);
}
/**
@@ -573,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;
/**
@@ -672,7 +803,7 @@ TEST_F(PalmRejectorTest, TwoPointersAreCanceled) {
{{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);
@@ -849,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);
@@ -890,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(
@@ -936,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(
@@ -970,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/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index fa7bc5da7f..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;
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/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index ea856e4859..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();
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index bc11d87ebd..b724daa8ce 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -1494,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/MockPowerAdvisor.h b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
index 8c164edded..c8bd5e436c 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
@@ -55,7 +55,8 @@ public:
MOCK_METHOD(void, setRequiresClientComposition,
(DisplayId displayId, bool requiresClientComposition), (override));
MOCK_METHOD(void, setExpectedPresentTime, (nsecs_t expectedPresentTime), (override));
- MOCK_METHOD(void, setPresentFenceTime, (nsecs_t presentFenceTime), (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));
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 86809007b4..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) {
@@ -325,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;
@@ -373,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) {
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 2161436d44..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;
@@ -281,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;
@@ -366,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/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index b9d4753f45..a0350b717a 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -315,7 +315,8 @@ void PowerAdvisor::setExpectedPresentTime(nsecs_t expectedPresentTime) {
mExpectedPresentTimes.append(expectedPresentTime);
}
-void PowerAdvisor::setPresentFenceTime(nsecs_t presentFenceTime) {
+void PowerAdvisor::setSfPresentTiming(nsecs_t presentFenceTime, nsecs_t presentEndTime) {
+ mLastSfPresentEndTime = presentEndTime;
mLastPresentFenceTime = presentFenceTime;
}
@@ -334,13 +335,7 @@ void PowerAdvisor::setCommitStart(nsecs_t commitStartTime) {
}
void PowerAdvisor::setCompositeEnd(nsecs_t compositeEnd) {
- mLastCompositeEndTime = compositeEnd;
- // calculate the postcomp time here as well
- std::vector<DisplayId>&& displays = getOrderedDisplayIds(&DisplayTimingData::hwcPresentEndTime);
- DisplayTimingData& timingData = mDisplayTimingData[displays.back()];
- mLastPostcompDuration = compositeEnd -
- (timingData.skippedValidate ? *timingData.hwcValidateEndTime
- : *timingData.hwcPresentEndTime);
+ mLastPostcompDuration = compositeEnd - mLastSfPresentEndTime;
}
void PowerAdvisor::setDisplays(std::vector<DisplayId>& displayIds) {
@@ -399,7 +394,7 @@ std::optional<nsecs_t> PowerAdvisor::estimateWorkDuration(bool earlyHint) {
getOrderedDisplayIds(&DisplayTimingData::hwcPresentStartTime);
DisplayTimeline referenceTiming, estimatedTiming;
- // Iterate over the displays in the same order they are presented
+ // Iterate over the displays that use hwc in the same order they are presented
for (DisplayId displayId : displayIds) {
if (mDisplayTimingData.count(displayId) == 0) {
continue;
@@ -451,8 +446,11 @@ std::optional<nsecs_t> PowerAdvisor::estimateWorkDuration(bool earlyHint) {
}
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
@@ -460,7 +458,10 @@ std::optional<nsecs_t> PowerAdvisor::estimateWorkDuration(bool earlyHint) {
std::max(estimatedEndTime, estimatedGpuEndTime.value_or(0)) - mCommitStartTimes[0];
// We finish SurfaceFlinger when post-composition finishes, so add that in here
- nsecs_t flingerDuration = estimatedEndTime + mLastPostcompDuration - mCommitStartTimes[0];
+ 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);
@@ -640,9 +641,8 @@ AidlPowerHalWrapper::AidlPowerHalWrapper(sp<IPower> powerHal) : mPowerHal(std::m
mSupportsPowerHint = checkPowerHintSessionSupported();
- mAllowedActualDeviation =
- base::GetIntProperty<nsecs_t>("debug.sf.allowed_actual_deviation",
- std::chrono::nanoseconds(250us).count());
+ // Currently set to 0 to disable rate limiter by default
+ mAllowedActualDeviation = base::GetIntProperty<nsecs_t>("debug.sf.allowed_actual_deviation", 0);
}
AidlPowerHalWrapper::~AidlPowerHalWrapper() {
@@ -838,10 +838,7 @@ const bool AidlPowerHalWrapper::sTraceHintSessionData =
base::GetBoolProperty(std::string("debug.sf.trace_hint_sessions"), false);
PowerAdvisor::HalWrapper* PowerAdvisor::getPowerHal() {
- static std::unique_ptr<HalWrapper> sHalWrapper = nullptr;
- static bool sHasHal = true;
-
- if (!sHasHal) {
+ if (!mHasHal) {
return nullptr;
}
@@ -849,57 +846,57 @@ PowerAdvisor::HalWrapper* PowerAdvisor::getPowerHal() {
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 (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
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
- sHalWrapper->setPowerHintSessionThreadIds(oldPowerHintSessionThreadIds);
+ mHalWrapper->setPowerHintSessionThreadIds(oldPowerHintSessionThreadIds);
// Only set duration and start if duration is defined
if (oldTargetWorkDuration.has_value()) {
- sHalWrapper->setTargetWorkDuration(*oldTargetWorkDuration);
+ 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 98921b0861..6e25f787d7 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -72,8 +72,8 @@ public:
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 once it's known at the end of the frame
- virtual void setPresentFenceTime(nsecs_t presentFenceTime) = 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;
@@ -142,7 +142,7 @@ public:
void setSkippedValidate(DisplayId displayId, bool skipped) override;
void setRequiresClientComposition(DisplayId displayId, bool requiresClientComposition) override;
void setExpectedPresentTime(nsecs_t expectedPresentTime) override;
- void setPresentFenceTime(nsecs_t presentFenceTime) override;
+ void setSfPresentTiming(nsecs_t presentFenceTime, nsecs_t presentEndTime) override;
void setHwcPresentDelayedTime(
DisplayId displayId,
std::chrono::steady_clock::time_point earliestFrameStartTime) override;
@@ -154,6 +154,13 @@ public:
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;
@@ -245,8 +252,6 @@ private:
// Current frame's delay
nsecs_t mFrameDelayDuration = 0;
- // Last frame's composite end time
- nsecs_t mLastCompositeEndTime = -1;
// Last frame's post-composition duration
nsecs_t mLastPostcompDuration = 0;
// Buffer of recent commit start times
@@ -255,6 +260,8 @@ private:
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
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/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 8621d31d91..0e1acb4154 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2085,7 +2085,11 @@ bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expected
}
// Save this once per commit + composite to ensure consistency
- mPowerHintSessionEnabled = mPowerAdvisor->usePowerHintSession();
+ // 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
@@ -2242,7 +2246,8 @@ void SurfaceFlinger::composite(nsecs_t frameTime, int64_t vsyncId)
// Send a power hint hint after presentation is finished
if (mPowerHintSessionEnabled) {
- mPowerAdvisor->setPresentFenceTime(mPreviousPresentFences[0].fenceTime->getSignalTime());
+ mPowerAdvisor->setSfPresentTiming(mPreviousPresentFences[0].fenceTime->getSignalTime(),
+ systemTime());
if (mPowerHintSessionMode.late) {
mPowerAdvisor->sendActualWorkDuration();
}
@@ -2884,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);
@@ -4865,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;
}
@@ -4882,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);
@@ -4913,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);
}
@@ -4927,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);
}
@@ -4999,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__);
@@ -5011,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);
}
}
@@ -5310,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);
@@ -5361,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());
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 83134a2ebc..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;
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/SurfaceFlinger_PowerHintTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
index c2d87f2484..2c9888dd8e 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
@@ -97,6 +97,7 @@ void SurfaceFlingerPowerHintTest::SetUp() {
.setNativeWindow(mNativeWindow)
.setPowerMode(hal::PowerMode::ON)
.inject();
+ mFlinger.mutableActiveDisplayToken() = mDisplay->getDisplayToken();
}
void SurfaceFlingerPowerHintTest::setupScheduler() {
@@ -148,5 +149,28 @@ TEST_F(SurfaceFlingerPowerHintTest, sendDurationsIncludingHwcWaitTime) {
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());
+}
+
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index b70fdcd93e..283f9ca77b 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -745,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/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 d6dca45188..aede250db5 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
@@ -53,7 +53,8 @@ public:
MOCK_METHOD(void, setRequiresClientComposition,
(DisplayId displayId, bool requiresClientComposition), (override));
MOCK_METHOD(void, setExpectedPresentTime, (nsecs_t expectedPresentTime), (override));
- MOCK_METHOD(void, setPresentFenceTime, (nsecs_t presentFenceTime), (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));