Merge tag 'android-security-13.0.0_r14' into staging/lineage-20.0_merge-android-security-13.0.0_r14
Android Security 13.0.0 Release 14 (11228180)
# -----BEGIN PGP SIGNATURE-----
#
# iF0EABECAB0WIQRDQNE1cO+UXoOBCWTorT+BmrEOeAUCZcEefgAKCRDorT+BmrEO
# eBZMAJ9X5pHMA9vWVMGpuRqqjhezxtzRrACeNhs//JRYL35m8RfQBtBziwtsEQQ=
# =v03+
# -----END PGP SIGNATURE-----
# gpg: Signature made Mon Feb 5 19:44:30 2024 EET
# gpg: using DSA key 4340D13570EF945E83810964E8AD3F819AB10E78
# gpg: Good signature from "The Android Open Source Project <initial-contribution@android.com>" [marginal]
# gpg: initial-contribution@android.com: Verified 2266 signatures in the past
# 2 years. Encrypted 4 messages in the past 2 years.
# gpg: WARNING: This key is not certified with sufficiently trusted signatures!
# gpg: It is not certain that the signature belongs to the owner.
# Primary key fingerprint: 4340 D135 70EF 945E 8381 0964 E8AD 3F81 9AB1 0E78
# By Keith Mok
# Via Android Build Coastguard Worker
* tag 'android-security-13.0.0_r14':
File size seal for memory mapped region
Change-Id: I3052f024da635dc39548a6be9a4e0b107f16a81b
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 5267b02..d0b5d59 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -295,8 +295,12 @@
write /sys/kernel/debug/tracing/synthetic_events "rss_stat_throttled unsigned int mm_id; unsigned int curr; int member; long size"
# allow creating event triggers
- chmod 0666 /sys/kernel/debug/tracing/events/kmem/rss_stat/trigger
chmod 0666 /sys/kernel/tracing/events/kmem/rss_stat/trigger
+ chmod 0666 /sys/kernel/debug/tracing/events/kmem/rss_stat/trigger
+
+ # allow enabling rss_stat_throttled
+ chmod 0666 /sys/kernel/tracing/events/synthetic/rss_stat_throttled/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/synthetic/rss_stat_throttled/enable
# Only create the tracing instance if persist.mm_events.enabled
# Attempting to remove the tracing instance after it has been created
diff --git a/cmds/dumpstate/DumpstateInternal.cpp b/cmds/dumpstate/DumpstateInternal.cpp
index 3091f6b..6f7fea3 100644
--- a/cmds/dumpstate/DumpstateInternal.cpp
+++ b/cmds/dumpstate/DumpstateInternal.cpp
@@ -162,17 +162,16 @@
return 0;
}
bool newline = false;
+ int poll_timeout_ms = 30 * 1000;
while (true) {
- uint64_t start_time = Nanotime();
pollfd fds[] = { { .fd = fd, .events = POLLIN } };
- int ret = TEMP_FAILURE_RETRY(poll(fds, arraysize(fds), 30 * 1000));
+ int ret = TEMP_FAILURE_RETRY(poll(fds, arraysize(fds), poll_timeout_ms));
if (ret == -1) {
dprintf(out_fd, "*** %s: poll failed: %s\n", path, strerror(errno));
newline = true;
break;
- } else if (ret == 0) {
- uint64_t elapsed = Nanotime() - start_time;
- dprintf(out_fd, "*** %s: Timed out after %.3fs\n", path, (float)elapsed / NANOS_PER_SEC);
+ } else if (ret == 0 && poll_timeout_ms != 0) {
+ dprintf(out_fd, "*** %s: Timed out after %ds\n", path, poll_timeout_ms / 1000 );
newline = true;
break;
} else {
@@ -189,6 +188,7 @@
break;
}
}
+ poll_timeout_ms = 0;
}
if (!newline) dprintf(out_fd, "\n");
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 2b94b71..ec1e89b 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -3172,6 +3172,15 @@
"", {"cmd", service, "tracing", "save-for-bugreport"},
CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build());
}
+ // Additionally, include the proto logging from WMShell.
+ RunCommand(
+ // Empty name because it's not intended to be classified as a bugreport section.
+ // Actual logging files can be found as "/data/misc/wmtrace/shell_log.winscope"
+ // in the bugreport.
+ "", {"dumpsys", "activity", "service", "SystemUIService",
+ "WMShell", "protolog", "save-for-bugreport"},
+ CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build());
+
}
void Dumpstate::onUiIntensiveBugreportDumpsFinished(int32_t calling_uid) {
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index a49f563..6d3f7c3 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -100,6 +100,8 @@
static constexpr const char* kDataMirrorCePath = "/data_mirror/data_ce";
static constexpr const char* kDataMirrorDePath = "/data_mirror/data_de";
+static constexpr const char* kMiscMirrorCePath = "/data_mirror/misc_ce";
+static constexpr const char* kMiscMirrorDePath = "/data_mirror/misc_de";
static constexpr const int MIN_RESTRICTED_HOME_SDK_VERSION = 24; // > M
@@ -3539,16 +3541,28 @@
std::string mirrorVolCePath(StringPrintf("%s/%s", kDataMirrorCePath, uuid_));
if (fs_prepare_dir(mirrorVolCePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) {
- return error("Failed to create CE mirror");
+ return error("Failed to create CE data mirror");
}
std::string mirrorVolDePath(StringPrintf("%s/%s", kDataMirrorDePath, uuid_));
if (fs_prepare_dir(mirrorVolDePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) {
- return error("Failed to create DE mirror");
+ return error("Failed to create DE data mirror");
+ }
+
+ std::string mirrorVolMiscCePath(StringPrintf("%s/%s", kMiscMirrorCePath, uuid_));
+ if (fs_prepare_dir(mirrorVolMiscCePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) {
+ return error("Failed to create CE misc mirror");
+ }
+
+ std::string mirrorVolMiscDePath(StringPrintf("%s/%s", kMiscMirrorDePath, uuid_));
+ if (fs_prepare_dir(mirrorVolMiscDePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) {
+ return error("Failed to create DE misc mirror");
}
auto cePath = StringPrintf("%s/user", create_data_path(uuid_).c_str());
auto dePath = StringPrintf("%s/user_de", create_data_path(uuid_).c_str());
+ auto miscCePath = StringPrintf("%s/misc_ce", create_data_path(uuid_).c_str());
+ auto miscDePath = StringPrintf("%s/misc_de", create_data_path(uuid_).c_str());
if (access(cePath.c_str(), F_OK) != 0) {
return error("Cannot access CE path: " + cePath);
@@ -3556,6 +3570,12 @@
if (access(dePath.c_str(), F_OK) != 0) {
return error("Cannot access DE path: " + dePath);
}
+ if (access(miscCePath.c_str(), F_OK) != 0) {
+ return error("Cannot access misc CE path: " + cePath);
+ }
+ if (access(miscDePath.c_str(), F_OK) != 0) {
+ return error("Cannot access misc DE path: " + dePath);
+ }
struct stat ceStat, mirrorCeStat;
if (stat(cePath.c_str(), &ceStat) != 0) {
@@ -3583,6 +3603,21 @@
MS_NOSUID | MS_NODEV | MS_NOATIME | MS_BIND | MS_NOEXEC, nullptr)) == -1) {
return error("Failed to mount " + mirrorVolDePath);
}
+
+ // Mount misc CE mirror
+ if (TEMP_FAILURE_RETRY(mount(miscCePath.c_str(), mirrorVolMiscCePath.c_str(), NULL,
+ MS_NOSUID | MS_NODEV | MS_NOATIME | MS_BIND | MS_NOEXEC,
+ nullptr)) == -1) {
+ return error("Failed to mount " + mirrorVolMiscCePath);
+ }
+
+ // Mount misc DE mirror
+ if (TEMP_FAILURE_RETRY(mount(miscDePath.c_str(), mirrorVolMiscDePath.c_str(), NULL,
+ MS_NOSUID | MS_NODEV | MS_NOATIME | MS_BIND | MS_NOEXEC,
+ nullptr)) == -1) {
+ return error("Failed to mount " + mirrorVolMiscDePath);
+ }
+
return ok();
}
@@ -3605,6 +3640,8 @@
std::string mirrorCeVolPath(StringPrintf("%s/%s", kDataMirrorCePath, uuid_));
std::string mirrorDeVolPath(StringPrintf("%s/%s", kDataMirrorDePath, uuid_));
+ std::string mirrorMiscCeVolPath(StringPrintf("%s/%s", kMiscMirrorCePath, uuid_));
+ std::string mirrorMiscDeVolPath(StringPrintf("%s/%s", kMiscMirrorDePath, uuid_));
std::lock_guard<std::recursive_mutex> lock(mMountsLock);
@@ -3629,6 +3666,29 @@
if (delete_dir_contents_and_dir(mirrorDeVolPath, true) != 0) {
res = error("Failed to delete " + mirrorDeVolPath);
}
+
+ // Unmount misc CE storage
+ if (TEMP_FAILURE_RETRY(umount(mirrorMiscCeVolPath.c_str())) != 0) {
+ if (errno != ENOENT) {
+ res = error(StringPrintf("Failed to umount %s %s", mirrorMiscCeVolPath.c_str(),
+ strerror(errno)));
+ }
+ }
+ if (delete_dir_contents_and_dir(mirrorMiscCeVolPath, true) != 0) {
+ res = error("Failed to delete " + mirrorMiscCeVolPath);
+ }
+
+ // Unmount misc DE storage
+ if (TEMP_FAILURE_RETRY(umount(mirrorMiscDeVolPath.c_str())) != 0) {
+ if (errno != ENOENT) {
+ res = error(StringPrintf("Failed to umount %s %s", mirrorMiscDeVolPath.c_str(),
+ strerror(errno)));
+ }
+ }
+ if (delete_dir_contents_and_dir(mirrorMiscDeVolPath, true) != 0) {
+ res = error("Failed to delete " + mirrorMiscDeVolPath);
+ }
+
return res;
}
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 45aeab6..4d9b710 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -1184,8 +1184,8 @@
int wait_child_with_timeout(pid_t pid, int timeout_ms) {
int pidfd = pidfd_open(pid, /*flags=*/0);
if (pidfd < 0) {
- PLOG(ERROR) << "pidfd_open failed for pid " << pid;
- kill(pid, SIGKILL);
+ PLOG(ERROR) << "pidfd_open failed for pid " << pid
+ << ", waiting for child process without timeout";
return wait_child(pid);
}
diff --git a/include/android/choreographer.h b/include/android/choreographer.h
index 63aa7ff..cd8e63d 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 @@
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 @@
/**
* 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 @@
__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 @@
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 @@
__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 @@
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/keycodes.h b/include/android/keycodes.h
index 214559d..3357660 100644
--- a/include/android/keycodes.h
+++ b/include/android/keycodes.h
@@ -776,7 +776,39 @@
AKEYCODE_THUMBS_DOWN = 287,
/** Used to switch current account that is consuming content.
* May be consumed by system to switch current viewer profile. */
- AKEYCODE_PROFILE_SWITCH = 288
+ AKEYCODE_PROFILE_SWITCH = 288,
+ /** Video Application key #1. */
+ AKEYCODE_VIDEO_APP_1 = 289,
+ /** Video Application key #2. */
+ AKEYCODE_VIDEO_APP_2 = 290,
+ /** Video Application key #3. */
+ AKEYCODE_VIDEO_APP_3 = 291,
+ /** Video Application key #4. */
+ AKEYCODE_VIDEO_APP_4 = 292,
+ /** Video Application key #5. */
+ AKEYCODE_VIDEO_APP_5 = 293,
+ /** Video Application key #6. */
+ AKEYCODE_VIDEO_APP_6 = 294,
+ /** Video Application key #7. */
+ AKEYCODE_VIDEO_APP_7 = 295,
+ /** Video Application key #8. */
+ AKEYCODE_VIDEO_APP_8 = 296,
+ /** Featured Application key #1. */
+ AKEYCODE_FEATURED_APP_1 = 297,
+ /** Featured Application key #2. */
+ AKEYCODE_FEATURED_APP_2 = 298,
+ /** Featured Application key #3. */
+ AKEYCODE_FEATURED_APP_3 = 299,
+ /** Featured Application key #4. */
+ AKEYCODE_FEATURED_APP_4 = 300,
+ /** Demo Application key #1. */
+ AKEYCODE_DEMO_APP_1 = 301,
+ /** Demo Application key #2. */
+ AKEYCODE_DEMO_APP_2 = 302,
+ /** Demo Application key #3. */
+ AKEYCODE_DEMO_APP_3 = 303,
+ /** Demo Application key #4. */
+ AKEYCODE_DEMO_APP_4 = 304,
// NOTE: If you add a new keycode here you must also add it to several other files.
// Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index 9a36ecb..6223ef7 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -597,20 +597,20 @@
__INTRODUCED_IN(31);
/**
- * Sets the frame timeline to use in Surface Flinger.
+ * Sets the frame timeline to use in SurfaceFlinger.
*
- * A frame timeline should be chosen based on what frame deadline the application
- * can meet when rendering the frame and the application's desired present time.
- * By setting a frame timeline, Surface Flinger tries to present the frame at the corresponding
- * expected present time.
+ * A frame timeline should be chosen based on the frame deadline the application
+ * can meet when rendering the frame and the application's desired presentation time.
+ * By setting a frame timeline, SurfaceFlinger tries to present the frame at the corresponding
+ * expected presentation time.
*
* To receive frame timelines, a callback must be posted to Choreographer using
- * AChoreographer_postExtendedFrameCallback(). The \a vsnycId can then be extracted from the
+ * AChoreographer_postVsyncCallback(). The \c vsyncId can then be extracted from the
* callback payload using AChoreographerFrameCallbackData_getFrameTimelineVsyncId().
*
- * \param vsyncId The vsync ID received from AChoreographer, setting the frame's present target to
- * the corresponding expected present time and deadline from the frame to be rendered. A stale or
- * invalid value will be ignored.
+ * \param vsyncId The vsync ID received from AChoreographer, setting the frame's presentation target
+ * to the corresponding expected presentation time and deadline from the frame to be rendered. A
+ * stale or invalid value will be ignored.
*/
void ASurfaceTransaction_setFrameTimeline(ASurfaceTransaction* transaction,
AVsyncId vsyncId) __INTRODUCED_IN(33);
diff --git a/include/input/Input.h b/include/input/Input.h
index e7d68fc..7ea2970 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -297,6 +297,8 @@
*/
const char* motionClassificationToString(MotionClassification classification);
+const char* motionToolTypeToString(int32_t toolType);
+
/**
* Portion of FrameMetrics timeline of interest to input code.
*/
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index c4f03c9..3585392 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -300,6 +300,8 @@
/*
* 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 @@
* 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 b2bd535..5084950 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 @@
*/
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 @@
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 @@
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 @@
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 08ad8c6..9a3e15f 100644
--- a/include/input/Keyboard.h
+++ b/include/input/Keyboard.h
@@ -61,9 +61,7 @@
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 0a75278..55f730b 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 @@
}
/**
+ * 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/include/powermanager/PowerManager.h b/include/powermanager/PowerManager.h
index 9bac242..d73e19f 100644
--- a/include/powermanager/PowerManager.h
+++ b/include/powermanager/PowerManager.h
@@ -44,6 +44,12 @@
THERMAL_STATUS_SHUTDOWN = 6,
};
+enum {
+ USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS = 1 << 0,
+ USER_ACTIVITY_FLAG_INDIRECT = 1 << 1,
+ USER_ACTIVITY_FLAG_NO_BUTTON_LIGHTS = 1 << 2,
+};
+
}; // namespace android
#endif // ANDROID_POWERMANAGER_H
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index a3533d8..9ebeca0 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -187,6 +187,11 @@
cc_test {
name: "binderRpcTest",
+ // b/269799024
+ test_options: {
+ unit_test: false,
+ },
+
host_supported: true,
target: {
darwin: {
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 0f1a02a..3551a8f 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -86,6 +86,7 @@
/* list of hal interface to dump containing process during native dumps */
static const std::vector<std::string> aidl_interfaces_to_dump {
"android.hardware.camera.provider.ICameraProvider",
+ "android.hardware.input.processor.IInputProcessor",
};
/* list of extra hal interfaces to dump containing process during native dumps */
diff --git a/libs/graphicsenv/Android.bp b/libs/graphicsenv/Android.bp
index a96a07a..af50a29 100644
--- a/libs/graphicsenv/Android.bp
+++ b/libs/graphicsenv/Android.bp
@@ -27,10 +27,13 @@
srcs: [
"GpuStatsInfo.cpp",
"GraphicsEnv.cpp",
- "IGpuService.cpp"
+ "IGpuService.cpp",
],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
shared_libs: [
"libbase",
@@ -46,4 +49,13 @@
],
export_include_dirs: ["include"],
+
+ product_variables: {
+ // `debuggable` is set for eng and userdebug builds
+ debuggable: {
+ cflags: [
+ "-DANDROID_DEBUGGABLE",
+ ],
+ },
+ },
}
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 7f0cac5..e86881a 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -126,7 +126,20 @@
}
bool GraphicsEnv::isDebuggable() {
- return prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) > 0;
+ // This flag determines if the application is marked debuggable
+ bool appDebuggable = prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) > 0;
+
+ // This flag is set only in `debuggable` builds of the platform
+#if defined(ANDROID_DEBUGGABLE)
+ bool platformDebuggable = true;
+#else
+ bool platformDebuggable = false;
+#endif
+
+ ALOGV("GraphicsEnv::isDebuggable returning appDebuggable=%s || platformDebuggable=%s",
+ appDebuggable ? "true" : "false", platformDebuggable ? "true" : "false");
+
+ return appDebuggable || platformDebuggable;
}
void GraphicsEnv::setDriverPathAndSphalLibraries(const std::string path,
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index 56d1139..098e4a6 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -35,7 +35,7 @@
// Check if the process is debuggable. It returns false except in any of the
// following circumstances:
- // 1. ro.debuggable=1 (global debuggable enabled).
+ // 1. ANDROID_DEBUGGABLE is defined (global debuggable enabled).
// 2. android:debuggable="true" in the manifest for an individual app.
// 3. An app which explicitly calls prctl(PR_SET_DUMPABLE, 1).
// 4. GraphicsEnv calls prctl(PR_SET_DUMPABLE, 1) in the presence of
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index d634c58..861cb6f 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -149,13 +149,9 @@
},
}
-cc_library_shared {
- name: "libgui",
- vendor_available: true,
- vndk: {
- enabled: true,
- private: true,
- },
+cc_defaults {
+ name: "libgui_defaults",
+
double_loadable: true,
defaults: ["libgui_bufferqueue-defaults"],
@@ -410,4 +406,20 @@
],
}
+cc_library_shared {
+ name: "libgui",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ private: true,
+ },
+ defaults: ["libgui_defaults"]
+}
+
+cc_library_shared {
+ name: "libgui_vendor",
+ vendor: true,
+ defaults: ["libgui_defaults"]
+}
+
subdirs = ["tests"]
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index dbccf30..b8ea080 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -287,18 +287,17 @@
// We need to check if we were waiting for a transaction callback in order to
// process any pending buffers and unblock. It's possible to get transaction
- // callbacks for previous requests so we need to ensure the frame from this
- // transaction callback matches the last acquired buffer. Since acquireNextBuffer
- // will stop processing buffers when mWaitForTransactionCallback is set, we know
- // that mLastAcquiredFrameNumber is the frame we're waiting on.
- // We also want to check if mNextTransaction is null because it's possible another
+ // callbacks for previous requests so we need to ensure that there are no pending
+ // frame numbers that were in a sync. We remove the frame from mSyncedFrameNumbers
+ // set and then check if it's empty. If there are no more pending syncs, we can
+ // proceed with flushing the shadow queue.
+ // We also want to check if mSyncTransaction is null because it's possible another
// sync request came in while waiting, but it hasn't started processing yet. In that
// case, we don't actually want to flush the frames in between since they will get
// processed and merged with the sync transaction and released earlier than if they
// were sent to SF
- if (mWaitForTransactionCallback && mSyncTransaction == nullptr &&
- currFrameNumber >= mLastAcquiredFrameNumber) {
- mWaitForTransactionCallback = false;
+ mSyncedFrameNumbers.erase(currFrameNumber);
+ if (mSyncedFrameNumbers.empty() && mSyncTransaction == nullptr) {
flushShadowQueue();
}
} else {
@@ -308,7 +307,6 @@
BQA_LOGE("No matching SurfaceControls found: mSurfaceControlsWithPendingCallback was "
"empty.");
}
-
decStrong((void*)transactionCommittedCallbackThunk);
}
}
@@ -351,6 +349,21 @@
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) {
+ releaseBufferCallbackLocked(staleRelease,
+ stat.previousReleaseFence
+ ? stat.previousReleaseFence
+ : Fence::NO_FENCE,
+ stat.currentMaxAcquiredBufferCount,
+ true /* fakeRelease */);
+ }
} else {
BQA_LOGE("Failed to find matching SurfaceControl in transactionCallback");
}
@@ -391,7 +404,16 @@
const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
std::optional<uint32_t> currentMaxAcquiredBufferCount) {
BBQ_TRACE();
+
std::unique_lock _lock{mMutex};
+ releaseBufferCallbackLocked(id, releaseFence, currentMaxAcquiredBufferCount,
+ false /* fakeRelease */);
+}
+
+void BLASTBufferQueue::releaseBufferCallbackLocked(
+ const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
+ std::optional<uint32_t> currentMaxAcquiredBufferCount, bool fakeRelease) {
+ ATRACE_CALL();
BQA_LOGV("releaseBufferCallback %s", id.to_string().c_str());
// Calculate how many buffers we need to hold before we release them back
@@ -409,16 +431,27 @@
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);
+ if (fakeRelease) {
+ BQA_LOGE("Faking releaseBufferCallback from transactionCompleteCallback %" PRIu64,
+ id.framenumber);
+ BBQ_TRACE("FakeReleaseCallback");
+ }
+ }
// Release all buffers that are beyond the ones that we need to hold
while (mPendingRelease.size() > numPendingBuffersToHold) {
const auto releasedBuffer = mPendingRelease.front();
mPendingRelease.pop_front();
releaseBuffer(releasedBuffer.callbackId, releasedBuffer.releaseFence);
- // Don't process the transactions here if mWaitForTransactionCallback is set. Instead, let
- // onFrameAvailable handle processing them since it will merge with the syncTransaction.
- if (!mWaitForTransactionCallback) {
+ // Don't process the transactions here if mSyncedFrameNumbers is not empty. That means
+ // are still transactions that have sync buffers in them that have not been applied or
+ // dropped. Instead, let onFrameAvailable handle processing them since it will merge with
+ // the syncTransaction.
+ if (mSyncedFrameNumbers.empty()) {
acquireNextBufferLocked(std::nullopt);
}
}
@@ -442,22 +475,31 @@
BQA_LOGV("released %s", callbackId.to_string().c_str());
mBufferItemConsumer->releaseBuffer(it->second, releaseFence);
mSubmitted.erase(it);
+ // Remove the frame number from mSyncedFrameNumbers since we can get a release callback
+ // without getting a transaction committed if the buffer was dropped.
+ mSyncedFrameNumbers.erase(callbackId.framenumber);
}
-void BLASTBufferQueue::acquireNextBufferLocked(
+status_t BLASTBufferQueue::acquireNextBufferLocked(
const std::optional<SurfaceComposerClient::Transaction*> transaction) {
- // If the next transaction is set, we want to guarantee the our acquire will not fail, so don't
- // include the extra buffer when checking if we can acquire the next buffer.
- const bool includeExtraAcquire = !transaction;
- const bool maxAcquired = maxBuffersAcquired(includeExtraAcquire);
- if (mNumFrameAvailable == 0 || maxAcquired) {
- BQA_LOGV("Can't process next buffer maxBuffersAcquired=%s", boolToString(maxAcquired));
- return;
+ // Check if we have frames available and we have not acquired the maximum number of buffers.
+ // Even with this check, the consumer can fail to acquire an additional buffer if the consumer
+ // has already acquired (mMaxAcquiredBuffers + 1) and the new buffer is not droppable. In this
+ // case mBufferItemConsumer->acquireBuffer will return with NO_BUFFER_AVAILABLE.
+ if (mNumFrameAvailable == 0) {
+ BQA_LOGV("Can't acquire next buffer. No available frames");
+ return BufferQueue::NO_BUFFER_AVAILABLE;
+ }
+
+ if (mNumAcquired >= (mMaxAcquiredBuffers + 2)) {
+ BQA_LOGV("Can't acquire next buffer. Already acquired max frames %d max:%d + 2",
+ mNumAcquired, mMaxAcquiredBuffers);
+ return BufferQueue::NO_BUFFER_AVAILABLE;
}
if (mSurfaceControl == nullptr) {
BQA_LOGE("ERROR : surface control is null");
- return;
+ return NAME_NOT_FOUND;
}
SurfaceComposerClient::Transaction localTransaction;
@@ -474,10 +516,10 @@
mBufferItemConsumer->acquireBuffer(&bufferItem, 0 /* expectedPresent */, false);
if (status == BufferQueue::NO_BUFFER_AVAILABLE) {
BQA_LOGV("Failed to acquire a buffer, err=NO_BUFFER_AVAILABLE");
- return;
+ return status;
} else if (status != OK) {
BQA_LOGE("Failed to acquire a buffer, err=%s", statusToString(status).c_str());
- return;
+ return status;
}
auto buffer = bufferItem.mGraphicBuffer;
@@ -487,7 +529,7 @@
if (buffer == nullptr) {
mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE);
BQA_LOGE("Buffer was empty");
- return;
+ return BAD_VALUE;
}
if (rejectBuffer(bufferItem)) {
@@ -496,8 +538,7 @@
mSize.width, mSize.height, mRequestedSize.width, mRequestedSize.height,
buffer->getWidth(), buffer->getHeight(), bufferItem.mTransform);
mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE);
- acquireNextBufferLocked(transaction);
- return;
+ return acquireNextBufferLocked(transaction);
}
mNumAcquired++;
@@ -551,9 +592,23 @@
t->setDesiredPresentTime(bufferItem.mTimestamp);
}
- if (!mNextFrameTimelineInfoQueue.empty()) {
- t->setFrameTimelineInfo(mNextFrameTimelineInfoQueue.front());
- mNextFrameTimelineInfoQueue.pop();
+ // Drop stale frame timeline infos
+ while (!mPendingFrameTimelines.empty() &&
+ mPendingFrameTimelines.front().first < bufferItem.mFrameNumber) {
+ ATRACE_FORMAT_INSTANT("dropping stale frameNumber: %" PRIu64 " vsyncId: %" PRId64,
+ mPendingFrameTimelines.front().first,
+ mPendingFrameTimelines.front().second.vsyncId);
+ mPendingFrameTimelines.pop();
+ }
+
+ if (!mPendingFrameTimelines.empty() &&
+ mPendingFrameTimelines.front().first == bufferItem.mFrameNumber) {
+ ATRACE_FORMAT_INSTANT("Transaction::setFrameTimelineInfo frameNumber: %" PRIu64
+ " vsyncId: %" PRId64,
+ bufferItem.mFrameNumber,
+ mPendingFrameTimelines.front().second.vsyncId);
+ t->setFrameTimelineInfo(mPendingFrameTimelines.front().second);
+ mPendingFrameTimelines.pop();
}
{
@@ -585,6 +640,7 @@
bufferItem.mTimestamp, bufferItem.mIsAutoTimestamp ? "(auto)" : "",
static_cast<uint32_t>(mPendingTransactions.size()), bufferItem.mGraphicBuffer->getId(),
bufferItem.mAutoRefresh ? " mAutoRefresh" : "", bufferItem.mTransform);
+ return OK;
}
Rect BLASTBufferQueue::computeCrop(const BufferItem& item) {
@@ -607,42 +663,19 @@
mBufferItemConsumer->releaseBuffer(bufferItem, bufferItem.mFence);
}
-void BLASTBufferQueue::flushAndWaitForFreeBuffer(std::unique_lock<std::mutex>& lock) {
- if (mWaitForTransactionCallback && mNumFrameAvailable > 0) {
- // We are waiting on a previous sync's transaction callback so allow another sync
- // transaction to proceed.
- //
- // We need to first flush out the transactions that were in between the two syncs.
- // We do this by merging them into mSyncTransaction so any buffer merging will get
- // a release callback invoked. The release callback will be async so we need to wait
- // on max acquired to make sure we have the capacity to acquire another buffer.
- if (maxBuffersAcquired(false /* includeExtraAcquire */)) {
- BQA_LOGD("waiting to flush shadow queue...");
- mCallbackCV.wait(lock);
- }
- while (mNumFrameAvailable > 0) {
- // flush out the shadow queue
- acquireAndReleaseBuffer();
- }
- }
-
- while (maxBuffersAcquired(false /* includeExtraAcquire */)) {
- BQA_LOGD("waiting for free buffer.");
- mCallbackCV.wait(lock);
- }
-}
-
void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
std::function<void(SurfaceComposerClient::Transaction*)> prevCallback = nullptr;
SurfaceComposerClient::Transaction* prevTransaction = nullptr;
+
{
- BBQ_TRACE();
std::unique_lock _lock{mMutex};
+ BBQ_TRACE();
+
+ bool waitForTransactionCallback = !mSyncedFrameNumbers.empty();
const bool syncTransactionSet = mTransactionReadyCallback != nullptr;
BQA_LOGV("onFrameAvailable-start syncTransactionSet=%s", boolToString(syncTransactionSet));
if (syncTransactionSet) {
- bool mayNeedToWaitForBuffer = true;
// If we are going to re-use the same mSyncTransaction, release the buffer that may
// already be set in the Transaction. This is to allow us a free slot early to continue
// processing a new buffer.
@@ -653,20 +686,35 @@
bufferData->frameNumber);
releaseBuffer(bufferData->generateReleaseCallbackId(),
bufferData->acquireFence);
- // Because we just released a buffer, we know there's no need to wait for a free
- // buffer.
- mayNeedToWaitForBuffer = false;
}
}
- if (mayNeedToWaitForBuffer) {
- flushAndWaitForFreeBuffer(_lock);
+ if (waitForTransactionCallback) {
+ // We are waiting on a previous sync's transaction callback so allow another sync
+ // transaction to proceed.
+ //
+ // We need to first flush out the transactions that were in between the two syncs.
+ // We do this by merging them into mSyncTransaction so any buffer merging will get
+ // a release callback invoked.
+ while (mNumFrameAvailable > 0) {
+ // flush out the shadow queue
+ acquireAndReleaseBuffer();
+ }
+ } else {
+ // Make sure the frame available count is 0 before proceeding with a sync to ensure
+ // the correct frame is used for the sync. The only way mNumFrameAvailable would be
+ // greater than 0 is if we already ran out of buffers previously. This means we
+ // need to flush the buffers before proceeding with the sync.
+ while (mNumFrameAvailable > 0) {
+ BQA_LOGD("waiting until no queued buffers");
+ mCallbackCV.wait(_lock);
+ }
}
}
// add to shadow queue
mNumFrameAvailable++;
- if (mWaitForTransactionCallback && mNumFrameAvailable >= 2) {
+ if (waitForTransactionCallback && mNumFrameAvailable >= 2) {
acquireAndReleaseBuffer();
}
ATRACE_INT(mQueuedBufferTrace.c_str(),
@@ -676,21 +724,30 @@
item.mFrameNumber, boolToString(syncTransactionSet));
if (syncTransactionSet) {
- acquireNextBufferLocked(mSyncTransaction);
+ // Add to mSyncedFrameNumbers before waiting in case any buffers are released
+ // while waiting for a free buffer. The release and commit callback will try to
+ // acquire buffers if there are any available, but we don't want it to acquire
+ // in the case where a sync transaction wants the buffer.
+ mSyncedFrameNumbers.emplace(item.mFrameNumber);
+ // If there's no available buffer and we're in a sync transaction, we need to wait
+ // instead of returning since we guarantee a buffer will be acquired for the sync.
+ while (acquireNextBufferLocked(mSyncTransaction) == BufferQueue::NO_BUFFER_AVAILABLE) {
+ BQA_LOGD("waiting for available buffer");
+ mCallbackCV.wait(_lock);
+ }
// Only need a commit callback when syncing to ensure the buffer that's synced has been
// sent to SF
incStrong((void*)transactionCommittedCallbackThunk);
mSyncTransaction->addTransactionCommittedCallback(transactionCommittedCallbackThunk,
static_cast<void*>(this));
- mWaitForTransactionCallback = true;
if (mAcquireSingleBuffer) {
prevCallback = mTransactionReadyCallback;
prevTransaction = mSyncTransaction;
mTransactionReadyCallback = nullptr;
mSyncTransaction = nullptr;
}
- } else if (!mWaitForTransactionCallback) {
+ } else if (!waitForTransactionCallback) {
acquireNextBufferLocked(std::nullopt);
}
}
@@ -786,15 +843,6 @@
return mSize != bufferSize;
}
-// Check if we have acquired the maximum number of buffers.
-// Consumer can acquire an additional buffer if that buffer is not droppable. Set
-// includeExtraAcquire is true to include this buffer to the count. Since this depends on the state
-// of the buffer, the next acquire may return with NO_BUFFER_AVAILABLE.
-bool BLASTBufferQueue::maxBuffersAcquired(bool includeExtraAcquire) const {
- int maxAcquiredBuffers = mMaxAcquiredBuffers + (includeExtraAcquire ? 2 : 1);
- return mNumAcquired >= maxAcquiredBuffers;
-}
-
class BBQSurface : public Surface {
private:
std::mutex mMutex;
@@ -831,12 +879,13 @@
return mBbq->setFrameRate(frameRate, compatibility, changeFrameRateStrategy);
}
- status_t setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) override {
+ status_t setFrameTimelineInfo(uint64_t frameNumber,
+ const FrameTimelineInfo& frameTimelineInfo) override {
std::unique_lock _lock{mMutex};
if (mDestroyed) {
return DEAD_OBJECT;
}
- return mBbq->setFrameTimelineInfo(frameTimelineInfo);
+ return mBbq->setFrameTimelineInfo(frameNumber, frameTimelineInfo);
}
void destroy() override {
@@ -858,9 +907,12 @@
return t.setFrameRate(mSurfaceControl, frameRate, compatibility, shouldBeSeamless).apply();
}
-status_t BLASTBufferQueue::setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) {
+status_t BLASTBufferQueue::setFrameTimelineInfo(uint64_t frameNumber,
+ const FrameTimelineInfo& frameTimelineInfo) {
+ ATRACE_FORMAT("%s(%s) frameNumber: %" PRIu64 " vsyncId: %" PRId64, __func__, mName.c_str(),
+ frameNumber, frameTimelineInfo.vsyncId);
std::unique_lock _lock{mMutex};
- mNextFrameTimelineInfoQueue.push(frameTimelineInfo);
+ mPendingFrameTimelines.push({frameNumber, frameTimelineInfo});
return OK;
}
@@ -1097,9 +1149,9 @@
}
// Clear sync states
- if (mWaitForTransactionCallback) {
- BQA_LOGD("mWaitForTransactionCallback cleared");
- mWaitForTransactionCallback = false;
+ if (!mSyncedFrameNumbers.empty()) {
+ BQA_LOGD("mSyncedFrameNumbers cleared");
+ mSyncedFrameNumbers.clear();
}
if (mSyncTransaction != nullptr) {
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 5fe5e71..e8e3f8e 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -153,13 +153,14 @@
return BAD_VALUE;
}
- int bufferCount = mCore->getMinUndequeuedBufferCountLocked();
- bufferCount += maxDequeuedBuffers;
+ int minUndequedBufferCount = mCore->getMinUndequeuedBufferCountLocked();
+ int bufferCount = minUndequedBufferCount + maxDequeuedBuffers;
if (bufferCount > BufferQueueDefs::NUM_BUFFER_SLOTS) {
BQ_LOGE("setMaxDequeuedBufferCount: bufferCount %d too large "
"(max %d)", bufferCount, BufferQueueDefs::NUM_BUFFER_SLOTS);
- return BAD_VALUE;
+ bufferCount = BufferQueueDefs::NUM_BUFFER_SLOTS;
+ maxDequeuedBuffers = bufferCount - minUndequedBufferCount;
}
const int minBufferSlots = mCore->getMinMaxBufferCountLocked();
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 54b6d6a..4c80792 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1862,12 +1862,13 @@
int Surface::dispatchSetFrameTimelineInfo(va_list args) {
ATRACE_CALL();
+ auto frameNumber = static_cast<uint64_t>(va_arg(args, uint64_t));
auto frameTimelineVsyncId = static_cast<int64_t>(va_arg(args, int64_t));
auto inputEventId = static_cast<int32_t>(va_arg(args, int32_t));
auto startTimeNanos = static_cast<int64_t>(va_arg(args, int64_t));
ALOGV("Surface::%s", __func__);
- return setFrameTimelineInfo({frameTimelineVsyncId, inputEventId, startTimeNanos});
+ return setFrameTimelineInfo(frameNumber, {frameTimelineVsyncId, inputEventId, startTimeNanos});
}
bool Surface::transformToDisplayInverse() const {
@@ -2637,7 +2638,8 @@
changeFrameRateStrategy);
}
-status_t Surface::setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) {
+status_t Surface::setFrameTimelineInfo(uint64_t /*frameNumber*/,
+ const FrameTimelineInfo& frameTimelineInfo) {
return composerService()->setFrameTimelineInfo(mGraphicBufferProducer, frameTimelineInfo);
}
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 47d801a..05beb07 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -352,7 +352,8 @@
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 @@
transactionStats.latchTime, surfaceStats.acquireTimeOrFence,
transactionStats.presentFence,
surfaceStats.previousReleaseFence, surfaceStats.transformHint,
- surfaceStats.eventStats);
+ surfaceStats.eventStats,
+ surfaceStats.currentMaxAcquiredBufferCount);
if (callbacksMap[callbackId].surfaceControls[surfaceStats.surfaceControl]) {
callbacksMap[callbackId]
.surfaceControls[surfaceStats.surfaceControl]
@@ -897,6 +899,10 @@
mApplyToken = nullptr;
}
+uint64_t SurfaceComposerClient::Transaction::getId() {
+ return mId;
+}
+
void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) {
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
@@ -1268,8 +1274,11 @@
mStatus = BAD_INDEX;
return *this;
}
+ if (alpha < 0.0f || alpha > 1.0f) {
+ ALOGE("SurfaceComposerClient::Transaction::setAlpha: invalid alpha %f, clamping", alpha);
+ }
s->what |= layer_state_t::eAlphaChanged;
- s->alpha = alpha;
+ s->alpha = std::clamp(alpha, 0.f, 1.f);
registerSurfaceControlForCallback(sc);
return *this;
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 9328a54..4007667 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -95,9 +95,13 @@
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,
+ bool fakeRelease);
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);
@@ -105,7 +109,7 @@
void update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height, int32_t format);
status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless);
- status_t setFrameTimelineInfo(const FrameTimelineInfo& info);
+ status_t setFrameTimelineInfo(uint64_t frameNumber, const FrameTimelineInfo& info);
void setSidebandStream(const sp<NativeHandle>& stream);
@@ -132,12 +136,11 @@
void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
sp<IGraphicBufferConsumer>* outConsumer);
- void acquireNextBufferLocked(
+ status_t acquireNextBufferLocked(
const std::optional<SurfaceComposerClient::Transaction*> transaction) REQUIRES(mMutex);
Rect computeCrop(const BufferItem& item) REQUIRES(mMutex);
// Return true if we need to reject the buffer based on the scaling mode and the buffer size.
bool rejectBuffer(const BufferItem& item) REQUIRES(mMutex);
- bool maxBuffersAcquired(bool includeExtraAcquire) const REQUIRES(mMutex);
static PixelFormat convertBufferFormat(PixelFormat& format);
void mergePendingTransactions(SurfaceComposerClient::Transaction* t, uint64_t frameNumber)
REQUIRES(mMutex);
@@ -146,7 +149,6 @@
void acquireAndReleaseBuffer() REQUIRES(mMutex);
void releaseBuffer(const ReleaseCallbackId& callbackId, const sp<Fence>& releaseFence)
REQUIRES(mMutex);
- void flushAndWaitForFreeBuffer(std::unique_lock<std::mutex>& lock);
std::string mName;
// Represents the queued buffer count from buffer queue,
@@ -177,6 +179,12 @@
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);
@@ -229,7 +237,7 @@
std::vector<std::tuple<uint64_t /* framenumber */, SurfaceComposerClient::Transaction>>
mPendingTransactions GUARDED_BY(mMutex);
- std::queue<FrameTimelineInfo> mNextFrameTimelineInfoQueue GUARDED_BY(mMutex);
+ std::queue<std::pair<uint64_t, FrameTimelineInfo>> mPendingFrameTimelines GUARDED_BY(mMutex);
// Tracks the last acquired frame number
uint64_t mLastAcquiredFrameNumber GUARDED_BY(mMutex) = 0;
@@ -251,7 +259,6 @@
std::queue<sp<SurfaceControl>> mSurfaceControlsWithPendingCallback GUARDED_BY(mMutex);
uint32_t mCurrentMaxAcquiredBufferCount;
- bool mWaitForTransactionCallback GUARDED_BY(mMutex) = false;
// Flag to determine if syncTransaction should only acquire a single buffer and then clear or
// continue to acquire buffers until explicitly cleared
@@ -279,6 +286,8 @@
uint64_t mLastAppliedFrameNumber = 0;
std::function<void(bool)> mTransactionHangCallback;
+
+ std::unordered_set<uint64_t> mSyncedFrameNumbers GUARDED_BY(mMutex);
};
} // namespace android
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index ab9ebaa..8d4e15b 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -193,7 +193,7 @@
virtual status_t setFrameRate(float frameRate, int8_t compatibility,
int8_t changeFrameRateStrategy);
- virtual status_t setFrameTimelineInfo(const FrameTimelineInfo& info);
+ virtual status_t setFrameTimelineInfo(uint64_t frameNumber, const FrameTimelineInfo& info);
protected:
virtual ~Surface();
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index efbdb36..9033e17 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -65,14 +65,16 @@
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 @@
sp<Fence> previousReleaseFence;
uint32_t transformHint = 0;
FrameEventHistoryStats frameEventStats;
+ uint32_t currentMaxAcquiredBufferCount = 0;
};
using TransactionCompletedCallbackTakesContext =
@@ -461,6 +464,10 @@
// Clears the contents of the transaction without applying it.
void clear();
+ // Returns the current id of the transaction.
+ // The id is updated every time the transaction is applied.
+ uint64_t getId();
+
status_t apply(bool synchronous = false, bool oneWay = false);
// Merge another transaction in to this one, clearing other
// as if it had been applied.
diff --git a/libs/gui/include/gui/TraceUtils.h b/libs/gui/include/gui/TraceUtils.h
index e5d2684..0009615 100644
--- a/libs/gui/include/gui/TraceUtils.h
+++ b/libs/gui/include/gui/TraceUtils.h
@@ -27,6 +27,8 @@
#define ATRACE_FORMAT_BEGIN(fmt, ...) TraceUtils::atraceFormatBegin(fmt, ##__VA_ARGS__)
+#define ATRACE_FORMAT_INSTANT(fmt, ...) TraceUtils::intantFormat(fmt, ##__VA_ARGS__)
+
namespace android {
class TraceUtils {
@@ -50,6 +52,20 @@
ATRACE_BEGIN(buf);
}
+ static void intantFormat(const char* fmt, ...) {
+ if (CC_LIKELY(!ATRACE_ENABLED())) return;
+
+ const int BUFFER_SIZE = 256;
+ va_list ap;
+ char buf[BUFFER_SIZE];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, BUFFER_SIZE, fmt, ap);
+ va_end(ap);
+
+ ATRACE_INSTANT(buf);
+ }
+
}; // class TraceUtils
} /* namespace android */
diff --git a/libs/gui/include/gui/test/CallbackUtils.h b/libs/gui/include/gui/test/CallbackUtils.h
index 62d1496..08785b4 100644
--- a/libs/gui/include/gui/test/CallbackUtils.h
+++ b/libs/gui/include/gui/test/CallbackUtils.h
@@ -135,7 +135,8 @@
void verifySurfaceControlStats(const SurfaceControlStats& surfaceControlStats,
nsecs_t latchTime) const {
const auto& [surfaceControl, latch, acquireTimeOrFence, presentFence,
- previousReleaseFence, transformHint, frameEvents] = surfaceControlStats;
+ previousReleaseFence, transformHint, frameEvents, ignore] =
+ surfaceControlStats;
ASSERT_TRUE(std::holds_alternative<nsecs_t>(acquireTimeOrFence));
ASSERT_EQ(std::get<nsecs_t>(acquireTimeOrFence) > 0,
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index cb7e94c..b993289 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -161,6 +161,10 @@
ASSERT_EQ(numFramesSubmitted, mBlastBufferQueueAdapter->mSubmitted.size());
}
+ void mergeWithNextTransaction(Transaction* merge, uint64_t frameNumber) {
+ mBlastBufferQueueAdapter->mergeWithNextTransaction(merge, frameNumber);
+ }
+
private:
sp<TestBLASTBufferQueue> mBlastBufferQueueAdapter;
};
@@ -1111,6 +1115,39 @@
ASSERT_TRUE(receivedCallback);
}
+TEST_F(BLASTBufferQueueTest, SyncNextTransactionDropBuffer) {
+ uint8_t r = 255;
+ uint8_t g = 0;
+ uint8_t b = 0;
+
+ BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+
+ sp<IGraphicBufferProducer> igbProducer;
+ setUpProducer(adapter, igbProducer);
+
+ Transaction sync;
+ adapter.setSyncTransaction(sync);
+ queueBuffer(igbProducer, 0, 255, 0, 0);
+
+ // Merge a transaction that has a complete callback into the next frame so we can get notified
+ // when to take a screenshot
+ CallbackHelper transactionCallback;
+ Transaction t;
+ t.addTransactionCompletedCallback(transactionCallback.function,
+ transactionCallback.getContext());
+ adapter.mergeWithNextTransaction(&t, 2);
+ queueBuffer(igbProducer, r, g, b, 0);
+
+ // Drop the buffer, but ensure the next one continues to get processed.
+ sync.setBuffer(mSurfaceControl, nullptr);
+
+ CallbackData callbackData;
+ transactionCallback.getCallbackData(&callbackData);
+ ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+}
+
// This test will currently fail because the old surfacecontrol will steal the last presented buffer
// until the old surface control is destroyed. This is not necessarily a bug but to document a
// limitation with the update API and to test any changes to make the api more robust. The current
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index c6cdeb7..2637f59 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -1184,18 +1184,23 @@
std::vector<sp<IGraphicBufferProducer>> mProducers;
};
-TEST_F(MultiDisplayTests, drop_input_if_layer_on_invalid_display) {
+TEST_F(MultiDisplayTests, drop_touch_if_layer_on_invalid_display) {
ui::LayerStack layerStack = ui::LayerStack::fromValue(42);
// Do not create a display associated with the LayerStack.
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
surface->doTransaction([&](auto &t, auto &sc) { t.setLayerStack(sc, layerStack); });
surface->showAt(100, 100);
+ // Touches should be dropped if the layer is on an invalid display.
injectTapOnDisplay(101, 101, layerStack.id);
- surface->requestFocus(layerStack.id);
- injectKeyOnDisplay(AKEYCODE_V, layerStack.id);
-
EXPECT_EQ(surface->consumeEvent(100), nullptr);
+
+ // However, we still let the window be focused and receive keys.
+ surface->requestFocus(layerStack.id);
+ surface->assertFocusChange(true);
+
+ injectKeyOnDisplay(AKEYCODE_V, layerStack.id);
+ surface->expectKey(AKEYCODE_V);
}
TEST_F(MultiDisplayTests, virtual_display_receives_input) {
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 5d7874a..1335e4d 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -67,6 +67,7 @@
"libbase",
"liblog",
"libcutils",
+ "libvintf",
],
static_libs: [
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 13ca9ec..155cb04 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -64,9 +64,10 @@
}
bool shouldDisregardTransformation(uint32_t source) {
- // Do not apply any transformations to axes from joysticks or touchpads.
+ // Do not apply any transformations to axes from joysticks, touchpads, or relative mice.
return isFromSource(source, AINPUT_SOURCE_CLASS_JOYSTICK) ||
- isFromSource(source, AINPUT_SOURCE_CLASS_POSITION);
+ isFromSource(source, AINPUT_SOURCE_CLASS_POSITION) ||
+ isFromSource(source, AINPUT_SOURCE_MOUSE_RELATIVE);
}
bool shouldDisregardOffset(uint32_t source) {
@@ -89,6 +90,25 @@
}
}
+const char* motionToolTypeToString(int32_t toolType) {
+ switch (toolType) {
+ case AMOTION_EVENT_TOOL_TYPE_UNKNOWN:
+ return "UNKNOWN";
+ case AMOTION_EVENT_TOOL_TYPE_FINGER:
+ return "FINGER";
+ case AMOTION_EVENT_TOOL_TYPE_STYLUS:
+ return "STYLUS";
+ case AMOTION_EVENT_TOOL_TYPE_MOUSE:
+ return "MOUSE";
+ case AMOTION_EVENT_TOOL_TYPE_ERASER:
+ return "ERASER";
+ case AMOTION_EVENT_TOOL_TYPE_PALM:
+ return "PALM";
+ default:
+ return "INVALID";
+ }
+}
+
// --- IdGenerator ---
IdGenerator::IdGenerator(Source source) : mSource(source) {}
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index 0bee1b6..a908969 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -53,33 +53,39 @@
}
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/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp
index c0aa2e2..2d768ce 100644
--- a/libs/input/InputEventLabels.cpp
+++ b/libs/input/InputEventLabels.cpp
@@ -314,7 +314,23 @@
DEFINE_KEYCODE(REFRESH), \
DEFINE_KEYCODE(THUMBS_UP), \
DEFINE_KEYCODE(THUMBS_DOWN), \
- DEFINE_KEYCODE(PROFILE_SWITCH)
+ DEFINE_KEYCODE(PROFILE_SWITCH), \
+ DEFINE_KEYCODE(VIDEO_APP_1), \
+ DEFINE_KEYCODE(VIDEO_APP_2), \
+ DEFINE_KEYCODE(VIDEO_APP_3), \
+ DEFINE_KEYCODE(VIDEO_APP_4), \
+ DEFINE_KEYCODE(VIDEO_APP_5), \
+ DEFINE_KEYCODE(VIDEO_APP_6), \
+ DEFINE_KEYCODE(VIDEO_APP_7), \
+ DEFINE_KEYCODE(VIDEO_APP_8), \
+ DEFINE_KEYCODE(FEATURED_APP_1), \
+ DEFINE_KEYCODE(FEATURED_APP_2), \
+ DEFINE_KEYCODE(FEATURED_APP_3), \
+ DEFINE_KEYCODE(FEATURED_APP_4), \
+ DEFINE_KEYCODE(DEMO_APP_1), \
+ DEFINE_KEYCODE(DEMO_APP_2), \
+ DEFINE_KEYCODE(DEMO_APP_3), \
+ DEFINE_KEYCODE(DEMO_APP_4)
// NOTE: If you add a new axis here you must also add it to several other files.
// Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list.
diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp
index 7c25cda..170e748 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 @@
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 @@
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 @@
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 @@
*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 @@
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::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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
-#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 @@
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 @@
}
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 @@
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 f0895b3..c3f5151 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/InputEventLabels.h>
-#include <input/KeyLayoutMap.h>
-#include <input/KeyCharacterMap.h>
#include <input/InputDevice.h>
+#include <input/InputEventLabels.h>
+#include <input/KeyCharacterMap.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 @@
}
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 @@
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 6ffe851..d947cd9 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -36,8 +36,9 @@
"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 61e88df..e872fa4 100644
--- a/libs/input/tests/InputDevice_test.cpp
+++ b/libs/input/tests/InputDevice_test.cpp
@@ -64,13 +64,11 @@
mKeyMap.keyCharacterMapFile = path;
}
- virtual void SetUp() override {
+ void SetUp() override {
loadKeyLayout("Generic");
loadKeyCharacterMap("Generic");
}
- virtual void TearDown() override {}
-
KeyMap mKeyMap;
};
@@ -132,4 +130,20 @@
ASSERT_EQ(*mKeyMap.keyCharacterMap, *frenchOverlaidKeyCharacterMap);
}
+TEST(InputDeviceKeyLayoutTest, DoesNotLoadWhenRequiredKernelConfigIsMissing) {
+ std::string klPath = base::GetExecutableDirectory() + "/data/kl_with_required_fake_config.kl";
+ base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(klPath);
+ ASSERT_FALSE(ret.ok()) << "Should not be able to load KeyLayout at " << klPath;
+ // We assert error message here because it's used by 'validatekeymaps' tool
+ ASSERT_EQ("Missing kernel config", ret.error().message());
+}
+
+TEST(InputDeviceKeyLayoutTest, LoadsWhenRequiredKernelConfigIsPresent) {
+ std::string klPath = base::GetExecutableDirectory() + "/data/kl_with_required_real_config.kl";
+ base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(klPath);
+ ASSERT_TRUE(ret.ok()) << "Cannot load KeyLayout at " << klPath;
+ const std::shared_ptr<KeyLayoutMap>& map = *ret;
+ ASSERT_NE(nullptr, map) << "Map should be valid because CONFIG_UHID should always be present";
+}
+
} // namespace android
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index a92016b..4b31246 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -715,10 +715,10 @@
}
TEST_F(MotionEventTest, JoystickAndTouchpadAreNotTransformed) {
- constexpr static std::array kNonTransformedSources = {std::pair(AINPUT_SOURCE_TOUCHPAD,
- AMOTION_EVENT_ACTION_DOWN),
- std::pair(AINPUT_SOURCE_JOYSTICK,
- AMOTION_EVENT_ACTION_MOVE)};
+ constexpr static std::array kNonTransformedSources =
+ {std::pair(AINPUT_SOURCE_TOUCHPAD, AMOTION_EVENT_ACTION_DOWN),
+ std::pair(AINPUT_SOURCE_JOYSTICK, AMOTION_EVENT_ACTION_MOVE),
+ std::pair(AINPUT_SOURCE_MOUSE_RELATIVE, AMOTION_EVENT_ACTION_MOVE)};
// Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
ui::Transform transform(ui::Transform::ROT_90, 800, 400);
transform.set(transform.tx() + 20, transform.ty() + 40);
@@ -738,7 +738,7 @@
TEST_F(MotionEventTest, NonPointerSourcesAreNotTranslated) {
constexpr static std::array kNonPointerSources = {std::pair(AINPUT_SOURCE_TRACKBALL,
AMOTION_EVENT_ACTION_DOWN),
- std::pair(AINPUT_SOURCE_MOUSE_RELATIVE,
+ std::pair(AINPUT_SOURCE_TOUCH_NAVIGATION,
AMOTION_EVENT_ACTION_MOVE)};
// Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
ui::Transform transform(ui::Transform::ROT_90, 800, 400);
diff --git a/libs/input/tests/data/kl_with_required_fake_config.kl b/libs/input/tests/data/kl_with_required_fake_config.kl
new file mode 100644
index 0000000..2d0a507
--- /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 0000000..303b23e
--- /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/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index a54af1f..86e76c4 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -1043,11 +1043,12 @@
}
static inline int native_window_set_frame_timeline_info(struct ANativeWindow* window,
+ uint64_t frameNumber,
int64_t frameTimelineVsyncId,
int32_t inputEventId,
int64_t startTimeNanos) {
- return window->perform(window, NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO, frameTimelineVsyncId,
- inputEventId, startTimeNanos);
+ return window->perform(window, NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO, frameNumber,
+ frameTimelineVsyncId, inputEventId, startTimeNanos);
}
// ------------------------------------------------------------------------------------------------
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 22dd866..6dc01b9 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -921,7 +921,8 @@
// Finally, we cut the layer into 3 parts, with top and bottom parts having rounded corners
// and the middle part without rounded corners.
- const int32_t radius = ceil(layer.geometry.roundedCornersRadius);
+ const int32_t radius = ceil(
+ (layer.geometry.roundedCornersRadius.x + layer.geometry.roundedCornersRadius.y) / 2.0);
const Rect topRect(bounds.left, bounds.top, bounds.right, bounds.top + radius);
setScissor(topRect);
drawMesh(mesh);
@@ -1266,23 +1267,24 @@
const half3 solidColor = layer.source.solidColor;
const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha);
+ const float radius =
+ (layer.geometry.roundedCornersRadius.x + layer.geometry.roundedCornersRadius.y) /
+ 2.0f;
// Buffer sources will have a black solid color ignored in the shader,
// so in that scenario the solid color passed here is arbitrary.
- setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color,
- layer.geometry.roundedCornersRadius);
+ setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color, radius);
if (layer.disableBlending) {
glDisable(GL_BLEND);
}
setSourceDataSpace(layer.sourceDataspace);
if (layer.shadow.length > 0.0f) {
- handleShadow(layer.geometry.boundaries, layer.geometry.roundedCornersRadius,
- layer.shadow);
+ handleShadow(layer.geometry.boundaries, radius, layer.shadow);
}
// We only want to do a special handling for rounded corners when having rounded corners
// is the only reason it needs to turn on blending, otherwise, we handle it like the
// usual way since it needs to turn on blending anyway.
- else if (layer.geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) {
+ else if (radius > 0.0 && color.a >= 1.0f && isOpaque) {
handleRoundedCorners(display, layer, mesh);
} else {
drawMesh(mesh);
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index 154e526..b3a617c 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -87,7 +87,7 @@
// rectangle to figure out how to apply the radius for this layer. The crop rectangle will be
// in local layer coordinate space, so we have to take the layer transform into account when
// walking up the tree.
- float roundedCornersRadius = 0.0;
+ vec2 roundedCornersRadius = vec2(0.0f, 0.0f);
// Rectangle within which corners will be rounded.
FloatRect roundedCornersCrop = FloatRect();
@@ -258,7 +258,8 @@
PrintTo(settings.boundaries, os);
*os << "\n .positionTransform = ";
PrintMatrix(settings.positionTransform, os);
- *os << "\n .roundedCornersRadius = " << settings.roundedCornersRadius;
+ *os << "\n .roundedCornersRadiusX = " << settings.roundedCornersRadius.x;
+ *os << "\n .roundedCornersRadiusY = " << settings.roundedCornersRadius.y;
*os << "\n .roundedCornersCrop = ";
PrintTo(settings.roundedCornersCrop, os);
*os << "\n}";
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index f3064f3..c39f0a9 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -66,7 +66,7 @@
Geometry{
.boundaries = rect,
.roundedCornersCrop = rect,
- .roundedCornersRadius = 50.f,
+ .roundedCornersRadius = {50.f, 50.f},
},
// drawShadow ignores alpha
.shadow =
@@ -87,7 +87,7 @@
Geometry{
.boundaries = smallerRect,
.roundedCornersCrop = rect,
- .roundedCornersRadius = 50.f,
+ .roundedCornersRadius = {50.f, 50.f},
},
.source =
PixelSource{
@@ -148,7 +148,7 @@
// In reduced shader mode, all non-zero round rect radii get the same code path.
for (float roundedCornersRadius : {0.0f, 50.0f}) {
// roundedCornersCrop is always set, but the radius triggers the behavior
- layer.geometry.roundedCornersRadius = roundedCornersRadius;
+ layer.geometry.roundedCornersRadius = {roundedCornersRadius, roundedCornersRadius};
for (bool isOpaque : {true, false}) {
layer.source.buffer.isOpaque = isOpaque;
for (auto alpha : {half(.2f), half(1.0f)}) {
@@ -181,7 +181,7 @@
for (auto transform : {mat4(), kScaleAndTranslate}) {
layer.geometry.positionTransform = transform;
for (float roundedCornersRadius : {0.0f, 50.f}) {
- layer.geometry.roundedCornersRadius = roundedCornersRadius;
+ layer.geometry.roundedCornersRadius = {roundedCornersRadius, roundedCornersRadius};
auto layers = std::vector<LayerSettings>{layer};
renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
base::unique_fd());
@@ -238,7 +238,7 @@
.geometry =
Geometry{
.boundaries = rect,
- .roundedCornersRadius = 27, // larger than the 20 above.
+ .roundedCornersRadius = {27.f, 27.f},
.roundedCornersCrop =
FloatRect(0, 0, displayRect.width(), displayRect.height()),
},
@@ -275,9 +275,9 @@
// larger than the layer bounds.
.positionTransform = kFlip,
.boundaries = rect,
- .roundedCornersRadius = 94.2551,
- .roundedCornersCrop = FloatRect(
- -93.75, 0, displayRect.width() + 93.75, displayRect.height()),
+ .roundedCornersRadius = {94.2551f, 94.2551f},
+ .roundedCornersCrop = FloatRect(-93.75, 0, displayRect.width() + 93.75,
+ displayRect.height()),
},
.source = PixelSource{.buffer =
Buffer{
@@ -307,10 +307,11 @@
// the boundaries have to be smaller than the rounded crop so that
// clipRRect is used instead of drawRRect
.boundaries = small,
- .roundedCornersRadius = 50.f,
+ .roundedCornersRadius = {50.f, 50.f},
.roundedCornersCrop = rect,
},
- .source = PixelSource{
+ .source =
+ PixelSource{
.solidColor = half3(0.f, 0.f, 0.f),
},
.sourceDataspace = kDestDataSpace,
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 97271cb..0caa9f2 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -1276,7 +1276,7 @@
* produce the insected roundRect. If false, the returned state of the radii param is undefined.
*/
static bool intersectionIsRoundRect(const SkRect& bounds, const SkRect& crop,
- const SkRect& insetCrop, float cornerRadius,
+ const SkRect& insetCrop, const vec2& cornerRadius,
SkVector radii[4]) {
const bool leftEqual = bounds.fLeft == crop.fLeft;
const bool topEqual = bounds.fTop == crop.fTop;
@@ -1288,8 +1288,8 @@
// In particular the round rect implementation will scale the value of all corner radii
// if the sum of the radius along any edge is greater than the length of that edge.
// See https://www.w3.org/TR/css-backgrounds-3/#corner-overlap
- const bool requiredWidth = bounds.width() > (cornerRadius * 2);
- const bool requiredHeight = bounds.height() > (cornerRadius * 2);
+ const bool requiredWidth = bounds.width() > (cornerRadius.x * 2);
+ const bool requiredHeight = bounds.height() > (cornerRadius.y * 2);
if (!requiredWidth || !requiredHeight) {
return false;
}
@@ -1298,7 +1298,7 @@
// contained within the cropped shape and does not need rounded.
// compute the UpperLeft corner radius
if (leftEqual && topEqual) {
- radii[0].set(cornerRadius, cornerRadius);
+ radii[0].set(cornerRadius.x, cornerRadius.y);
} else if ((leftEqual && bounds.fTop >= insetCrop.fTop) ||
(topEqual && bounds.fLeft >= insetCrop.fLeft)) {
radii[0].set(0, 0);
@@ -1307,7 +1307,7 @@
}
// compute the UpperRight corner radius
if (rightEqual && topEqual) {
- radii[1].set(cornerRadius, cornerRadius);
+ radii[1].set(cornerRadius.x, cornerRadius.y);
} else if ((rightEqual && bounds.fTop >= insetCrop.fTop) ||
(topEqual && bounds.fRight <= insetCrop.fRight)) {
radii[1].set(0, 0);
@@ -1316,7 +1316,7 @@
}
// compute the BottomRight corner radius
if (rightEqual && bottomEqual) {
- radii[2].set(cornerRadius, cornerRadius);
+ radii[2].set(cornerRadius.x, cornerRadius.y);
} else if ((rightEqual && bounds.fBottom <= insetCrop.fBottom) ||
(bottomEqual && bounds.fRight <= insetCrop.fRight)) {
radii[2].set(0, 0);
@@ -1325,7 +1325,7 @@
}
// compute the BottomLeft corner radius
if (leftEqual && bottomEqual) {
- radii[3].set(cornerRadius, cornerRadius);
+ radii[3].set(cornerRadius.x, cornerRadius.y);
} else if ((leftEqual && bounds.fBottom <= insetCrop.fBottom) ||
(bottomEqual && bounds.fLeft >= insetCrop.fLeft)) {
radii[3].set(0, 0);
@@ -1338,22 +1338,22 @@
inline std::pair<SkRRect, SkRRect> SkiaGLRenderEngine::getBoundsAndClip(const FloatRect& boundsRect,
const FloatRect& cropRect,
- const float cornerRadius) {
+ const vec2& cornerRadius) {
const SkRect bounds = getSkRect(boundsRect);
const SkRect crop = getSkRect(cropRect);
SkRRect clip;
- if (cornerRadius > 0) {
+ if (cornerRadius.x > 0 && cornerRadius.y > 0) {
// it the crop and the bounds are equivalent or there is no crop then we don't need a clip
if (bounds == crop || crop.isEmpty()) {
- return {SkRRect::MakeRectXY(bounds, cornerRadius, cornerRadius), clip};
+ return {SkRRect::MakeRectXY(bounds, cornerRadius.x, cornerRadius.y), clip};
}
// This makes an effort to speed up common, simple bounds + clip combinations by
// converting them to a single RRect draw. It is possible there are other cases
// that can be converted.
if (crop.contains(bounds)) {
- const auto insetCrop = crop.makeInset(cornerRadius, cornerRadius);
+ const auto insetCrop = crop.makeInset(cornerRadius.x, cornerRadius.y);
if (insetCrop.contains(bounds)) {
return {SkRRect::MakeRect(bounds), clip}; // clip is empty - no rounding required
}
@@ -1367,7 +1367,7 @@
}
// we didn't hit any of our fast paths so set the clip to the cropRect
- clip.setRectXY(crop, cornerRadius, cornerRadius);
+ clip.setRectXY(crop, cornerRadius.x, cornerRadius.y);
}
// if we hit this point then we either don't have rounded corners or we are going to rely
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 5ef9944..68c3363 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -90,7 +90,8 @@
inline SkRect getSkRect(const FloatRect& layer);
inline SkRect getSkRect(const Rect& layer);
inline std::pair<SkRRect, SkRRect> getBoundsAndClip(const FloatRect& bounds,
- const FloatRect& crop, float cornerRadius);
+ const FloatRect& crop,
+ const vec2& cornerRadius);
inline bool layerHasBlur(const LayerSettings& layer, bool colorTransformModifiesAlpha);
inline SkColor getSkColor(const vec4& color);
inline SkM44 getSkM44(const mat4& matrix);
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 7c70a74..8889f76 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -444,7 +444,9 @@
const ubyte4& backgroundColor) {
const Rect casterRect(castingLayer.geometry.boundaries);
Region casterRegion = Region(casterRect);
- const float casterCornerRadius = castingLayer.geometry.roundedCornersRadius;
+ const float casterCornerRadius = (castingLayer.geometry.roundedCornersRadius.x +
+ castingLayer.geometry.roundedCornersRadius.y) /
+ 2.0;
if (casterCornerRadius > 0.0f) {
// ignore the corners if a corner radius is set
Rect cornerRect(casterCornerRadius, casterCornerRadius);
@@ -1129,7 +1131,7 @@
renderengine::LayerSettings layer;
layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
layer.geometry.boundaries = fullscreenRect().toFloatRect();
- layer.geometry.roundedCornersRadius = 5.0f;
+ layer.geometry.roundedCornersRadius = {5.0f, 5.0f};
layer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect();
SourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
layer.alpha = 1.0f;
@@ -2131,7 +2133,7 @@
casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
renderengine::LayerSettings castingLayer;
castingLayer.geometry.boundaries = casterBounds.toFloatRect();
- castingLayer.geometry.roundedCornersRadius = 3.0f;
+ castingLayer.geometry.roundedCornersRadius = {3.0f, 3.0f};
castingLayer.geometry.roundedCornersCrop = casterBounds.toFloatRect();
castingLayer.alpha = 1.0f;
renderengine::ShadowSettings settings =
@@ -2219,7 +2221,8 @@
renderengine::LayerSettings redLayer;
redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
redLayer.geometry.boundaries = fullscreenRect().toFloatRect();
- redLayer.geometry.roundedCornersRadius = 5.0f;
+ redLayer.geometry.roundedCornersRadius = {5.0f, 5.0f};
+
redLayer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect();
// Red background.
redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
@@ -2231,7 +2234,7 @@
renderengine::LayerSettings greenLayer;
greenLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
greenLayer.geometry.boundaries = fullscreenRect().toFloatRect();
- greenLayer.geometry.roundedCornersRadius = 5.0f;
+ greenLayer.geometry.roundedCornersRadius = {5.0f, 5.0f};
// Bottom right corner is not going to be rounded.
greenLayer.geometry.roundedCornersCrop =
Rect(DEFAULT_DISPLAY_WIDTH / 3, DEFAULT_DISPLAY_HEIGHT / 3, DEFAULT_DISPLAY_HEIGHT,
@@ -2268,7 +2271,7 @@
renderengine::LayerSettings redLayer;
redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
redLayer.geometry.boundaries = fullscreenRect().toFloatRect();
- redLayer.geometry.roundedCornersRadius = 5.0f;
+ redLayer.geometry.roundedCornersRadius = {5.0f, 5.0f};
redLayer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect();
// Red background.
redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
@@ -2313,7 +2316,7 @@
renderengine::LayerSettings redLayer;
redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
redLayer.geometry.boundaries = FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, 32);
- redLayer.geometry.roundedCornersRadius = 64;
+ redLayer.geometry.roundedCornersRadius = {64.0f, 64.0f};
redLayer.geometry.roundedCornersCrop = FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, 128);
// Red background.
redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
@@ -2334,6 +2337,49 @@
expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, 31), 255, 0, 0, 255);
}
+TEST_P(RenderEngineTest, testRoundedCornersXY) {
+ if (GetParam()->type() != renderengine::RenderEngine::RenderEngineType::SKIA_GL) {
+ GTEST_SKIP();
+ }
+
+ initializeRenderEngine();
+
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+ settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+
+ std::vector<renderengine::LayerSettings> layers;
+
+ renderengine::LayerSettings redLayer;
+ redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+ redLayer.geometry.boundaries = fullscreenRect().toFloatRect();
+ redLayer.geometry.roundedCornersRadius = {5.0f, 20.0f};
+ redLayer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect();
+ // Red background.
+ redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
+ redLayer.alpha = 1.0f;
+
+ layers.push_back(redLayer);
+
+ invokeDraw(settings, layers);
+
+ // Due to roundedCornersRadius, the corners are untouched.
+ expectBufferColor(Point(0, 0), 0, 0, 0, 0);
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 0), 0, 0, 0, 0);
+ expectBufferColor(Point(0, DEFAULT_DISPLAY_HEIGHT - 1), 0, 0, 0, 0);
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, DEFAULT_DISPLAY_HEIGHT - 1), 0, 0, 0, 0);
+
+ // Y-axis draws a larger radius, check that its untouched as well
+ expectBufferColor(Point(0, DEFAULT_DISPLAY_HEIGHT - 5), 0, 0, 0, 0);
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, DEFAULT_DISPLAY_HEIGHT - 5), 0, 0, 0, 0);
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 5), 0, 0, 0, 0);
+ expectBufferColor(Point(0, 5), 0, 0, 0, 0);
+
+ // middle should be red
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 255, 0, 0, 255);
+}
+
TEST_P(RenderEngineTest, testClear) {
initializeRenderEngine();
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index d138495..41a7adc 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -158,6 +158,7 @@
defaults: [
"libui-defaults",
+ "gralloc_10_usage_bits_defaults",
// Uncomment the following line to enable VALIDATE_REGIONS traces
//defaults: ["libui-validate-regions-defaults"],
],
diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp
index f23f10a..995dded 100644
--- a/libs/ui/Gralloc2.cpp
+++ b/libs/ui/Gralloc2.cpp
@@ -48,6 +48,13 @@
for (const auto bit : hardware::hidl_enum_range<BufferUsage>()) {
bits = bits | bit;
}
+
+ if (ADDNL_GRALLOC_10_USAGE_BITS) {
+ uint64_t addnl_bits = static_cast<uint64_t>(ADDNL_GRALLOC_10_USAGE_BITS);
+ ALOGI("Adding additional valid usage bits: 0x%" PRIx64, addnl_bits);
+ bits = bits | addnl_bits;
+ }
+
return bits;
}();
return valid10UsageBits;
diff --git a/libs/ui/Gralloc3.cpp b/libs/ui/Gralloc3.cpp
index 15c60bc..a50e497 100644
--- a/libs/ui/Gralloc3.cpp
+++ b/libs/ui/Gralloc3.cpp
@@ -47,6 +47,13 @@
hardware::hidl_enum_range<hardware::graphics::common::V1_2::BufferUsage>()) {
bits = bits | bit;
}
+
+ if (ADDNL_GRALLOC_10_USAGE_BITS) {
+ uint64_t addnl_bits = static_cast<uint64_t>(ADDNL_GRALLOC_10_USAGE_BITS);
+ ALOGI("Adding additional valid usage bits: 0x%" PRIx64, addnl_bits);
+ bits = bits | addnl_bits;
+ }
+
return bits;
}();
return validUsageBits;
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
index f6ab7b2..b79f785 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -73,6 +73,13 @@
hardware::hidl_enum_range<hardware::graphics::common::V1_2::BufferUsage>()) {
bits = bits | bit;
}
+
+ if (ADDNL_GRALLOC_10_USAGE_BITS) {
+ uint64_t addnl_bits = static_cast<uint64_t>(ADDNL_GRALLOC_10_USAGE_BITS);
+ ALOGI("Adding additional valid usage bits: 0x%" PRIx64, addnl_bits);
+ bits = bits | addnl_bits;
+ }
+
return bits;
}();
return validUsageBits | kRemovedUsageBits;
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index c9fce8a..09e3290 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -136,7 +136,11 @@
cc_library_shared {
name: "libEGL",
- defaults: ["egl_libs_defaults"],
+ defaults: [
+ "egl_libs_defaults",
+ "egl_display_array_defaults",
+ "nvidia_enhancements_defaults"
+ ],
llndk: {
symbol_file: "libEGL.map.txt",
export_llndk_headers: ["gl_headers"],
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 76fd7f0..65c3c49 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -631,6 +631,11 @@
ALOGE_IF(!getProcAddress,
"can't find eglGetProcAddress() in EGL driver library");
+#ifdef NV_ANDROID_FRAMEWORK_ENHANCEMENTS
+ // This internally sets a bit in the main Nvidia EGL driver to enable desktop openGL
+ getProcAddress("eglSentinelForNVFrameworks");
+#endif
+
egl_t* egl = &cnx->egl;
__eglMustCastToProperFunctionPointerType* curr =
(__eglMustCastToProperFunctionPointerType*)egl;
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 0b755aa..7eeb57d 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -74,8 +74,12 @@
return eglDisplay ? eglDisplay->getRefsCount() : 0;
}
+#ifdef EGL_DISPLAY_ARRAY
+egl_display_t egl_display_t::sDisplay[NUM_DISPLAYS];
+#else
std::map<EGLDisplay, std::unique_ptr<egl_display_t>> egl_display_t::displayMap;
std::mutex egl_display_t::displayMapLock;
+#endif
egl_display_t::egl_display_t()
: magic('_dpy'),
@@ -94,12 +98,21 @@
return nullptr;
}
+#ifdef EGL_DISPLAY_ARRAY
+ uintptr_t index = uintptr_t(dpy) - 1U;
+ if (index >= NUM_DISPLAYS || !sDisplay[index].isValid()) {
+#else
const std::lock_guard<std::mutex> lock(displayMapLock);
auto search = displayMap.find(dpy);
if (search == displayMap.end() || !search->second->isValid()) {
+#endif
return nullptr;
}
+#ifdef EGL_DISPLAY_ARRAY
+ return &sDisplay[index];
+#else
return search->second.get();
+#endif
}
void egl_display_t::addObject(egl_object_t* object) {
@@ -127,7 +140,11 @@
const EGLAttrib* attrib_list) {
if (uintptr_t(disp) >= NUM_DISPLAYS) return nullptr;
+#ifdef EGL_DISPLAY_ARRAY
+ return sDisplay[uintptr_t(disp)].getPlatformDisplay(disp, attrib_list);
+#else
return getPlatformDisplay(disp, attrib_list);
+#endif
}
static EGLDisplay getPlatformDisplayAngle(EGLNativeDisplayType display, egl_connection_t* const cnx,
@@ -182,6 +199,9 @@
EGLDisplay egl_display_t::getPlatformDisplay(EGLNativeDisplayType display,
const EGLAttrib* attrib_list) {
+#ifdef EGL_DISPLAY_ARRAY
+ std::lock_guard<std::mutex> _l(lock);
+#endif
ATRACE_CALL();
// get our driver loader
@@ -217,9 +237,14 @@
}
}
+#ifdef EGL_DISPLAY_ARRAY
+ disp.dpy = dpy;
+#endif
if (dpy == EGL_NO_DISPLAY) {
loader.close(cnx);
- } else {
+ }
+#ifndef EGL_DISPLAY_ARRAY
+ else {
const std::lock_guard<std::mutex> lock(displayMapLock);
if (displayMap.find(dpy) == displayMap.end()) {
auto d = std::make_unique<egl_display_t>();
@@ -228,9 +253,14 @@
}
return dpy;
}
+#endif
}
+#ifdef EGL_DISPLAY_ARRAY
+ return EGLDisplay(uintptr_t(display) + 1U);
+#else
return nullptr;
+#endif
}
EGLBoolean egl_display_t::initialize(EGLint* major, EGLint* minor) {
diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h
index 87c2176..4438826 100644
--- a/opengl/libs/EGL/egl_display.h
+++ b/opengl/libs/EGL/egl_display.h
@@ -23,8 +23,10 @@
#include <stdint.h>
#include <condition_variable>
+#ifndef EGL_DISPLAY_ARRAY
#include <map>
#include <memory>
+#endif
#include <mutex>
#include <string>
#include <unordered_set>
@@ -42,11 +44,19 @@
bool needsAndroidPEglMitigation();
class EGLAPI egl_display_t { // marked as EGLAPI for testing purposes
+#ifdef EGL_DISPLAY_ARRAY
+ static egl_display_t sDisplay[NUM_DISPLAYS];
+#else
static std::map<EGLDisplay, std::unique_ptr<egl_display_t>> displayMap;
static std::mutex displayMapLock;
+#endif
EGLDisplay getDisplay(EGLNativeDisplayType display);
+#ifdef EGL_DISPLAY_ARRAY
+ EGLDisplay getPlatformDisplay(EGLNativeDisplayType display, const EGLAttrib* attrib_list);
+#else
static EGLDisplay getPlatformDisplay(EGLNativeDisplayType display,
const EGLAttrib* attrib_list);
+#endif
void loseCurrentImpl(egl_context_t* cur_c);
public:
diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp
index efbe613..e8ce92d 100644
--- a/opengl/libs/EGL/egl_object.cpp
+++ b/opengl/libs/EGL/egl_object.cpp
@@ -317,7 +317,14 @@
// call the implementation's glGetString(GL_EXTENSIONS)
const char* exts = (const char*)gEGLImpl.hooks[version]->gl.glGetString(GL_EXTENSIONS);
- if (!exts) return;
+ if (!exts) {
+#ifndef NV_ANDROID_FRAMEWORK_ENHANCEMENTS
+ return;
+#else
+ gEGLImpl.hooks[version]->gl.glGetError();
+ exts = "";
+#endif
+}
// If this context is sharing with another context, and the other context was reset
// e.g. due to robustness failure, this context might also be reset and glGetString can
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index f4dbe49..8bd2b83 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -924,6 +924,12 @@
if (context != EGL_NO_CONTEXT) {
// figure out if it's a GLESv1 or GLESv2
int version = egl_connection_t::GLESv1_INDEX;
+
+#ifdef NV_ANDROID_FRAMEWORK_ENHANCEMENTS
+ if (cnx->egl.eglQueryAPI() == EGL_OPENGL_API)
+ version = egl_connection_t::GLESv2_INDEX;
+#endif
+
if (attrib_list) {
while (*attrib_list != EGL_NONE) {
GLint attr = *attrib_list++;
@@ -1150,6 +1156,10 @@
addr = findBuiltinWrapper(procname);
if (addr) return addr;
+#ifdef NV_ANDROID_FRAMEWORK_ENHANCEMENTS
+ if (gEGLImpl.dso && gEGLImpl.egl.eglGetProcAddress)
+ addr = gEGLImpl.egl.eglGetProcAddress(procname);
+#else
// this protects accesses to sGLExtensionMap, sGLExtensionSlot, and sGLExtensionSlotMap
pthread_mutex_lock(&sExtensionMapMutex);
@@ -1234,6 +1244,7 @@
}
pthread_mutex_unlock(&sExtensionMapMutex);
+#endif
return addr;
}
diff --git a/services/gpuservice/vts/OWNERS b/services/gpuservice/vts/OWNERS
index e789052..a63de1c 100644
--- a/services/gpuservice/vts/OWNERS
+++ b/services/gpuservice/vts/OWNERS
@@ -1,4 +1,5 @@
# Bug component: 653544
+kocdemir@google.com
paulthomson@google.com
pbaiget@google.com
lfy@google.com
diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp
index 2a3924b..dce327e 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -196,9 +196,20 @@
}
coords += StringPrintf("{%" PRIu32 ": ", i);
coords +=
- StringPrintf("id=%" PRIu32 " x=%.1f y=%.1f, pressure=%.1f", pointerProperties[i].id,
+ StringPrintf("id=%" PRIu32 " x=%.1f y=%.1f pressure=%.1f", pointerProperties[i].id,
pointerCoords[i].getX(), pointerCoords[i].getY(),
pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
+ const int32_t toolType = pointerProperties[i].toolType;
+ if (toolType != AMOTION_EVENT_TOOL_TYPE_FINGER) {
+ coords += StringPrintf(" toolType=%s", motionToolTypeToString(toolType));
+ }
+ const float major = pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR);
+ const float minor = pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR);
+ const float orientation = pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
+ if (major != 0 || minor != 0) {
+ coords += StringPrintf(" major=%.1f minor=%.1f orientation=%.1f", major, minor,
+ orientation);
+ }
coords += "}";
}
return StringPrintf("NotifyMotionArgs(id=%" PRId32 ", eventTime=%" PRId64 ", deviceId=%" PRId32
diff --git a/services/inputflinger/UnwantedInteractionBlocker.cpp b/services/inputflinger/UnwantedInteractionBlocker.cpp
index f57ff33..ec41025 100644
--- a/services/inputflinger/UnwantedInteractionBlocker.cpp
+++ b/services/inputflinger/UnwantedInteractionBlocker.cpp
@@ -29,8 +29,40 @@
using android::base::StringPrintf;
+/**
+ * This type is declared here to ensure consistency between the instantiated type (used in the
+ * constructor via std::make_unique) and the cast-to type (used in PalmRejector::dump() with
+ * static_cast). Due to the lack of rtti support, dynamic_cast is not available, so this can't be
+ * checked at runtime to avoid undefined behaviour.
+ */
+using PalmFilterImplementation = ::ui::NeuralStylusPalmDetectionFilter;
+
namespace android {
+/**
+ * Log detailed debug messages about each inbound motion event notification to the blocker.
+ * Enable this via "adb shell setprop log.tag.UnwantedInteractionBlockerInboundMotion DEBUG"
+ * (requires restart)
+ */
+const bool DEBUG_INBOUND_MOTION =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "InboundMotion", ANDROID_LOG_INFO);
+
+/**
+ * Log detailed debug messages about each outbound motion event processed by the blocker.
+ * Enable this via "adb shell setprop log.tag.UnwantedInteractionBlockerOutboundMotion DEBUG"
+ * (requires restart)
+ */
+const bool DEBUG_OUTBOUND_MOTION =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "OutboundMotion", ANDROID_LOG_INFO);
+
+/**
+ * Log the data sent to the model and received back from the model.
+ * Enable this via "adb shell setprop log.tag.UnwantedInteractionBlockerModel DEBUG"
+ * (requires restart)
+ */
+const bool DEBUG_MODEL =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Model", ANDROID_LOG_INFO);
+
// Category (=namespace) name for the input settings that are applied at boot time
static const char* INPUT_NATIVE_BOOT = "input_native_boot";
/**
@@ -45,13 +77,11 @@
}
static bool isFromTouchscreen(int32_t source) {
- return isFromSource(source, AINPUT_SOURCE_TOUCHSCREEN) &&
- !isFromSource(source, AINPUT_SOURCE_STYLUS);
+ return isFromSource(source, AINPUT_SOURCE_TOUCHSCREEN);
}
static ::base::TimeTicks toChromeTimestamp(nsecs_t eventTime) {
- return ::base::TimeTicks::UnixEpoch() +
- ::base::Milliseconds(static_cast<float>(ns2ms(eventTime)));
+ return ::base::TimeTicks::UnixEpoch() + ::base::TimeDelta::FromNanosecondsD(eventTime);
}
/**
@@ -61,39 +91,22 @@
static bool isPalmRejectionEnabled() {
std::string value = toLower(
server_configurable_flags::GetServerConfigurableFlag(INPUT_NATIVE_BOOT,
- PALM_REJECTION_ENABLED, "false"));
- if (value == "true" || value == "1") {
+ PALM_REJECTION_ENABLED, "0"));
+ if (value == "1") {
return true;
}
return false;
}
-static int getLinuxToolType(int32_t toolType) {
- switch (toolType) {
- case AMOTION_EVENT_TOOL_TYPE_FINGER:
- return MT_TOOL_FINGER;
- case AMOTION_EVENT_TOOL_TYPE_STYLUS:
- return MT_TOOL_PEN;
- case AMOTION_EVENT_TOOL_TYPE_PALM:
- return MT_TOOL_PALM;
+static int getLinuxToolCode(int toolType) {
+ if (toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS) {
+ return BTN_TOOL_PEN;
}
- ALOGW("Got tool type %" PRId32 ", converting to MT_TOOL_FINGER", toolType);
- return MT_TOOL_FINGER;
-}
-
-static std::string dumpDeviceInfo(const AndroidPalmFilterDeviceInfo& info) {
- std::string out;
- out += StringPrintf("max_x = %.2f\n", info.max_x);
- out += StringPrintf("max_y = %.2f\n", info.max_y);
- out += StringPrintf("x_res = %.2f\n", info.x_res);
- out += StringPrintf("y_res = %.2f\n", info.y_res);
- out += StringPrintf("major_radius_res = %.2f\n", info.major_radius_res);
- out += StringPrintf("minor_radius_res = %.2f\n", info.minor_radius_res);
- out += StringPrintf("minor_radius_supported = %s\n",
- info.minor_radius_supported ? "true" : "false");
- out += StringPrintf("touch_major_res = %" PRId32 "\n", info.touch_major_res);
- out += StringPrintf("touch_minor_res = %" PRId32 "\n", info.touch_minor_res);
- return out;
+ if (toolType == AMOTION_EVENT_TOOL_TYPE_FINGER) {
+ return BTN_TOOL_FINGER;
+ }
+ ALOGW("Got tool type %" PRId32 ", converting to BTN_TOOL_FINGER", toolType);
+ return BTN_TOOL_FINGER;
}
static int32_t getActionUpForPointerId(const NotifyMotionArgs& args, int32_t pointerId) {
@@ -128,32 +141,6 @@
return AMOTION_EVENT_ACTION_MOVE;
}
-std::string toString(const ::ui::InProgressTouchEvdev& touch) {
- return StringPrintf("x=%.1f, y=%.1f, tracking_id=%i, slot=%zu,"
- " pressure=%.1f, major=%i, minor=%i, "
- "tool_type=%i, altered=%s, was_touching=%s, touching=%s",
- touch.x, touch.y, touch.tracking_id, touch.slot, touch.pressure,
- touch.major, touch.minor, touch.tool_type, toString(touch.altered),
- toString(touch.was_touching), toString(touch.touching));
-}
-
-/**
- * Remove the data for the provided pointers from the args. The pointers are identified by their
- * pointerId, not by the index inside the array.
- * Return the new NotifyMotionArgs struct that has the remaining pointers.
- * The only fields that may be different in the returned args from the provided args are:
- * - action
- * - pointerCount
- * - pointerProperties
- * - pointerCoords
- * Action might change because it contains a pointer index. If another pointer is removed, the
- * active pointer index would be shifted.
- * Do not call this function for events with POINTER_UP or POINTER_DOWN events when removed pointer
- * id is the acting pointer id.
- *
- * @param args the args from which the pointers should be removed
- * @param pointerIds the pointer ids of the pointers that should be removed
- */
NotifyMotionArgs removePointerIds(const NotifyMotionArgs& args,
const std::set<int32_t>& pointerIds) {
const uint8_t actionIndex = MotionEvent::getActionIndex(args.action);
@@ -199,6 +186,26 @@
return newArgs;
}
+/**
+ * Remove stylus pointers from the provided NotifyMotionArgs.
+ *
+ * Return NotifyMotionArgs where the stylus pointers have been removed.
+ * If this results in removal of the active pointer, then return nullopt.
+ */
+static std::optional<NotifyMotionArgs> removeStylusPointerIds(const NotifyMotionArgs& args) {
+ std::set<int32_t> stylusPointerIds;
+ for (uint32_t i = 0; i < args.pointerCount; i++) {
+ if (args.pointerProperties[i].toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS) {
+ stylusPointerIds.insert(args.pointerProperties[i].id);
+ }
+ }
+ NotifyMotionArgs withoutStylusPointers = removePointerIds(args, stylusPointerIds);
+ if (withoutStylusPointers.pointerCount == 0 || withoutStylusPointers.action == ACTION_UNKNOWN) {
+ return std::nullopt;
+ }
+ return withoutStylusPointers;
+}
+
std::optional<AndroidPalmFilterDeviceInfo> createPalmFilterDeviceInfo(
const InputDeviceInfo& deviceInfo) {
if (!isFromTouchscreen(deviceInfo.getSources())) {
@@ -326,6 +333,7 @@
}
void UnwantedInteractionBlocker::notifyMotion(const NotifyMotionArgs* args) {
+ ALOGD_IF(DEBUG_INBOUND_MOTION, "%s: %s", __func__, args->dump().c_str());
{ // acquire lock
std::scoped_lock lock(mLock);
const std::vector<NotifyMotionArgs> processedArgs =
@@ -339,17 +347,22 @@
mQueuedListener.flush();
}
+void UnwantedInteractionBlocker::enqueueOutboundMotionLocked(const NotifyMotionArgs& args) {
+ ALOGD_IF(DEBUG_OUTBOUND_MOTION, "%s: %s", __func__, args.dump().c_str());
+ mQueuedListener.notifyMotion(&args);
+}
+
void UnwantedInteractionBlocker::notifyMotionLocked(const NotifyMotionArgs* args) {
auto it = mPalmRejectors.find(args->deviceId);
const bool sendToPalmRejector = it != mPalmRejectors.end() && isFromTouchscreen(args->source);
if (!sendToPalmRejector) {
- mQueuedListener.notifyMotion(args);
+ enqueueOutboundMotionLocked(*args);
return;
}
std::vector<NotifyMotionArgs> processedArgs = it->second.processMotion(*args);
for (const NotifyMotionArgs& loopArgs : processedArgs) {
- mQueuedListener.notifyMotion(&loopArgs);
+ enqueueOutboundMotionLocked(loopArgs);
}
}
@@ -428,9 +441,10 @@
dump += "UnwantedInteractionBlocker:\n";
dump += " mPreferStylusOverTouchBlocker:\n";
dump += addLinePrefix(mPreferStylusOverTouchBlocker.dump(), " ");
- dump += StringPrintf(" mEnablePalmRejection: %s\n", toString(mEnablePalmRejection));
+ dump += StringPrintf(" mEnablePalmRejection: %s\n",
+ std::to_string(mEnablePalmRejection).c_str());
dump += StringPrintf(" isPalmRejectionEnabled (flag value): %s\n",
- toString(isPalmRejectionEnabled()));
+ std::to_string(isPalmRejectionEnabled()).c_str());
dump += mPalmRejectors.empty() ? " mPalmRejectors: None\n" : " mPalmRejectors:\n";
for (const auto& [deviceId, palmRejector] : mPalmRejectors) {
dump += StringPrintf(" deviceId = %" PRId32 ":\n", deviceId);
@@ -512,6 +526,15 @@
return out;
}
+class AndroidPalmRejectionModel : public ::ui::OneDeviceTrainNeuralStylusPalmDetectionFilterModel {
+public:
+ AndroidPalmRejectionModel()
+ : ::ui::OneDeviceTrainNeuralStylusPalmDetectionFilterModel(/*default version*/ "",
+ std::vector<float>()) {
+ config_.resample_period = ::ui::kResamplePeriod;
+ }
+};
+
PalmRejector::PalmRejector(const AndroidPalmFilterDeviceInfo& info,
std::unique_ptr<::ui::PalmDetectionFilter> filter)
: mSharedPalmState(std::make_unique<::ui::SharedPalmDetectionFilterState>()),
@@ -523,11 +546,9 @@
return;
}
std::unique_ptr<::ui::NeuralStylusPalmDetectionFilterModel> model =
- std::make_unique<::ui::OneDeviceTrainNeuralStylusPalmDetectionFilterModel>(
- std::vector<float>());
- mPalmDetectionFilter =
- std::make_unique<::ui::NeuralStylusPalmDetectionFilter>(mDeviceInfo, std::move(model),
- mSharedPalmState.get());
+ std::make_unique<AndroidPalmRejectionModel>();
+ mPalmDetectionFilter = std::make_unique<PalmFilterImplementation>(mDeviceInfo, std::move(model),
+ mSharedPalmState.get());
}
std::vector<::ui::InProgressTouchEvdev> getTouches(const NotifyMotionArgs& args,
@@ -541,7 +562,7 @@
touches.emplace_back(::ui::InProgressTouchEvdev());
touches.back().major = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR);
touches.back().minor = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR);
- touches.back().tool_type = getLinuxToolType(args.pointerProperties[i].toolType);
+ // The field 'tool_type' is not used for palm rejection
// Whether there is new information for the touch.
touches.back().altered = true;
@@ -588,15 +609,57 @@
// 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::set<int32_t> PalmRejector::detectPalmPointers(const NotifyMotionArgs& args) {
+ std::bitset<::ui::kNumTouchEvdevSlots> slotsToHold;
+ std::bitset<::ui::kNumTouchEvdevSlots> slotsToSuppress;
+
+ // Store the slot state before we call getTouches and update it. This way, we can find
+ // the slots that have been removed due to the incoming event.
+ SlotState oldSlotState = mSlotState;
+ mSlotState.update(args);
+
+ std::vector<::ui::InProgressTouchEvdev> touches =
+ getTouches(args, mDeviceInfo, oldSlotState, mSlotState);
+ ::base::TimeTicks chromeTimestamp = toChromeTimestamp(args.eventTime);
+
+ if (DEBUG_MODEL) {
+ std::stringstream touchesStream;
+ for (const ::ui::InProgressTouchEvdev& touch : touches) {
+ touchesStream << touch.tracking_id << " : " << touch << "\n";
+ }
+ ALOGD("Filter: touches = %s", touchesStream.str().c_str());
+ }
+
+ mPalmDetectionFilter->Filter(touches, chromeTimestamp, &slotsToHold, &slotsToSuppress);
+
+ ALOGD_IF(DEBUG_MODEL, "Response: slotsToHold = %s, slotsToSuppress = %s",
+ slotsToHold.to_string().c_str(), slotsToSuppress.to_string().c_str());
+
+ // Now that we know which slots should be suppressed, let's convert those to pointer id's.
+ std::set<int32_t> 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);
+ if (!slot) {
+ slot = mSlotState.getSlotForPointerId(pointerId);
+ LOG_ALWAYS_FATAL_IF(!slot, "Could not find slot for pointer id %" PRId32, pointerId);
+ }
+ if (slotsToSuppress.test(*slot)) {
+ newSuppressedIds.insert(pointerId);
+ }
+ }
+ return newSuppressedIds;
+}
+
std::vector<NotifyMotionArgs> PalmRejector::processMotion(const NotifyMotionArgs& args) {
if (mPalmDetectionFilter == nullptr) {
return {args};
@@ -614,32 +677,17 @@
if (args.action == AMOTION_EVENT_ACTION_DOWN) {
mSuppressedPointerIds.clear();
}
- std::bitset<::ui::kNumTouchEvdevSlots> slotsToHold;
- std::bitset<::ui::kNumTouchEvdevSlots> slotsToSuppress;
- // Store the slot state before we call getTouches and update it. This way, we can find
- // 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);
-
- mPalmDetectionFilter->Filter(touches, chromeTimestamp, &slotsToHold, &slotsToSuppress);
-
- // 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);
- 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);
- if (!slot) {
- slot = mSlotState.getSlotForPointerId(pointerId);
- LOG_ALWAYS_FATAL_IF(!slot, "Could not find slot for pointer id %" PRId32, pointerId);
- }
- if (slotsToSuppress.test(*slot)) {
- mSuppressedPointerIds.insert(pointerId);
- }
+
+ 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 =
@@ -648,29 +696,40 @@
LOG_ALWAYS_FATAL_IF(checkArgs.action == ACTION_UNKNOWN, "%s", checkArgs.dump().c_str());
}
- if (mSuppressedPointerIds != oldSuppressedIds) {
- if (argsWithoutUnwantedPointers.size() != 1 ||
- argsWithoutUnwantedPointers[0].pointerCount != args.pointerCount) {
- ALOGI("Palm detected, removing pointer ids %s from %s",
- dumpSet(mSuppressedPointerIds).c_str(), args.dump().c_str());
- }
+ // Only log if new pointers are getting rejected. That means mSuppressedPointerIds is not a
+ // subset of oldSuppressedIds.
+ if (!std::includes(oldSuppressedIds.begin(), oldSuppressedIds.end(),
+ mSuppressedPointerIds.begin(), mSuppressedPointerIds.end())) {
+ ALOGI("Palm detected, removing pointer ids %s after %" PRId64 "ms from %s",
+ dumpSet(mSuppressedPointerIds).c_str(), ns2ms(args.eventTime - args.downTime),
+ args.dump().c_str());
}
return argsWithoutUnwantedPointers;
}
-const AndroidPalmFilterDeviceInfo& PalmRejector::getPalmFilterDeviceInfo() {
+const AndroidPalmFilterDeviceInfo& PalmRejector::getPalmFilterDeviceInfo() const {
return mDeviceInfo;
}
std::string PalmRejector::dump() const {
std::string out;
out += "mDeviceInfo:\n";
- out += addLinePrefix(dumpDeviceInfo(mDeviceInfo), " ");
+ std::stringstream deviceInfo;
+ deviceInfo << mDeviceInfo << ", touch_major_res=" << mDeviceInfo.touch_major_res
+ << ", touch_minor_res=" << mDeviceInfo.touch_minor_res << "\n";
+ out += addLinePrefix(deviceInfo.str(), " ");
out += "mSlotState:\n";
out += addLinePrefix(mSlotState.dump(), " ");
out += "mSuppressedPointerIds: ";
out += dumpSet(mSuppressedPointerIds) + "\n";
+ std::stringstream state;
+ state << *mSharedPalmState;
+ out += "mSharedPalmState: " + state.str() + "\n";
+ std::stringstream filter;
+ filter << static_cast<const PalmFilterImplementation&>(*mPalmDetectionFilter);
+ out += "mPalmDetectionFilter:\n";
+ out += addLinePrefix(filter.str(), " ") + "\n";
return out;
}
diff --git a/services/inputflinger/UnwantedInteractionBlocker.h b/services/inputflinger/UnwantedInteractionBlocker.h
index a433764..5d0dde8 100644
--- a/services/inputflinger/UnwantedInteractionBlocker.h
+++ b/services/inputflinger/UnwantedInteractionBlocker.h
@@ -43,6 +43,25 @@
static constexpr int32_t ACTION_UNKNOWN = -1;
+/**
+ * Remove the data for the provided pointers from the args. The pointers are identified by their
+ * pointerId, not by the index inside the array.
+ * Return the new NotifyMotionArgs struct that has the remaining pointers.
+ * The only fields that may be different in the returned args from the provided args are:
+ * - action
+ * - pointerCount
+ * - pointerProperties
+ * - pointerCoords
+ * Action might change because it contains a pointer index. If another pointer is removed, the
+ * active pointer index would be shifted.
+ *
+ * If the active pointer id is removed (for example, for events like
+ * POINTER_UP or POINTER_DOWN), then the action is set to ACTION_UNKNOWN. It is up to the caller
+ * to set the action appropriately after the call.
+ *
+ * @param args the args from which the pointers should be removed
+ * @param pointerIds the pointer ids of the pointers that should be removed
+ */
NotifyMotionArgs removePointerIds(const NotifyMotionArgs& args,
const std::set<int32_t>& pointerIds);
@@ -101,6 +120,9 @@
std::map<int32_t /*deviceId*/, PalmRejector> mPalmRejectors GUARDED_BY(mLock);
// TODO(b/210159205): delete this when simultaneous stylus and touch is supported
void notifyMotionLocked(const NotifyMotionArgs* args) REQUIRES(mLock);
+
+ // Call this function for outbound events so that they can be logged when logging is enabled.
+ void enqueueOutboundMotionLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
};
class SlotState {
@@ -144,13 +166,21 @@
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 4f8995f..9a2aea6 100644
--- a/services/inputflinger/dispatcher/DebugConfig.h
+++ b/services/inputflinger/dispatcher/DebugConfig.h
@@ -75,11 +75,8 @@
/**
* Log debug messages about touch occlusion
- * Enable this via "adb shell setprop log.tag.InputDispatcherTouchOcclusion DEBUG" (requires
- * restart)
*/
-const bool DEBUG_TOUCH_OCCLUSION =
- __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "TouchOcclusion", ANDROID_LOG_INFO);
+constexpr bool DEBUG_TOUCH_OCCLUSION = true;
/**
* Log debug messages about the app switch latency optimization.
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 5e9427a..c2a3f7a 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -27,6 +27,7 @@
#include <ftl/enum.h>
#include <gui/SurfaceComposerClient.h>
#include <input/InputDevice.h>
+#include <openssl/mem.h>
#include <powermanager/PowerManager.h>
#include <unistd.h>
#include <utils/Trace.h>
@@ -2195,12 +2196,32 @@
// Update the temporary touch state.
BitSet32 pointerIds;
- if (isSplit) {
- uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
- pointerIds.markBit(pointerId);
- }
-
+ pointerIds.markBit(entry.pointerProperties[pointerIndex].id);
tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, pointerIds);
+
+ // If this is the pointer going down and the touched window has a wallpaper
+ // then also add the touched wallpaper windows so they are locked in for the duration
+ // of the touch gesture.
+ // We do not collect wallpapers during HOVER_MOVE or SCROLL because the wallpaper
+ // engine only supports touch events. We would need to add a mechanism similar
+ // to View.onGenericMotionEvent to enable wallpapers to handle these events.
+ if (maskedAction == AMOTION_EVENT_ACTION_DOWN ||
+ maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN) {
+ if ((targetFlags & InputTarget::FLAG_FOREGROUND) &&
+ windowHandle->getInfo()->inputConfig.test(
+ gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) {
+ sp<WindowInfoHandle> wallpaper = findWallpaperWindowBelow(windowHandle);
+ if (wallpaper != nullptr) {
+ int32_t wallpaperFlags = InputTarget::FLAG_WINDOW_IS_OBSCURED |
+ InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED |
+ InputTarget::FLAG_DISPATCH_AS_IS;
+ if (isSplit) {
+ wallpaperFlags |= InputTarget::FLAG_SPLIT;
+ }
+ tempTouchState.addOrUpdateWindow(wallpaper, wallpaperFlags, pointerIds);
+ }
+ }
+ }
}
} else {
/* Case 2: Pointer move, up, cancel or non-splittable pointer down. */
@@ -2275,10 +2296,26 @@
}
BitSet32 pointerIds;
- if (isSplit) {
- pointerIds.markBit(entry.pointerProperties[0].id);
- }
+ pointerIds.markBit(entry.pointerProperties[0].id);
tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
+
+ // Check if the wallpaper window should deliver the corresponding event.
+ slipWallpaperTouch(targetFlags, oldTouchedWindowHandle, newTouchedWindowHandle,
+ tempTouchState, pointerIds);
+ }
+ }
+
+ // Update the pointerIds for non-splittable when it received pointer down.
+ if (!isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN) {
+ // If no split, we suppose all touched windows should receive pointer down.
+ const int32_t pointerIndex = getMotionEventActionPointerIndex(action);
+ for (size_t i = 0; i < tempTouchState.windows.size(); i++) {
+ TouchedWindow& touchedWindow = tempTouchState.windows[i];
+ // Ignore drag window for it should just track one pointer.
+ if (mDragState && mDragState->dragWindow == touchedWindow.windowHandle) {
+ continue;
+ }
+ touchedWindow.pointerIds.markBit(entry.pointerProperties[pointerIndex].id);
}
}
}
@@ -2370,37 +2407,6 @@
}
}
- // If this is the first pointer going down and the touched window has a wallpaper
- // then also add the touched wallpaper windows so they are locked in for the duration
- // of the touch gesture.
- // We do not collect wallpapers during HOVER_MOVE or SCROLL because the wallpaper
- // engine only supports touch events. We would need to add a mechanism similar
- // to View.onGenericMotionEvent to enable wallpapers to handle these events.
- if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
- sp<WindowInfoHandle> foregroundWindowHandle =
- tempTouchState.getFirstForegroundWindowHandle();
- if (foregroundWindowHandle &&
- foregroundWindowHandle->getInfo()->inputConfig.test(
- WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) {
- const std::vector<sp<WindowInfoHandle>>& windowHandles =
- getWindowHandlesLocked(displayId);
- for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
- const WindowInfo* info = windowHandle->getInfo();
- if (info->displayId == displayId &&
- windowHandle->getInfo()->inputConfig.test(
- WindowInfo::InputConfig::IS_WALLPAPER)) {
- tempTouchState
- .addOrUpdateWindow(windowHandle,
- InputTarget::FLAG_WINDOW_IS_OBSCURED |
- InputTarget::
- FLAG_WINDOW_IS_PARTIALLY_OBSCURED |
- InputTarget::FLAG_DISPATCH_AS_IS,
- BitSet32(0));
- }
- }
- }
- }
-
// Success! Output targets.
injectionResult = InputEventInjectionResult::SUCCEEDED;
@@ -2453,21 +2459,17 @@
}
} else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
// One pointer went up.
- if (isSplit) {
- int32_t pointerIndex = getMotionEventActionPointerIndex(action);
- uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
+ int32_t pointerIndex = getMotionEventActionPointerIndex(action);
+ uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
- for (size_t i = 0; i < tempTouchState.windows.size();) {
- TouchedWindow& touchedWindow = tempTouchState.windows[i];
- if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) {
- touchedWindow.pointerIds.clearBit(pointerId);
- if (touchedWindow.pointerIds.isEmpty()) {
- tempTouchState.windows.erase(tempTouchState.windows.begin() + i);
- continue;
- }
- }
- i += 1;
+ for (size_t i = 0; i < tempTouchState.windows.size();) {
+ TouchedWindow& touchedWindow = tempTouchState.windows[i];
+ touchedWindow.pointerIds.clearBit(pointerId);
+ if (touchedWindow.pointerIds.isEmpty()) {
+ tempTouchState.windows.erase(tempTouchState.windows.begin() + i);
+ continue;
}
+ i += 1;
}
}
@@ -2849,6 +2851,7 @@
}
int32_t eventType = USER_ACTIVITY_EVENT_OTHER;
+ int32_t keyCode = AKEYCODE_UNKNOWN;
switch (eventEntry.type) {
case EventEntry::Type::MOTION: {
const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);
@@ -2867,6 +2870,7 @@
return;
}
eventType = USER_ACTIVITY_EVENT_BUTTON;
+ keyCode = keyEntry.keyCode;
break;
}
default: {
@@ -2876,10 +2880,10 @@
}
}
- auto command = [this, eventTime = eventEntry.eventTime, eventType, displayId]()
+ auto command = [this, eventTime = eventEntry.eventTime, eventType, displayId, keyCode]()
REQUIRES(mLock) {
scoped_unlock unlock(mLock);
- mPolicy->pokeUserActivity(eventTime, eventType, displayId);
+ mPolicy->pokeUserActivity(eventTime, eventType, displayId, keyCode);
};
postCommandLocked(std::move(command));
}
@@ -3678,7 +3682,7 @@
}
void InputDispatcher::synthesizePointerDownEventsForConnectionLocked(
- const sp<Connection>& connection) {
+ const sp<Connection>& connection, int32_t targetFlags) {
if (connection->status == Connection::Status::BROKEN) {
return;
}
@@ -3706,7 +3710,7 @@
target.globalScaleFactor = windowInfo->globalScaleFactor;
}
target.inputChannel = connection->inputChannel;
- target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+ target.flags = targetFlags;
for (std::unique_ptr<EventEntry>& downEventEntry : downEvents) {
switch (downEventEntry->type) {
@@ -3737,6 +3741,16 @@
startDispatchCycleLocked(currentTime, connection);
}
+void InputDispatcher::synthesizeCancelationEventsForWindowLocked(
+ const sp<WindowInfoHandle>& windowHandle, const CancelationOptions& options) {
+ if (windowHandle != nullptr) {
+ sp<Connection> wallpaperConnection = getConnectionLocked(windowHandle->getToken());
+ if (wallpaperConnection != nullptr) {
+ synthesizeCancelationEventsForConnectionLocked(wallpaperConnection, options);
+ }
+ }
+}
+
std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent(
const MotionEntry& originalMotionEntry, BitSet32 pointerIds) {
ALOG_ASSERT(pointerIds.value != 0);
@@ -4422,7 +4436,7 @@
if (calculatedHmac == INVALID_HMAC) {
return nullptr;
}
- if (calculatedHmac != event.getHmac()) {
+ if (0 != CRYPTO_memcmp(calculatedHmac.data(), event.getHmac().data(), calculatedHmac.size())) {
return nullptr;
}
return result;
@@ -4470,6 +4484,14 @@
if (it == mDisplayInfos.end()) return;
const auto& transformToDisplay = it->second.transform.inverse() * injectedTransform;
+ if (entry.xCursorPosition != AMOTION_EVENT_INVALID_CURSOR_POSITION &&
+ entry.yCursorPosition != AMOTION_EVENT_INVALID_CURSOR_POSITION) {
+ const vec2 cursor =
+ MotionEvent::calculateTransformedXY(entry.source, transformToDisplay,
+ {entry.xCursorPosition, entry.yCursorPosition});
+ entry.xCursorPosition = cursor.x;
+ entry.yCursorPosition = cursor.y;
+ }
for (uint32_t i = 0; i < entry.pointerCount; i++) {
entry.pointerCoords[i] =
MotionEvent::calculateTransformedCoords(entry.source, transformToDisplay,
@@ -4747,14 +4769,7 @@
touchedWindow.windowHandle->getInfo()->inputConfig.test(
gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) {
sp<WindowInfoHandle> wallpaper = state.getWallpaperWindow();
- if (wallpaper != nullptr) {
- sp<Connection> wallpaperConnection =
- getConnectionLocked(wallpaper->getToken());
- if (wallpaperConnection != nullptr) {
- synthesizeCancelationEventsForConnectionLocked(wallpaperConnection,
- options);
- }
- }
+ synthesizeCancelationEventsForWindowLocked(wallpaper, options);
}
}
state.windows.erase(state.windows.begin() + i);
@@ -5067,6 +5082,7 @@
// Erase old window.
int32_t oldTargetFlags = touchedWindow->targetFlags;
BitSet32 pointerIds = touchedWindow->pointerIds;
+ sp<WindowInfoHandle> fromWindowHandle = touchedWindow->windowHandle;
state->removeWindowByToken(fromToken);
// Add new window.
@@ -5079,14 +5095,13 @@
// Store the dragging window.
if (isDragDrop) {
- if (pointerIds.count() > 1) {
- ALOGW("The drag and drop cannot be started when there is more than 1 pointer on the"
- " window.");
+ if (pointerIds.count() != 1) {
+ ALOGW("The drag and drop cannot be started when there is no pointer or more than 1"
+ " pointer on the window.");
return false;
}
- // If the window didn't not support split or the source is mouse, the pointerIds count
- // would be 0, so we have to track the pointer 0.
- const int32_t id = pointerIds.count() == 0 ? 0 : pointerIds.firstMarkedBit();
+ // Track the pointer id for drag window and generate the drag state.
+ const int32_t id = pointerIds.firstMarkedBit();
mDragState = std::make_unique<DragState>(toWindowHandle, id);
}
@@ -5099,7 +5114,10 @@
options(CancelationOptions::CANCEL_POINTER_EVENTS,
"transferring touch focus from this window to another window");
synthesizeCancelationEventsForConnectionLocked(fromConnection, options);
- synthesizePointerDownEventsForConnectionLocked(toConnection);
+ synthesizePointerDownEventsForConnectionLocked(toConnection, newTargetFlags);
+ // Check if the wallpaper window should deliver the corresponding event.
+ transferWallpaperTouch(oldTargetFlags, newTargetFlags, fromWindowHandle, toWindowHandle,
+ *state, pointerIds);
}
if (DEBUG_FOCUS) {
@@ -6322,6 +6340,13 @@
{ // acquire lock
std::scoped_lock _l(mLock);
+
+ // Ensure that we have an entry created for all existing displays so that if a displayId has
+ // no windows, we can tell that the windows were removed from the display.
+ for (const auto& [displayId, _] : mWindowHandlesByDisplay) {
+ handlesPerDisplay[displayId];
+ }
+
mDisplayInfos.clear();
for (const auto& displayInfo : displayInfos) {
mDisplayInfos.emplace(displayInfo.displayId, displayInfo);
@@ -6377,4 +6402,97 @@
mMonitorDispatchingTimeout = timeout;
}
+void InputDispatcher::slipWallpaperTouch(int32_t targetFlags,
+ const sp<WindowInfoHandle>& oldWindowHandle,
+ const sp<WindowInfoHandle>& newWindowHandle,
+ TouchState& state, const BitSet32& pointerIds) {
+ const bool oldHasWallpaper = oldWindowHandle->getInfo()->inputConfig.test(
+ gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER);
+ const bool newHasWallpaper = (targetFlags & InputTarget::FLAG_FOREGROUND) &&
+ newWindowHandle->getInfo()->inputConfig.test(
+ gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER);
+ const sp<WindowInfoHandle> oldWallpaper =
+ oldHasWallpaper ? state.getWallpaperWindow() : nullptr;
+ const sp<WindowInfoHandle> newWallpaper =
+ newHasWallpaper ? findWallpaperWindowBelow(newWindowHandle) : nullptr;
+ if (oldWallpaper == newWallpaper) {
+ return;
+ }
+
+ if (oldWallpaper != nullptr) {
+ state.addOrUpdateWindow(oldWallpaper, InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT,
+ BitSet32(0));
+ }
+
+ if (newWallpaper != nullptr) {
+ state.addOrUpdateWindow(newWallpaper,
+ InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER |
+ InputTarget::FLAG_WINDOW_IS_OBSCURED |
+ InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED,
+ pointerIds);
+ }
+}
+
+void InputDispatcher::transferWallpaperTouch(int32_t oldTargetFlags, int32_t newTargetFlags,
+ const sp<WindowInfoHandle> fromWindowHandle,
+ const sp<WindowInfoHandle> toWindowHandle,
+ TouchState& state, const BitSet32& pointerIds) {
+ const bool oldHasWallpaper = (oldTargetFlags & InputTarget::FLAG_FOREGROUND) &&
+ fromWindowHandle->getInfo()->inputConfig.test(
+ gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER);
+ const bool newHasWallpaper = (newTargetFlags & InputTarget::FLAG_FOREGROUND) &&
+ toWindowHandle->getInfo()->inputConfig.test(
+ gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER);
+
+ const sp<WindowInfoHandle> oldWallpaper =
+ oldHasWallpaper ? state.getWallpaperWindow() : nullptr;
+ const sp<WindowInfoHandle> newWallpaper =
+ newHasWallpaper ? findWallpaperWindowBelow(toWindowHandle) : nullptr;
+ if (oldWallpaper == newWallpaper) {
+ return;
+ }
+
+ if (oldWallpaper != nullptr) {
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
+ "transferring touch focus to another window");
+ state.removeWindowByToken(oldWallpaper->getToken());
+ synthesizeCancelationEventsForWindowLocked(oldWallpaper, options);
+ }
+
+ if (newWallpaper != nullptr) {
+ int32_t wallpaperFlags =
+ oldTargetFlags & (InputTarget::FLAG_SPLIT | InputTarget::FLAG_DISPATCH_AS_IS);
+ wallpaperFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED |
+ InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+ state.addOrUpdateWindow(newWallpaper, wallpaperFlags, pointerIds);
+ sp<Connection> wallpaperConnection = getConnectionLocked(newWallpaper->getToken());
+ if (wallpaperConnection != nullptr) {
+ sp<Connection> toConnection = getConnectionLocked(toWindowHandle->getToken());
+ toConnection->inputState.mergePointerStateTo(wallpaperConnection->inputState);
+ synthesizePointerDownEventsForConnectionLocked(wallpaperConnection, wallpaperFlags);
+ }
+ }
+}
+
+sp<WindowInfoHandle> InputDispatcher::findWallpaperWindowBelow(
+ const sp<WindowInfoHandle>& windowHandle) const {
+ const std::vector<sp<WindowInfoHandle>>& windowHandles =
+ getWindowHandlesLocked(windowHandle->getInfo()->displayId);
+ bool foundWindow = false;
+ for (const sp<WindowInfoHandle>& otherHandle : windowHandles) {
+ if (!foundWindow && otherHandle != windowHandle) {
+ continue;
+ }
+ if (windowHandle == otherHandle) {
+ foundWindow = true;
+ continue;
+ }
+
+ if (otherHandle->getInfo()->inputConfig.test(WindowInfo::InputConfig::IS_WALLPAPER)) {
+ return otherHandle;
+ }
+ }
+ return nullptr;
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index ed89ed0..0df5894 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -621,8 +621,12 @@
const CancelationOptions& options)
REQUIRES(mLock);
- void synthesizePointerDownEventsForConnectionLocked(const sp<Connection>& connection)
- REQUIRES(mLock);
+ void synthesizePointerDownEventsForConnectionLocked(const sp<Connection>& connection,
+ int32_t targetFlags) REQUIRES(mLock);
+
+ void synthesizeCancelationEventsForWindowLocked(
+ const sp<android::gui::WindowInfoHandle>& windowHandle,
+ const CancelationOptions& options) REQUIRES(mLock);
// Splitting motion events across windows.
std::unique_ptr<MotionEntry> splitMotionEvent(const MotionEntry& originalMotionEntry,
@@ -684,6 +688,18 @@
bool recentWindowsAreOwnedByLocked(int32_t pid, int32_t uid) REQUIRES(mLock);
sp<InputReporterInterface> mReporter;
+
+ void slipWallpaperTouch(int32_t targetFlags,
+ const sp<android::gui::WindowInfoHandle>& oldWindowHandle,
+ const sp<android::gui::WindowInfoHandle>& newWindowHandle,
+ TouchState& state, const BitSet32& pointerIds) REQUIRES(mLock);
+ void transferWallpaperTouch(int32_t oldTargetFlags, int32_t newTargetFlags,
+ const sp<android::gui::WindowInfoHandle> fromWindowHandle,
+ const sp<android::gui::WindowInfoHandle> toWindowHandle,
+ TouchState& state, const BitSet32& pointerIds) REQUIRES(mLock);
+
+ sp<android::gui::WindowInfoHandle> findWallpaperWindowBelow(
+ const sp<android::gui::WindowInfoHandle>& windowHandle) const REQUIRES(mLock);
};
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp
index d39113b..2df97d9 100644
--- a/services/inputflinger/dispatcher/InputTarget.cpp
+++ b/services/inputflinger/dispatcher/InputTarget.cpp
@@ -43,8 +43,8 @@
}
void InputTarget::addPointers(BitSet32 newPointerIds, const ui::Transform& transform) {
- // The pointerIds can be empty, but still a valid InputTarget. This can happen for Monitors
- // and non splittable windows since we will just use all the pointers from the input event.
+ // The pointerIds can be empty, but still a valid InputTarget. This can happen when there is no
+ // valid pointer property from the input event.
if (newPointerIds.isEmpty()) {
setDefaultPointerTransform(transform);
return;
diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h
index 4c31ec3..6783022 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.h
+++ b/services/inputflinger/dispatcher/TouchedWindow.h
@@ -29,7 +29,7 @@
struct TouchedWindow {
sp<gui::WindowInfoHandle> windowHandle;
int32_t targetFlags;
- BitSet32 pointerIds; // zero unless target flag FLAG_SPLIT is set
+ BitSet32 pointerIds;
};
} // namespace inputdispatcher
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index 575b3d7..89977c9 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -123,7 +123,8 @@
uint32_t policyFlags) = 0;
/* Poke user activity for an event dispatched to a window. */
- virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType, int32_t displayId) = 0;
+ virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType, int32_t displayId,
+ int32_t keyCode) = 0;
/* Notifies the policy that a pointer down event has occurred outside the current focused
* window.
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 41ecef3..428c0a9 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -183,6 +183,9 @@
// The set of disabled input devices (disabledDevices) has changed.
CHANGE_ENABLED_STATE = 1 << 9,
+ // Volume keys rotation option changed.
+ CHANGE_VOLUME_KEYS_ROTATION = 1 << 10,
+
// All devices must be reopened.
CHANGE_MUST_REOPEN = 1 << 31,
};
@@ -287,6 +290,10 @@
// The set of currently disabled input devices.
std::set<int32_t> disabledDevices;
+ // Remap volume keys according to display rotation
+ // 0 - disabled, 1 - phone or hybrid rotation mode, 2 - tablet rotation mode
+ int volumeKeysRotationMode;
+
InputReaderConfiguration()
: virtualKeyQuietTime(0),
pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f,
@@ -307,7 +314,8 @@
pointerGestureMovementSpeedRatio(0.8f),
pointerGestureZoomSpeedRatio(0.3f),
showTouches(false),
- pointerCaptureRequest() {}
+ pointerCaptureRequest(),
+ volumeKeysRotationMode(0) {}
static std::string changesToString(uint32_t changes);
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index d6a6bd2..1b58284 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -2050,6 +2050,16 @@
device->associatedDevice = associatedDevice;
}
+ // Disable device if device config property set
+ bool deviceDisabled = false;
+ if (device->configuration &&
+ device->configuration->tryGetProperty(String8("device.disabled"), deviceDisabled)) {
+ if (deviceDisabled) {
+ device->disable();
+ ALOGV("Disabling device with id %d\n", device->id);
+ }
+ }
+
// Figure out the kinds of events the device reports.
device->readDeviceBitMask(EVIOCGBIT(EV_KEY, 0), device->keyBitmask);
device->readDeviceBitMask(EVIOCGBIT(EV_ABS, 0), device->absBitmask);
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index ba5083b..989700f 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -110,7 +110,8 @@
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 @@
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::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 9c5a129..9bcf463 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -196,9 +196,9 @@
"(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 @@
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 a693496..8065f57 100644
--- a/services/inputflinger/reader/controller/PeripheralController.cpp
+++ b/services/inputflinger/reader/controller/PeripheralController.cpp
@@ -524,4 +524,8 @@
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 b1bc8c7..ac951eb 100644
--- a/services/inputflinger/reader/controller/PeripheralController.h
+++ b/services/inputflinger/reader/controller/PeripheralController.h
@@ -31,6 +31,7 @@
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 @@
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 7688a43..306e361 100644
--- a/services/inputflinger/reader/controller/PeripheralControllerInterface.h
+++ b/services/inputflinger/reader/controller/PeripheralControllerInterface.h
@@ -33,6 +33,8 @@
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 a9a4c71..8233682 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -25,6 +25,8 @@
#include "PointerControllerInterface.h"
#include "TouchCursorInputMapperCommon.h"
+#include "input/PrintTools.h"
+
namespace android {
// The default velocity control parameters that has no effect.
@@ -76,7 +78,7 @@
void CursorInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
InputMapper::populateDeviceInfo(info);
- if (mParameters.mode == Parameters::MODE_POINTER) {
+ if (mParameters.mode == Parameters::Mode::POINTER) {
float minX, minY, maxX, maxY;
if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) {
info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, minX, maxX, 0.0f, 0.0f, 0.0f);
@@ -113,6 +115,7 @@
toString(mCursorScrollAccumulator.haveRelativeHWheel()));
dump += StringPrintf(INDENT3 "VWheelScale: %0.3f\n", mVWheelScale);
dump += StringPrintf(INDENT3 "HWheelScale: %0.3f\n", mHWheelScale);
+ dump += StringPrintf(INDENT3 "DisplayId: %s\n", toString(mDisplayId).c_str());
dump += StringPrintf(INDENT3 "Orientation: %d\n", mOrientation);
dump += StringPrintf(INDENT3 "ButtonState: 0x%08x\n", mButtonState);
dump += StringPrintf(INDENT3 "Down: %s\n", toString(isPointerDown(mButtonState)));
@@ -131,12 +134,12 @@
// Configure device mode.
switch (mParameters.mode) {
- case Parameters::MODE_POINTER_RELATIVE:
+ case Parameters::Mode::POINTER_RELATIVE:
// Should not happen during first time configuration.
ALOGE("Cannot start a device in MODE_POINTER_RELATIVE, starting in MODE_POINTER");
- mParameters.mode = Parameters::MODE_POINTER;
+ mParameters.mode = Parameters::Mode::POINTER;
[[fallthrough]];
- case Parameters::MODE_POINTER:
+ case Parameters::Mode::POINTER:
mSource = AINPUT_SOURCE_MOUSE;
mXPrecision = 1.0f;
mYPrecision = 1.0f;
@@ -144,7 +147,7 @@
mYScale = 1.0f;
mPointerController = getContext()->getPointerController(getDeviceId());
break;
- case Parameters::MODE_NAVIGATION:
+ case Parameters::Mode::NAVIGATION:
mSource = AINPUT_SOURCE_TRACKBALL;
mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
@@ -157,12 +160,13 @@
mHWheelScale = 1.0f;
}
- const bool configurePointerCapture = (!changes && config->pointerCaptureRequest.enable) ||
- (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+ const bool configurePointerCapture = mParameters.mode != Parameters::Mode::NAVIGATION &&
+ ((!changes && config->pointerCaptureRequest.enable) ||
+ (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE));
if (configurePointerCapture) {
if (config->pointerCaptureRequest.enable) {
- if (mParameters.mode == Parameters::MODE_POINTER) {
- mParameters.mode = Parameters::MODE_POINTER_RELATIVE;
+ if (mParameters.mode == Parameters::Mode::POINTER) {
+ mParameters.mode = Parameters::Mode::POINTER_RELATIVE;
mSource = AINPUT_SOURCE_MOUSE_RELATIVE;
// Keep PointerController around in order to preserve the pointer position.
mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
@@ -170,8 +174,8 @@
ALOGE("Cannot request pointer capture, device is not in MODE_POINTER");
}
} else {
- if (mParameters.mode == Parameters::MODE_POINTER_RELATIVE) {
- mParameters.mode = Parameters::MODE_POINTER;
+ if (mParameters.mode == Parameters::Mode::POINTER_RELATIVE) {
+ mParameters.mode = Parameters::Mode::POINTER;
mSource = AINPUT_SOURCE_MOUSE;
} else {
ALOGE("Cannot release pointer capture, device is not in MODE_POINTER_RELATIVE");
@@ -186,8 +190,8 @@
if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED) ||
configurePointerCapture) {
- if (config->pointerCaptureRequest.enable) {
- // Disable any acceleration or scaling when Pointer Capture is enabled.
+ if (mParameters.mode == Parameters::Mode::POINTER_RELATIVE) {
+ // Disable any acceleration or scaling for the pointer when Pointer Capture is enabled.
mPointerVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS);
mWheelXVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS);
mWheelYVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS);
@@ -198,21 +202,36 @@
}
}
- if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+ if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO) ||
+ configurePointerCapture) {
+ const bool isPointer = mParameters.mode == Parameters::Mode::POINTER;
+
+ mDisplayId = ADISPLAY_ID_NONE;
+ if (auto viewport = mDeviceContext.getAssociatedViewport(); viewport) {
+ // This InputDevice is associated with a viewport.
+ // Only generate events for the associated display.
+ const bool mismatchedPointerDisplay =
+ isPointer && (viewport->displayId != mPointerController->getDisplayId());
+ mDisplayId = mismatchedPointerDisplay ? std::nullopt
+ : std::make_optional(viewport->displayId);
+ } else if (isPointer) {
+ // The InputDevice is not associated with a viewport, but it controls the mouse pointer.
+ mDisplayId = mPointerController->getDisplayId();
+ }
+
mOrientation = DISPLAY_ORIENTATION_0;
const bool isOrientedDevice =
(mParameters.orientationAware && mParameters.hasAssociatedDisplay);
-
// InputReader works in the un-rotated display coordinate space, so we don't need to do
// anything if the device is already orientation-aware. If the device is not
// orientation-aware, then we need to apply the inverse rotation of the display so that
// when the display rotation is applied later as a part of the per-window transform, we
- // get the expected screen coordinates.
- if (!isOrientedDevice) {
- std::optional<DisplayViewport> internalViewport =
- config->getDisplayViewportByType(ViewportType::INTERNAL);
- if (internalViewport) {
- mOrientation = getInverseRotation(internalViewport->orientation);
+ // get the expected screen coordinates. When pointer capture is enabled, we do not apply any
+ // rotations and report values directly from the input device.
+ if (!isOrientedDevice && mDisplayId &&
+ mParameters.mode != Parameters::Mode::POINTER_RELATIVE) {
+ if (auto viewport = config->getDisplayViewportById(*mDisplayId); viewport) {
+ mOrientation = getInverseRotation(viewport->orientation);
}
}
@@ -221,12 +240,12 @@
}
void CursorInputMapper::configureParameters() {
- mParameters.mode = Parameters::MODE_POINTER;
+ mParameters.mode = Parameters::Mode::POINTER;
String8 cursorModeString;
if (getDeviceContext().getConfiguration().tryGetProperty(String8("cursor.mode"),
cursorModeString)) {
if (cursorModeString == "navigation") {
- mParameters.mode = Parameters::MODE_NAVIGATION;
+ mParameters.mode = Parameters::Mode::NAVIGATION;
} else if (cursorModeString != "pointer" && cursorModeString != "default") {
ALOGW("Invalid value for cursor.mode: '%s'", cursorModeString.string());
}
@@ -237,7 +256,7 @@
mParameters.orientationAware);
mParameters.hasAssociatedDisplay = false;
- if (mParameters.mode == Parameters::MODE_POINTER || mParameters.orientationAware) {
+ if (mParameters.mode == Parameters::Mode::POINTER || mParameters.orientationAware) {
mParameters.hasAssociatedDisplay = true;
}
}
@@ -246,21 +265,7 @@
dump += INDENT3 "Parameters:\n";
dump += StringPrintf(INDENT4 "HasAssociatedDisplay: %s\n",
toString(mParameters.hasAssociatedDisplay));
-
- switch (mParameters.mode) {
- case Parameters::MODE_POINTER:
- dump += INDENT4 "Mode: pointer\n";
- break;
- case Parameters::MODE_POINTER_RELATIVE:
- dump += INDENT4 "Mode: relative pointer\n";
- break;
- case Parameters::MODE_NAVIGATION:
- dump += INDENT4 "Mode: navigation\n";
- break;
- default:
- ALOG_ASSERT(false);
- }
-
+ dump += StringPrintf(INDENT4 "Mode: %s\n", ftl::enum_string(mParameters.mode).c_str());
dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware));
}
@@ -290,6 +295,11 @@
}
void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) {
+ if (!mDisplayId) {
+ // Ignore events when there is no target display configured.
+ return;
+ }
+
int32_t lastButtonState = mButtonState;
int32_t currentButtonState = mCursorButtonAccumulator.getButtonState();
mButtonState = currentButtonState;
@@ -335,7 +345,6 @@
mPointerVelocityControl.move(when, &deltaX, &deltaY);
- int32_t displayId = ADISPLAY_ID_NONE;
float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
if (mSource == AINPUT_SOURCE_MOUSE) {
@@ -359,7 +368,6 @@
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
- displayId = mPointerController->getDisplayId();
} else {
// Pointer capture and navigation modes
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX);
@@ -381,7 +389,7 @@
// Synthesize key down from buttons if needed.
synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, readTime, getDeviceId(),
- mSource, displayId, policyFlags, lastButtonState, currentButtonState);
+ mSource, *mDisplayId, policyFlags, lastButtonState, currentButtonState);
// Send motion event.
if (downChanged || moved || scrolled || buttonsChanged) {
@@ -402,7 +410,7 @@
int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit());
buttonState &= ~actionButton;
NotifyMotionArgs releaseArgs(getContext()->getNextId(), when, readTime,
- getDeviceId(), mSource, displayId, policyFlags,
+ getDeviceId(), mSource, *mDisplayId, policyFlags,
AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
metaState, buttonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
@@ -414,7 +422,7 @@
}
NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
- displayId, policyFlags, motionEventAction, 0, 0, metaState,
+ *mDisplayId, policyFlags, motionEventAction, 0, 0, metaState,
currentButtonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords,
mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime,
@@ -427,7 +435,7 @@
int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit());
buttonState |= actionButton;
NotifyMotionArgs pressArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
- mSource, displayId, policyFlags,
+ mSource, *mDisplayId, policyFlags,
AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0,
metaState, buttonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
@@ -443,7 +451,7 @@
// Send hover move after UP to tell the application that the mouse is hovering now.
if (motionEventAction == AMOTION_EVENT_ACTION_UP && (mSource == AINPUT_SOURCE_MOUSE)) {
NotifyMotionArgs hoverArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
- mSource, displayId, policyFlags,
+ mSource, *mDisplayId, policyFlags,
AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
currentButtonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
@@ -458,7 +466,7 @@
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
- mSource, displayId, policyFlags,
+ mSource, *mDisplayId, policyFlags,
AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
currentButtonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
@@ -470,7 +478,7 @@
// Synthesize key up from buttons if needed.
synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, readTime, getDeviceId(), mSource,
- displayId, policyFlags, lastButtonState, currentButtonState);
+ *mDisplayId, policyFlags, lastButtonState, currentButtonState);
mCursorMotionAccumulator.finishSync();
mCursorScrollAccumulator.finishSync();
@@ -485,16 +493,7 @@
}
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 c84c6c4..60b3dd9 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -74,10 +74,17 @@
// Immutable configuration parameters.
struct Parameters {
- enum Mode {
- MODE_POINTER,
- MODE_POINTER_RELATIVE,
- MODE_NAVIGATION,
+ enum class Mode {
+ // In POINTER mode, the device is a mouse that controls the mouse cursor on the screen,
+ // reporting absolute screen locations using SOURCE_MOUSE.
+ POINTER,
+ // A mouse device in POINTER mode switches to the POINTER_RELATIVE mode when Pointer
+ // Capture is enabled, and reports relative values only using SOURCE_MOUSE_RELATIVE.
+ POINTER_RELATIVE,
+ // A device in NAVIGATION mode emits relative values using SOURCE_TRACKBALL.
+ NAVIGATION,
+
+ ftl_last = NAVIGATION,
};
Mode mode;
@@ -104,6 +111,10 @@
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/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 2ac8178..0525b73 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -25,9 +25,10 @@
// --- Static Definitions ---
static int32_t rotateValueUsingRotationMap(int32_t value, int32_t orientation,
- const int32_t map[][4], size_t mapSize) {
+ const int32_t map[][4], size_t mapSize,
+ int32_t rotationMapOffset) {
if (orientation != DISPLAY_ORIENTATION_0) {
- for (size_t i = 0; i < mapSize; i++) {
+ for (size_t i = rotationMapOffset; i < mapSize; i++) {
if (value == map[i][0]) {
return map[i][orientation];
}
@@ -39,6 +40,16 @@
static const int32_t keyCodeRotationMap[][4] = {
// key codes enumerated counter-clockwise with the original (unrotated) key first
// no rotation, 90 degree rotation, 180 degree rotation, 270 degree rotation
+
+ // volume keys - tablet
+ {AKEYCODE_VOLUME_UP, AKEYCODE_VOLUME_UP, AKEYCODE_VOLUME_DOWN, AKEYCODE_VOLUME_DOWN},
+ {AKEYCODE_VOLUME_DOWN, AKEYCODE_VOLUME_DOWN, AKEYCODE_VOLUME_UP, AKEYCODE_VOLUME_UP},
+
+ // volume keys - phone or hybrid
+ {AKEYCODE_VOLUME_UP, AKEYCODE_VOLUME_DOWN, AKEYCODE_VOLUME_DOWN, AKEYCODE_VOLUME_UP},
+ {AKEYCODE_VOLUME_DOWN, AKEYCODE_VOLUME_UP, AKEYCODE_VOLUME_UP, AKEYCODE_VOLUME_DOWN},
+
+ // dpad keys - common
{AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT},
{AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN},
{AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT},
@@ -81,10 +92,10 @@
static const size_t stemKeyRotationMapSize =
sizeof(stemKeyRotationMap) / sizeof(stemKeyRotationMap[0]);
-static int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) {
+static int32_t rotateKeyCode(int32_t keyCode, int32_t orientation, int32_t rotationMapOffset) {
keyCode = rotateStemKey(keyCode, orientation, stemKeyRotationMap, stemKeyRotationMapSize);
return rotateValueUsingRotationMap(keyCode, orientation, keyCodeRotationMap,
- keyCodeRotationMapSize);
+ keyCodeRotationMapSize, rotationMapOffset);
}
// --- KeyboardInputMapper ---
@@ -156,6 +167,13 @@
if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
mViewport = findViewport(when, config);
}
+
+ if (!changes || (changes & InputReaderConfiguration::CHANGE_VOLUME_KEYS_ROTATION)) {
+ // mode 0 (disabled) ~ offset 4
+ // mode 1 (phone) ~ offset 2
+ // mode 2 (tablet) ~ offset 0
+ mRotationMapOffset = 4 - 2 * config->volumeKeysRotationMode;
+ }
}
static void mapStemKey(int32_t keyCode, const PropertyMap& config, char const* property) {
@@ -171,7 +189,7 @@
}
void KeyboardInputMapper::configureParameters() {
- mParameters.orientationAware = false;
+ mParameters.orientationAware = !getDeviceContext().isExternal();
const PropertyMap& config = getDeviceContext().getConfiguration();
config.tryGetProperty(String8("keyboard.orientationAware"), mParameters.orientationAware);
@@ -284,7 +302,7 @@
if (down) {
// Rotate key codes according to orientation if needed.
if (mParameters.orientationAware) {
- keyCode = rotateKeyCode(keyCode, getOrientation());
+ keyCode = rotateKeyCode(keyCode, getOrientation(), mRotationMapOffset);
}
// Add key down.
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index 3787696..533fdf9 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -57,6 +57,8 @@
uint32_t mSource;
int32_t mKeyboardType;
+ int32_t mRotationMapOffset; // determines if and how volume keys rotate
+
std::vector<KeyDown> mKeyDowns; // keys that are down
int32_t mMetaState;
nsecs_t mDownTime; // time of most recent key down
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index 41a8426..cc323ad 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -48,11 +48,8 @@
delete[] mSlots;
mSlots = new Slot[slotCount];
-}
-void MultiTouchMotionAccumulator::reset(InputDeviceContext& deviceContext) {
- // Unfortunately there is no way to read the initial contents of the slots.
- // So when we reset the accumulator, we must assume they are all zeroes.
+ mCurrentSlot = -1;
if (mUsingSlotsProtocol) {
// Query the driver for the current slot index and use it as the initial slot
// before we start reading events from the device. It is possible that the
@@ -64,24 +61,22 @@
// This can cause the touch point to "jump", but at least there will be
// no stuck touches.
int32_t initialSlot;
- status_t status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot);
- if (status) {
- ALOGD("Could not retrieve current multitouch slot index. status=%d", status);
- initialSlot = -1;
+ if (const auto status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot);
+ status == OK) {
+ mCurrentSlot = initialSlot;
+ } else {
+ ALOGD("Could not retrieve current multi-touch slot index. status=%d", status);
}
- clearSlots(initialSlot);
- } else {
- clearSlots(-1);
}
}
-void MultiTouchMotionAccumulator::clearSlots(int32_t initialSlot) {
+void MultiTouchMotionAccumulator::resetSlots() {
if (mSlots) {
for (size_t i = 0; i < mSlotCount; i++) {
mSlots[i].clear();
}
}
- mCurrentSlot = initialSlot;
+ mCurrentSlot = -1;
}
void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) {
@@ -169,7 +164,7 @@
void MultiTouchMotionAccumulator::finishSync() {
if (!mUsingSlotsProtocol) {
- clearSlots(-1);
+ resetSlots();
}
}
@@ -230,10 +225,12 @@
MultiTouchInputMapper::~MultiTouchInputMapper() {}
void MultiTouchInputMapper::reset(nsecs_t when) {
- mMultiTouchMotionAccumulator.reset(getDeviceContext());
-
- mPointerIdBits.clear();
-
+ // The evdev multi-touch protocol does not allow userspace applications to query the initial or
+ // current state of the pointers at any time. This means if we clear our accumulated state when
+ // resetting the input mapper, there's no way to rebuild the full initial state of the pointers.
+ // We can only wait for updates to all the pointers and axes. Rather than clearing the state and
+ // rebuilding the state from scratch, we work around this kernel API limitation by never
+ // fully clearing any state specific to the multi-touch protocol.
TouchInputMapper::reset(when);
}
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
index b7c3457..e7d9350 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
@@ -71,7 +71,6 @@
~MultiTouchMotionAccumulator();
void configure(InputDeviceContext& deviceContext, size_t slotCount, bool usingSlotsProtocol);
- void reset(InputDeviceContext& deviceContext);
void process(const RawEvent* rawEvent);
void finishSync();
bool hasStylus() const;
@@ -86,7 +85,7 @@
bool mUsingSlotsProtocol;
bool mHaveStylus;
- void clearSlots(int32_t initialSlot);
+ void resetSlots();
void warnIfNotInUse(const RawEvent& event, const Slot& slot);
};
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 637b1cb..5a70167 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -44,6 +44,8 @@
// --- Static Definitions ---
+static const DisplayViewport kUninitializedViewport;
+
template <typename T>
inline static void swap(T& a, T& b) {
T temp = a;
@@ -390,6 +392,10 @@
}
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::configureInputDevice(nsecs_t when, bool* outResetNeeded) {
- DeviceMode oldDeviceMode = mDeviceMode;
+ const DeviceMode oldDeviceMode = mDeviceMode;
resolveExternalStylusPresence();
@@ -906,42 +912,37 @@
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 @@
: 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 @@
// 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.
@@ -1470,6 +1468,10 @@
}
void TouchInputMapper::sync(nsecs_t when, nsecs_t readTime) {
+ if (mDeviceMode == DeviceMode::DISABLED) {
+ // Only save the last pending state when the device is disabled.
+ mRawStatesPending.clear();
+ }
// Push a new state.
mRawStatesPending.emplace_back();
@@ -1926,6 +1928,10 @@
}
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();
@@ -3515,6 +3521,8 @@
void TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
bool down, bool hovering) {
+ LOG_ALWAYS_FATAL_IF(mDeviceMode != DeviceMode::POINTER,
+ "%s cannot be used when the device is not in POINTER mode.", __func__);
int32_t metaState = getContext()->getGlobalMetaState();
if (down || hovering) {
@@ -3637,6 +3645,10 @@
if (down || hovering) {
mPointerSimple.lastCoords.copyFrom(mPointerSimple.currentCoords);
mPointerSimple.lastProperties.copyFrom(mPointerSimple.currentProperties);
+ mPointerSimple.displayId = displayId;
+ mPointerSimple.source = mSource;
+ mPointerSimple.lastCursorX = xCursorPosition;
+ mPointerSimple.lastCursorY = yCursorPosition;
} else {
mPointerSimple.reset();
}
@@ -3646,7 +3658,23 @@
mPointerSimple.currentCoords.clear();
mPointerSimple.currentProperties.clear();
- dispatchPointerSimple(when, readTime, policyFlags, false, false);
+ if (mPointerSimple.down || mPointerSimple.hovering) {
+ int32_t metaState = getContext()->getGlobalMetaState();
+ NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(),
+ mPointerSimple.source, mPointerSimple.displayId, policyFlags,
+ AMOTION_EVENT_ACTION_CANCEL, 0, AMOTION_EVENT_FLAG_CANCELED,
+ metaState, mLastRawState.buttonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
+ &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
+ mPointerSimple.lastCursorX, mPointerSimple.lastCursorY,
+ mPointerSimple.downTime,
+ /* videoFrames */ {});
+ getListener().notifyMotion(&args);
+ if (mPointerController != nullptr) {
+ mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
+ }
+ }
+ mPointerSimple.reset();
}
void TouchInputMapper::dispatchMotion(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index c948f56..2937bf8 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -327,6 +327,8 @@
int32_t rawVScroll;
int32_t rawHScroll;
+ explicit inline RawState() { clear(); }
+
void copyFrom(const RawState& other) {
when = other.when;
readTime = other.readTime;
@@ -712,6 +714,12 @@
// Time the pointer last went down.
nsecs_t downTime;
+ // Values reported for the last pointer event.
+ uint32_t source;
+ int32_t displayId;
+ float lastCursorX;
+ float lastCursorY;
+
void reset() {
currentCoords.clear();
currentProperties.clear();
@@ -720,6 +728,10 @@
down = false;
hovering = false;
downTime = 0;
+ source = 0;
+ displayId = ADISPLAY_ID_NONE;
+ lastCursorX = 0.f;
+ lastCursorY = 0.f;
}
} mPointerSimple;
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 76a7c19..75cd9da 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -60,6 +60,7 @@
},
static_libs: [
"libc++fs",
+ "libgmock",
],
require_root: true,
test_suites: ["device-tests"],
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index df43071..ca0ef2a 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -58,6 +58,8 @@
AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
static constexpr int32_t POINTER_2_DOWN =
AMOTION_EVENT_ACTION_POINTER_DOWN | (2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+static constexpr int32_t POINTER_0_UP =
+ AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
static constexpr int32_t POINTER_1_UP =
AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
@@ -73,6 +75,9 @@
static constexpr std::chrono::duration STALE_EVENT_TIMEOUT = 1000ms;
+static constexpr int expectedWallpaperFlags =
+ AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+
struct PointF {
float x;
float y;
@@ -1670,8 +1675,6 @@
sp<FakeWindowHandle> wallpaperWindow =
new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
wallpaperWindow->setIsWallpaper(true);
- constexpr int expectedWallpaperFlags =
- AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {foregroundWindow, wallpaperWindow}}});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -1714,8 +1717,6 @@
sp<FakeWindowHandle> wallpaperWindow =
new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
wallpaperWindow->setIsWallpaper(true);
- constexpr int expectedWallpaperFlags =
- AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {foregroundWindow, wallpaperWindow}}});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -1745,24 +1746,27 @@
foregroundWindow->consumeMotionCancel();
}
+class ShouldSplitTouchFixture : public InputDispatcherTest,
+ public ::testing::WithParamInterface<bool> {};
+INSTANTIATE_TEST_SUITE_P(InputDispatcherTest, ShouldSplitTouchFixture,
+ ::testing::Values(true, false));
/**
* A single window that receives touch (on top), and a wallpaper window underneath it.
* The top window gets a multitouch gesture.
* Ensure that wallpaper gets the same gesture.
*/
-TEST_F(InputDispatcherTest, WallpaperWindow_ReceivesMultiTouch) {
+TEST_P(ShouldSplitTouchFixture, WallpaperWindowReceivesMultiTouch) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window =
- new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT);
- window->setDupTouchToWallpaper(true);
+ sp<FakeWindowHandle> foregroundWindow =
+ new FakeWindowHandle(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT);
+ foregroundWindow->setDupTouchToWallpaper(true);
+ foregroundWindow->setPreventSplitting(GetParam());
sp<FakeWindowHandle> wallpaperWindow =
new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
wallpaperWindow->setIsWallpaper(true);
- constexpr int expectedWallpaperFlags =
- AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, wallpaperWindow}}});
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {foregroundWindow, wallpaperWindow}}});
// Touch down on top window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -1771,7 +1775,7 @@
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
// Both top window and its wallpaper should receive the touch down
- window->consumeMotionDown();
+ foregroundWindow->consumeMotionDown();
wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
// Second finger down on the top window
@@ -1790,11 +1794,34 @@
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- window->consumeMotionPointerDown(1 /* pointerIndex */);
+ foregroundWindow->consumeMotionPointerDown(1 /* pointerIndex */);
wallpaperWindow->consumeMotionPointerDown(1 /* pointerIndex */, ADISPLAY_ID_DEFAULT,
expectedWallpaperFlags);
- window->assertNoEvents();
- wallpaperWindow->assertNoEvents();
+
+ const MotionEvent secondFingerUpEvent =
+ MotionEventBuilder(POINTER_0_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .displayId(ADISPLAY_ID_DEFAULT)
+ .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+ .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER)
+ .x(100)
+ .y(100))
+ .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER)
+ .x(150)
+ .y(150))
+ .build();
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, secondFingerUpEvent, INJECT_EVENT_TIMEOUT,
+ InputEventInjectionSync::WAIT_FOR_RESULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ foregroundWindow->consumeMotionPointerUp(0);
+ wallpaperWindow->consumeMotionPointerUp(0, ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {100, 100}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ foregroundWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+ wallpaperWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
}
/**
@@ -1821,8 +1848,6 @@
new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
wallpaperWindow->setFrame(Rect(0, 0, 400, 200));
wallpaperWindow->setIsWallpaper(true);
- constexpr int expectedWallpaperFlags =
- AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
mDispatcher->setInputWindows(
{{ADISPLAY_ID_DEFAULT, {leftWindow, rightWindow, wallpaperWindow}}});
@@ -1888,6 +1913,51 @@
}
/**
+ * Two windows: a window on the left with dup touch to wallpaper and window on the right without it.
+ * The touch slips to the right window. so left window and wallpaper should receive ACTION_CANCEL
+ * The right window should receive ACTION_DOWN.
+ */
+TEST_F(InputDispatcherTest, WallpaperWindowWhenSlippery) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> leftWindow =
+ new FakeWindowHandle(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
+ leftWindow->setFrame(Rect(0, 0, 200, 200));
+ leftWindow->setDupTouchToWallpaper(true);
+ leftWindow->setSlippery(true);
+
+ sp<FakeWindowHandle> rightWindow =
+ new FakeWindowHandle(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+ rightWindow->setFrame(Rect(200, 0, 400, 200));
+
+ sp<FakeWindowHandle> wallpaperWindow =
+ new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
+ wallpaperWindow->setIsWallpaper(true);
+
+ mDispatcher->setInputWindows(
+ {{ADISPLAY_ID_DEFAULT, {leftWindow, rightWindow, wallpaperWindow}}});
+
+ // Touch down on left window
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {100, 100}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ // Both foreground window and its wallpaper should receive the touch down
+ leftWindow->consumeMotionDown();
+ wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+ // Move to right window, the left window should receive cancel.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {201, 100}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ leftWindow->consumeMotionCancel();
+ rightWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ wallpaperWindow->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+}
+
+/**
* On the display, have a single window, and also an area where there's no window.
* First pointer touches the "no window" area of the screen. Second pointer touches the window.
* Make sure that the window receives the second pointer, and first pointer is simply ignored.
@@ -2293,6 +2363,72 @@
thirdWindow->consumeMotionDown();
}
+TEST_F(InputDispatcherTest, OnWindowInfosChanged_RemoveAllWindowsOnDisplay) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+ window->setFocusable(true);
+
+ mDispatcher->onWindowInfosChanged({*window->getInfo()}, {});
+ setFocusedWindow(window);
+
+ window->consumeFocusEvent(true);
+
+ NotifyKeyArgs keyDown = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+ NotifyKeyArgs keyUp = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyKey(&keyDown);
+ mDispatcher->notifyKey(&keyUp);
+
+ window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+ window->consumeKeyUp(ADISPLAY_ID_DEFAULT);
+
+ // All windows are removed from the display. Ensure that we can no longer dispatch to it.
+ mDispatcher->onWindowInfosChanged({}, {});
+
+ window->consumeFocusEvent(false);
+
+ mDispatcher->notifyKey(&keyDown);
+ mDispatcher->notifyKey(&keyUp);
+ window->assertNoEvents();
+}
+
+TEST_F(InputDispatcherTest, NonSplitTouchableWindowReceivesMultiTouch) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
+ "Fake Window", ADISPLAY_ID_DEFAULT);
+ // Ensure window is non-split and have some transform.
+ window->setPreventSplitting(true);
+ window->setWindowOffset(20, 40);
+ mDispatcher->onWindowInfosChanged({*window->getInfo()}, {});
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+ const MotionEvent secondFingerDownEvent =
+ MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .displayId(ADISPLAY_ID_DEFAULT)
+ .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+ .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50))
+ .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER)
+ .x(-30)
+ .y(-50))
+ .build();
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ InputEventInjectionSync::WAIT_FOR_RESULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ const MotionEvent* event = window->consumeMotion();
+ EXPECT_EQ(POINTER_1_DOWN, event->getAction());
+ EXPECT_EQ(70, event->getX(0)); // 50 + 20
+ EXPECT_EQ(90, event->getY(0)); // 50 + 40
+ EXPECT_EQ(-10, event->getX(1)); // -30 + 20
+ EXPECT_EQ(-10, event->getY(1)); // -50 + 40
+}
+
/**
* Ensure the correct coordinate spaces are used by InputDispatcher.
*
@@ -2443,20 +2579,26 @@
// Create a couple of windows
sp<FakeWindowHandle> firstWindow =
new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT);
+ firstWindow->setDupTouchToWallpaper(true);
+
sp<FakeWindowHandle> secondWindow =
new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT);
-
+ sp<FakeWindowHandle> wallpaper =
+ new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
+ wallpaper->setIsWallpaper(true);
// Add the windows to the dispatcher
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow, wallpaper}}});
// Send down to the first window
NotifyMotionArgs downMotionArgs =
generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT);
mDispatcher->notifyMotion(&downMotionArgs);
+
// Only the first window should get the down event
firstWindow->consumeMotionDown();
secondWindow->assertNoEvents();
+ wallpaper->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
// Transfer touch to the second window
TransferFunction f = GetParam();
@@ -2465,6 +2607,7 @@
// The first window gets cancel and the second gets down
firstWindow->consumeMotionCancel();
secondWindow->consumeMotionDown();
+ wallpaper->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
// Send up event to the second window
NotifyMotionArgs upMotionArgs =
@@ -2474,6 +2617,7 @@
// The first window gets no events and the second gets up
firstWindow->assertNoEvents();
secondWindow->consumeMotionUp();
+ wallpaper->assertNoEvents();
}
/**
@@ -2595,6 +2739,65 @@
secondWindow->consumeMotionUp();
}
+TEST_P(TransferTouchFixture, TransferTouch_MultipleWallpapers) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+ // Create a couple of windows
+ sp<FakeWindowHandle> firstWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "First Window",
+ ADISPLAY_ID_DEFAULT);
+ firstWindow->setDupTouchToWallpaper(true);
+ sp<FakeWindowHandle> secondWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Second Window",
+ ADISPLAY_ID_DEFAULT);
+ secondWindow->setDupTouchToWallpaper(true);
+
+ sp<FakeWindowHandle> wallpaper1 =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper1", ADISPLAY_ID_DEFAULT);
+ wallpaper1->setIsWallpaper(true);
+
+ sp<FakeWindowHandle> wallpaper2 =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper2", ADISPLAY_ID_DEFAULT);
+ wallpaper2->setIsWallpaper(true);
+ // Add the windows to the dispatcher
+ mDispatcher->setInputWindows(
+ {{ADISPLAY_ID_DEFAULT, {firstWindow, wallpaper1, secondWindow, wallpaper2}}});
+
+ // Send down to the first window
+ NotifyMotionArgs downMotionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyMotion(&downMotionArgs);
+
+ // Only the first window should get the down event
+ firstWindow->consumeMotionDown();
+ secondWindow->assertNoEvents();
+ wallpaper1->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+ wallpaper2->assertNoEvents();
+
+ // Transfer touch focus to the second window
+ TransferFunction f = GetParam();
+ bool success = f(mDispatcher, firstWindow->getToken(), secondWindow->getToken());
+ ASSERT_TRUE(success);
+
+ // The first window gets cancel and the second gets down
+ firstWindow->consumeMotionCancel();
+ secondWindow->consumeMotionDown();
+ wallpaper1->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+ wallpaper2->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+ // Send up event to the second window
+ NotifyMotionArgs upMotionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyMotion(&upMotionArgs);
+ // The first window gets no events and the second gets up
+ firstWindow->assertNoEvents();
+ secondWindow->consumeMotionUp();
+ wallpaper1->assertNoEvents();
+ wallpaper2->consumeMotionUp(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+}
+
// For the cases of single pointer touch and two pointers non-split touch, the api's
// 'transferTouch' and 'transferTouchFocus' are equivalent in behaviour. They only differ
// for the case where there are multiple pointers split across several windows.
@@ -6096,6 +6299,8 @@
sp<FakeWindowHandle> mWindow;
sp<FakeWindowHandle> mSecondWindow;
sp<FakeWindowHandle> mDragWindow;
+ // Mouse would force no-split, set the id as non-zero to verify if drag state could track it.
+ static constexpr int32_t MOUSE_POINTER_ID = 1;
void SetUp() override {
InputDispatcherTest::SetUp();
@@ -6110,11 +6315,41 @@
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mSecondWindow}}});
}
- void injectDown() {
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {50, 50}))
- << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ void injectDown(int fromSource = AINPUT_SOURCE_TOUCHSCREEN) {
+ switch (fromSource) {
+ case AINPUT_SOURCE_TOUCHSCREEN:
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ break;
+ case AINPUT_SOURCE_STYLUS:
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(
+ mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
+ AINPUT_SOURCE_STYLUS)
+ .buttonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_STYLUS)
+ .x(50)
+ .y(50))
+ .build()));
+ break;
+ case AINPUT_SOURCE_MOUSE:
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(
+ mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(MOUSE_POINTER_ID,
+ AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(50)
+ .y(50))
+ .build()));
+ break;
+ default:
+ FAIL() << "Source " << fromSource << " doesn't support drag and drop";
+ }
// Window should receive motion event.
mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -6123,9 +6358,9 @@
// Start performing drag, we will create a drag window and transfer touch to it.
// @param sendDown : if true, send a motion down on first window before perform drag and drop.
// Returns true on success.
- bool performDrag(bool sendDown = true) {
+ bool startDrag(bool sendDown = true, int fromSource = AINPUT_SOURCE_TOUCHSCREEN) {
if (sendDown) {
- injectDown();
+ injectDown(fromSource);
}
// The drag window covers the entire display
@@ -6143,36 +6378,10 @@
}
return transferred;
}
-
- // Start performing drag, we will create a drag window and transfer touch to it.
- void performStylusDrag() {
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
- MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_STYLUS)
- .buttonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)
- .pointer(PointerBuilder(0,
- AMOTION_EVENT_TOOL_TYPE_STYLUS)
- .x(50)
- .y(50))
- .build()));
- mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
-
- // The drag window covers the entire display
- mDragWindow = new FakeWindowHandle(mApp, mDispatcher, "DragWindow", ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows(
- {{ADISPLAY_ID_DEFAULT, {mDragWindow, mWindow, mSecondWindow}}});
-
- // Transfer touch focus to the drag window
- mDispatcher->transferTouchFocus(mWindow->getToken(), mDragWindow->getToken(),
- true /* isDragDrop */);
- mWindow->consumeMotionCancel();
- mDragWindow->consumeMotionDown();
- }
};
TEST_F(InputDispatcherDragTests, DragEnterAndDragExit) {
- performDrag();
+ startDrag();
// Move on window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -6210,7 +6419,7 @@
}
TEST_F(InputDispatcherDragTests, DragAndDrop) {
- performDrag();
+ startDrag();
// Move on window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -6242,7 +6451,7 @@
}
TEST_F(InputDispatcherDragTests, StylusDragAndDrop) {
- performStylusDrag();
+ startDrag(true, AINPUT_SOURCE_STYLUS);
// Move on window and keep button pressed.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -6289,7 +6498,7 @@
}
TEST_F(InputDispatcherDragTests, DragAndDropOnInvalidWindow) {
- performDrag();
+ startDrag();
// Set second window invisible.
mSecondWindow->setVisible(false);
@@ -6325,6 +6534,9 @@
}
TEST_F(InputDispatcherDragTests, NoDragAndDropWhenMultiFingers) {
+ // Ensure window could track pointerIds if it didn't support split touch.
+ mWindow->setPreventSplitting(true);
+
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{50, 50}))
@@ -6345,7 +6557,7 @@
mWindow->consumeMotionPointerDown(1 /* pointerIndex */);
// Should not perform drag and drop when window has multi fingers.
- ASSERT_FALSE(performDrag(false));
+ ASSERT_FALSE(startDrag(false));
}
TEST_F(InputDispatcherDragTests, DragAndDropWhenSplitTouch) {
@@ -6373,7 +6585,7 @@
mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
// Perform drag and drop from first window.
- ASSERT_TRUE(performDrag(false));
+ ASSERT_TRUE(startDrag(false));
// Move on window.
const MotionEvent secondFingerMoveEvent =
@@ -6408,7 +6620,7 @@
}
TEST_F(InputDispatcherDragTests, DragAndDropWhenMultiDisplays) {
- performDrag();
+ startDrag();
// Update window of second display.
sp<FakeWindowHandle> windowInSecondary =
@@ -6459,6 +6671,55 @@
mSecondWindow->assertNoEvents();
}
+TEST_F(InputDispatcherDragTests, MouseDragAndDrop) {
+ startDrag(true, AINPUT_SOURCE_MOUSE);
+ // Move on window.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(MOUSE_POINTER_ID,
+ AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(50)
+ .y(50))
+ .build()))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+ mWindow->consumeDragEvent(false, 50, 50);
+ mSecondWindow->assertNoEvents();
+
+ // Move to another window.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(MOUSE_POINTER_ID,
+ AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(150)
+ .y(50))
+ .build()))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+ mWindow->consumeDragEvent(true, 150, 50);
+ mSecondWindow->consumeDragEvent(false, 50, 50);
+
+ // drop to another window.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE)
+ .buttonState(0)
+ .pointer(PointerBuilder(MOUSE_POINTER_ID,
+ AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(150)
+ .y(50))
+ .build()))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+ mFakePolicy->assertDropTargetEquals(mSecondWindow->getToken());
+ mWindow->assertNoEvents();
+ mSecondWindow->assertNoEvents();
+}
+
class InputDispatcherDropInputFeatureTest : public InputDispatcherTest {};
TEST_F(InputDispatcherDropInputFeatureTest, WindowDropsInput) {
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index a26a0bc..8a97901 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -56,7 +56,9 @@
// 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,31 @@
// 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();
+}
+
+MATCHER_P(WithToolType, toolType, "InputEvent with specified tool type") {
+ const auto argToolType = arg.pointerProperties[0].toolType;
+ *result_listener << "expected tool type " << motionToolTypeToString(toolType) << ", but got "
+ << motionToolTypeToString(argToolType);
+ return argToolType == toolType;
+}
+
template<typename T>
static inline T min(T a, T b) {
return a < b ? a : b;
@@ -2137,6 +2164,8 @@
~FakePeripheralController() override {}
+ int32_t getEventHubId() const { return getDeviceContext().getEventHubId(); }
+
void populateDeviceInfo(InputDeviceInfo* deviceInfo) override {}
void dump(std::string& dump) override {}
@@ -2170,6 +2199,7 @@
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 +2902,6 @@
// 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 +2932,6 @@
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 +2941,21 @@
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 +2981,8 @@
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 +3007,8 @@
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 +3032,8 @@
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 +3059,7 @@
event.code = code;
event.value = value;
mapper.process(&event);
+ // Loop the reader to flush the input listener queue.
mReader->loopOnce();
}
@@ -4181,10 +4231,14 @@
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 +4515,7 @@
}
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 +4534,13 @@
}
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 +4551,7 @@
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 +4562,7 @@
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 +4573,7 @@
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 +4967,6 @@
ASSERT_TRUE(mReader->getContext()->getGeneration() != generation);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
- ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
ASSERT_EQ(DEVICE_ID, resetArgs.deviceId);
process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
@@ -4969,33 +5028,118 @@
ASSERT_EQ(20, args.pointerCoords[0].getY());
}
-TEST_F(CursorInputMapperTest, Process_ShouldHandleDisplayId) {
+TEST_F(CursorInputMapperTest, PointerCaptureDisablesOrientationChanges) {
+ addConfigurationProperty("cursor.mode", "pointer");
CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
- // Setup for second display.
- constexpr int32_t SECOND_DISPLAY_ID = 1;
- const std::string SECOND_DISPLAY_UNIQUE_ID = "local:1";
- mFakePolicy->addDisplayViewport(SECOND_DISPLAY_ID, 800, 480, DISPLAY_ORIENTATION_0,
- true /*isActive*/, SECOND_DISPLAY_UNIQUE_ID, NO_PORT,
- ViewportType::EXTERNAL);
- mFakePolicy->setDefaultPointerDisplayId(SECOND_DISPLAY_ID);
- configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ NotifyDeviceResetArgs resetArgs;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+ ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
+ ASSERT_EQ(DEVICE_ID, resetArgs.deviceId);
- mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
- mFakePointerController->setPosition(100, 200);
- mFakePointerController->setButtonState(0);
+ // Ensure the display is rotated.
+ prepareDisplay(DISPLAY_ORIENTATION_90);
NotifyMotionArgs args;
+
+ // Verify that the coordinates are rotated.
process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source);
ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
- 110.0f, 220.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+ ASSERT_EQ(-20, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X));
+ ASSERT_EQ(10, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y));
+
+ // Enable Pointer Capture.
+ mFakePolicy->setPointerCapture(true);
+ configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+ NotifyPointerCaptureChangedArgs captureArgs;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyCaptureWasCalled(&captureArgs));
+ ASSERT_TRUE(captureArgs.request.enable);
+
+ // Move and verify rotation is not applied.
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+ ASSERT_EQ(10, args.pointerCoords[0].getX());
+ ASSERT_EQ(20, args.pointerCoords[0].getY());
+}
+
+TEST_F(CursorInputMapperTest, ConfigureDisplayId_NoAssociatedViewport) {
+ CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+
+ // Set up the default display.
+ prepareDisplay(DISPLAY_ORIENTATION_90);
+
+ // Set up the secondary display as the display on which the pointer should be shown.
+ // The InputDevice is not associated with any display.
+ prepareSecondaryDisplay();
+ mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
+ configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+
+ mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
+ mFakePointerController->setPosition(100, 200);
+ mFakePointerController->setButtonState(0);
+
+ // Ensure input events are generated for the secondary display.
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE),
+ WithDisplayId(SECONDARY_DISPLAY_ID), WithCoords(110.0f, 220.0f))));
ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 110.0f, 220.0f));
- 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 ---
@@ -6648,6 +6792,171 @@
toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
}
+TEST_F(SingleTouchInputMapperTest, Reset_RecreatesTouchState) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareButtons();
+ prepareAxes(POSITION | PRESSURE);
+ SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+ NotifyMotionArgs motionArgs;
+
+ // Set the initial state for the touch pointer.
+ mFakeEventHub->setAbsoluteAxisValue(EVENTHUB_ID, ABS_X, 100);
+ mFakeEventHub->setAbsoluteAxisValue(EVENTHUB_ID, ABS_Y, 200);
+ mFakeEventHub->setAbsoluteAxisValue(EVENTHUB_ID, ABS_PRESSURE, RAW_PRESSURE_MAX);
+ mFakeEventHub->setScanCodeState(EVENTHUB_ID, BTN_TOUCH, 1);
+
+ // Reset the mapper. When the mapper is reset, we expect it to attempt to recreate the touch
+ // state by reading the current axis values.
+ mapper.reset(ARBITRARY_TIME);
+
+ // Send a sync to simulate an empty touch frame where nothing changes. The mapper should use
+ // the recreated touch state to generate a down event.
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
+TEST_F(SingleTouchInputMapperTest, WhenViewportActiveStatusChanged_PointerGestureIsReset) {
+ std::shared_ptr<FakePointerController> fakePointerController =
+ std::make_shared<FakePointerController>();
+ fakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
+ fakePointerController->setPosition(100, 200);
+ fakePointerController->setButtonState(0);
+ mFakePolicy->setPointerController(fakePointerController);
+
+ addConfigurationProperty("touch.deviceType", "pointer");
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareButtons();
+ mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOOL_PEN, 0, AKEYCODE_UNKNOWN, 0);
+ prepareAxes(POSITION);
+ SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
+
+ // Start a stylus gesture.
+ processKey(mapper, BTN_TOOL_PEN, 1);
+ processDown(mapper, 100, 200);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithAction(AMOTION_EVENT_ACTION_DOWN),
+ WithSource(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+ // TODO(b/257078296): Pointer mode generates extra event.
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithAction(AMOTION_EVENT_ACTION_MOVE),
+ WithSource(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+ // Make the viewport inactive. This will put the device in disabled mode, and the ongoing stylus
+ // gesture should be disabled.
+ auto viewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
+ viewport->isActive = false;
+ mFakePolicy->updateViewport(*viewport);
+ configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithAction(AMOTION_EVENT_ACTION_CANCEL),
+ WithSource(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+ // TODO(b/257078296): Pointer mode generates extra event.
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithAction(AMOTION_EVENT_ACTION_CANCEL),
+ WithSource(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
+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);
+ processUp(mapper);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+ // Start a new gesture while the viewport is still inactive.
+ processDown(mapper, 300, 400);
+ mFakeEventHub->setAbsoluteAxisValue(EVENTHUB_ID, ABS_X, 300);
+ mFakeEventHub->setAbsoluteAxisValue(EVENTHUB_ID, ABS_Y, 400);
+ mFakeEventHub->setScanCodeState(EVENTHUB_ID, BTN_TOUCH, 1);
+ processSync(mapper);
+
+ // 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());
+
+ // In the next sync, the touch state that was recreated when the device was reset is reported.
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+
+ // No more events.
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasNotCalled());
+}
+
// --- TouchDisplayProjectionTest ---
class TouchDisplayProjectionTest : public SingleTouchInputMapperTest {
@@ -8505,27 +8814,27 @@
ASSERT_TRUE(mFakePolicy->updateViewport(displayViewport));
configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
- // Finger move
+ // The ongoing touch should be canceled immediately
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ EXPECT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionArgs.action);
+
+ // Finger move is ignored
x += 10, y += 10;
processPosition(mapper, x, y);
processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- EXPECT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionArgs.action);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
// Reactivate display viewport
displayViewport.isActive = true;
ASSERT_TRUE(mFakePolicy->updateViewport(displayViewport));
configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
- // Finger move again
+ // Finger move again starts new gesture
x += 10, y += 10;
processPosition(mapper, x, y);
processSync(mapper);
-
- // Gesture is aborted, so events after display is activated won't be dispatched until there is
- // no pointer on the touch device.
- mFakeListener->assertNotifyMotionWasNotCalled();
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
}
TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) {
@@ -8683,6 +8992,10 @@
// 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));
}
}
@@ -9161,6 +9474,80 @@
ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
}
+TEST_F(MultiTouchInputMapperTest, Reset_PreservesLastTouchState) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareAxes(POSITION | ID | SLOT | PRESSURE);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+ NotifyMotionArgs motionArgs;
+
+ // First finger down.
+ processId(mapper, FIRST_TRACKING_ID);
+ processPosition(mapper, 100, 200);
+ processPressure(mapper, RAW_PRESSURE_MAX);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+
+ // Second finger down.
+ processSlot(mapper, SECOND_SLOT);
+ processId(mapper, SECOND_TRACKING_ID);
+ processPosition(mapper, 300, 400);
+ processPressure(mapper, RAW_PRESSURE_MAX);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(
+ mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(ACTION_POINTER_1_DOWN, motionArgs.action);
+
+ // Reset the mapper. When the mapper is reset, we expect the current multi-touch state to be
+ // preserved. Resetting should not generate any events.
+ mapper.reset(ARBITRARY_TIME);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+ // Send a sync to simulate an empty touch frame where nothing changes. The mapper should use
+ // the existing touch state to generate a down event.
+ processPosition(mapper, 301, 302);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(ACTION_POINTER_1_DOWN, motionArgs.action);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
+TEST_F(MultiTouchInputMapperTest, Reset_PreservesLastTouchState_NoPointersDown) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareAxes(POSITION | ID | SLOT | PRESSURE);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+ NotifyMotionArgs motionArgs;
+
+ // First finger touches down and releases.
+ processId(mapper, FIRST_TRACKING_ID);
+ processPosition(mapper, 100, 200);
+ processPressure(mapper, RAW_PRESSURE_MAX);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+ processId(mapper, INVALID_TRACKING_ID);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(
+ mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+
+ // Reset the mapper. When the mapper is reset, we expect it to restore the latest
+ // raw state where no pointers are down.
+ mapper.reset(ARBITRARY_TIME);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+ // Send an empty sync frame. Since there are no pointers, no events are generated.
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
// --- MultiTouchInputMapperTest_ExternalDevice ---
class MultiTouchInputMapperTest_ExternalDevice : public MultiTouchInputMapperTest {
diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp
index 6a26c63..57b382c 100644
--- a/services/inputflinger/tests/TestInputListener.cpp
+++ b/services/inputflinger/tests/TestInputListener.cpp
@@ -69,6 +69,13 @@
"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 626cdfc..0bdfc6b 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 @@
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 0062f42..29fa001 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 Y_RESOLUTION = 11;
constexpr int32_t MAJOR_RESOLUTION = 1;
+const nsecs_t RESAMPLE_PERIOD = ::ui::kResamplePeriod.InNanoseconds();
+
constexpr int POINTER_0_DOWN =
AMOTION_EVENT_ACTION_POINTER_DOWN | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
constexpr int POINTER_1_DOWN =
@@ -47,6 +53,27 @@
constexpr int UP = AMOTION_EVENT_ACTION_UP;
constexpr int CANCEL = AMOTION_EVENT_ACTION_CANCEL;
+constexpr int32_t FLAG_CANCELED = AMOTION_EVENT_FLAG_CANCELED;
+
+MATCHER_P(WithAction, action, "MotionEvent with specified action") {
+ bool result = true;
+ if (action == CANCEL) {
+ result &= (arg.flags & FLAG_CANCELED) != 0;
+ }
+ result &= arg.action == action;
+ *result_listener << "expected to receive " << MotionEvent::actionToString(action)
+ << " but received " << MotionEvent::actionToString(arg.action) << " instead.";
+ return result;
+}
+
+MATCHER_P(WithFlags, flags, "MotionEvent with specified flags") {
+ return arg.flags == flags;
+}
+
+static nsecs_t toNs(std::chrono::nanoseconds duration) {
+ return duration.count();
+}
+
struct PointerData {
float x;
float y;
@@ -252,7 +279,7 @@
/*newSuppressedPointerIds*/ {1});
ASSERT_EQ(2u, result.size());
assertArgs(result[0], POINTER_1_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}, {2, {7, 8, 9}}});
- ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags);
+ ASSERT_EQ(FLAG_CANCELED, result[0].flags);
assertArgs(result[1], MOVE, {{0, {1, 2, 3}}, {2, {7, 8, 9}}});
}
@@ -267,7 +294,7 @@
/*newSuppressedPointerIds*/ {0});
ASSERT_EQ(1u, result.size());
assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}});
- ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags);
+ ASSERT_EQ(FLAG_CANCELED, result[0].flags);
}
/**
@@ -282,7 +309,7 @@
/*newSuppressedPointerIds*/ {1});
ASSERT_EQ(1u, result.size());
assertArgs(result[0], POINTER_1_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}, {2, {7, 8, 9}}});
- ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags);
+ ASSERT_EQ(FLAG_CANCELED, result[0].flags);
}
/**
@@ -297,7 +324,7 @@
/*newSuppressedPointerIds*/ {0});
ASSERT_EQ(1u, result.size());
assertArgs(result[0], POINTER_0_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}});
- ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags);
+ ASSERT_EQ(FLAG_CANCELED, result[0].flags);
}
/**
@@ -312,7 +339,7 @@
/*newSuppressedPointerIds*/ {0, 1});
ASSERT_EQ(1u, result.size());
assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}, {1, {4, 5, 6}}});
- ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags);
+ ASSERT_EQ(FLAG_CANCELED, result[0].flags);
}
/**
@@ -328,7 +355,7 @@
/*newSuppressedPointerIds*/ {0, 1});
ASSERT_EQ(1u, result.size());
assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}});
- ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags);
+ ASSERT_EQ(FLAG_CANCELED, result[0].flags);
}
/**
@@ -384,7 +411,7 @@
expected.reported_tool_type = ::ui::EventPointerType::kTouch;
expected.stylus_button = false;
- ASSERT_EQ(expected, touches[0]) << toString(touches[0]);
+ ASSERT_EQ(expected, touches[0]) << touches[0];
}
// --- UnwantedInteractionBlockerTest ---
@@ -569,6 +596,114 @@
dumpThread.join();
}
+/**
+ * Heuristic filter that's present in the palm rejection model blocks touches early if the size
+ * of the touch is large. This is an integration test that checks that this filter kicks in.
+ */
+TEST_F(UnwantedInteractionBlockerTest, HeuristicFilterWorks) {
+ mBlocker->notifyInputDevicesChanged({generateTestDeviceInfo()});
+ // Small touch down
+ NotifyMotionArgs args1 = generateMotionArgs(0 /*downTime*/, 0 /*eventTime*/, DOWN, {{1, 2, 3}});
+ mBlocker->notifyMotion(&args1);
+ mTestListener.assertNotifyMotionWasCalled(WithAction(DOWN));
+
+ // Large touch oval on the next move
+ NotifyMotionArgs args2 =
+ generateMotionArgs(0 /*downTime*/, RESAMPLE_PERIOD, MOVE, {{4, 5, 200}});
+ mBlocker->notifyMotion(&args2);
+ mTestListener.assertNotifyMotionWasCalled(WithAction(MOVE));
+
+ // Lift up the touch to force the model to decide on whether it's a palm
+ NotifyMotionArgs args3 =
+ generateMotionArgs(0 /*downTime*/, 2 * RESAMPLE_PERIOD, UP, {{4, 5, 200}});
+ mBlocker->notifyMotion(&args3);
+ mTestListener.assertNotifyMotionWasCalled(WithAction(CANCEL));
+}
+
+/**
+ * Send a stylus event that would have triggered the heuristic palm detector if it were a touch
+ * event. However, since it's a stylus event, it should propagate without being canceled through
+ * the blocker.
+ * This is similar to `HeuristicFilterWorks` test, but for stylus tool.
+ */
+TEST_F(UnwantedInteractionBlockerTest, StylusIsNotBlocked) {
+ InputDeviceInfo info = generateTestDeviceInfo();
+ info.addSource(AINPUT_SOURCE_STYLUS);
+ mBlocker->notifyInputDevicesChanged({info});
+ NotifyMotionArgs args1 = generateMotionArgs(0 /*downTime*/, 0 /*eventTime*/, DOWN, {{1, 2, 3}});
+ args1.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+ mBlocker->notifyMotion(&args1);
+ mTestListener.assertNotifyMotionWasCalled(WithAction(DOWN));
+
+ // Move the stylus, setting large TOUCH_MAJOR/TOUCH_MINOR dimensions
+ NotifyMotionArgs args2 =
+ generateMotionArgs(0 /*downTime*/, RESAMPLE_PERIOD, MOVE, {{4, 5, 200}});
+ args2.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+ mBlocker->notifyMotion(&args2);
+ mTestListener.assertNotifyMotionWasCalled(WithAction(MOVE));
+
+ // Lift up the stylus. If it were a touch event, this would force the model to decide on whether
+ // it's a palm.
+ NotifyMotionArgs args3 =
+ generateMotionArgs(0 /*downTime*/, 2 * RESAMPLE_PERIOD, UP, {{4, 5, 200}});
+ args3.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+ mBlocker->notifyMotion(&args3);
+ mTestListener.assertNotifyMotionWasCalled(WithAction(UP));
+}
+
+/**
+ * Send a mixed touch and stylus event.
+ * The touch event goes first, and is a palm. The stylus event goes down after.
+ * Stylus event should continue to work even after touch is detected as a palm.
+ */
+TEST_F(UnwantedInteractionBlockerTest, TouchIsBlockedWhenMixedWithStylus) {
+ InputDeviceInfo info = generateTestDeviceInfo();
+ info.addSource(AINPUT_SOURCE_STYLUS);
+ mBlocker->notifyInputDevicesChanged({info});
+
+ // Touch down
+ NotifyMotionArgs args1 = generateMotionArgs(0 /*downTime*/, 0 /*eventTime*/, DOWN, {{1, 2, 3}});
+ mBlocker->notifyMotion(&args1);
+ mTestListener.assertNotifyMotionWasCalled(WithAction(DOWN));
+
+ // Stylus pointer down
+ NotifyMotionArgs args2 = generateMotionArgs(0 /*downTime*/, RESAMPLE_PERIOD, POINTER_1_DOWN,
+ {{1, 2, 3}, {10, 20, 30}});
+ args2.pointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+ mBlocker->notifyMotion(&args2);
+ mTestListener.assertNotifyMotionWasCalled(WithAction(POINTER_1_DOWN));
+
+ // Large touch oval on the next finger move
+ NotifyMotionArgs args3 = generateMotionArgs(0 /*downTime*/, 2 * RESAMPLE_PERIOD, MOVE,
+ {{1, 2, 300}, {11, 21, 30}});
+ args3.pointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+ mBlocker->notifyMotion(&args3);
+ mTestListener.assertNotifyMotionWasCalled(WithAction(MOVE));
+
+ // Lift up the finger pointer. It should be canceled due to the heuristic filter.
+ NotifyMotionArgs args4 = generateMotionArgs(0 /*downTime*/, 3 * RESAMPLE_PERIOD, POINTER_0_UP,
+ {{1, 2, 300}, {11, 21, 30}});
+ args4.pointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+ mBlocker->notifyMotion(&args4);
+ mTestListener.assertNotifyMotionWasCalled(
+ AllOf(WithAction(POINTER_0_UP), WithFlags(FLAG_CANCELED)));
+
+ NotifyMotionArgs args5 =
+ generateMotionArgs(0 /*downTime*/, 4 * RESAMPLE_PERIOD, MOVE, {{12, 22, 30}});
+ args5.pointerProperties[0].id = args4.pointerProperties[1].id;
+ args5.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+ mBlocker->notifyMotion(&args5);
+ mTestListener.assertNotifyMotionWasCalled(WithAction(MOVE));
+
+ // Lift up the stylus pointer
+ NotifyMotionArgs args6 =
+ generateMotionArgs(0 /*downTime*/, 5 * RESAMPLE_PERIOD, UP, {{4, 5, 200}});
+ args6.pointerProperties[0].id = args4.pointerProperties[1].id;
+ args6.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+ mBlocker->notifyMotion(&args6);
+ mTestListener.assertNotifyMotionWasCalled(WithAction(UP));
+}
+
using UnwantedInteractionBlockerTestDeathTest = UnwantedInteractionBlockerTest;
/**
@@ -630,138 +765,138 @@
*/
TEST_F(PalmRejectorTest, TwoPointersAreCanceled) {
std::vector<NotifyMotionArgs> argsList;
- constexpr nsecs_t downTime = 255955749837000;
+ const nsecs_t downTime = toNs(0ms);
mPalmRejector->processMotion(
generateMotionArgs(downTime, downTime, DOWN, {{1342.0, 613.0, 79.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955759313000, MOVE, {{1406.0, 650.0, 52.0}}));
+ generateMotionArgs(downTime, toNs(8ms), MOVE, {{1406.0, 650.0, 52.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955766361000, MOVE, {{1429.0, 672.0, 46.0}}));
+ generateMotionArgs(downTime, toNs(16ms), MOVE, {{1429.0, 672.0, 46.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955775989000, MOVE, {{1417.0, 685.0, 41.0}}));
+ generateMotionArgs(downTime, toNs(24ms), MOVE, {{1417.0, 685.0, 41.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955775989000, POINTER_1_DOWN,
+ generateMotionArgs(downTime, toNs(32ms), POINTER_1_DOWN,
{{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955783039000, MOVE,
+ generateMotionArgs(downTime, toNs(40ms), MOVE,
{{1414.0, 702.0, 41.0}, {1059.0, 731.0, 12.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955792536000, MOVE,
+ generateMotionArgs(downTime, toNs(48ms), MOVE,
{{1415.0, 719.0, 44.0}, {1060.0, 760.0, 11.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955799474000, MOVE,
+ generateMotionArgs(downTime, toNs(56ms), MOVE,
{{1421.0, 733.0, 42.0}, {1065.0, 769.0, 13.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955809177000, MOVE,
+ generateMotionArgs(downTime, toNs(64ms), MOVE,
{{1426.0, 742.0, 43.0}, {1068.0, 771.0, 13.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955816131000, MOVE,
+ generateMotionArgs(downTime, toNs(72ms), MOVE,
{{1430.0, 748.0, 45.0}, {1069.0, 772.0, 13.0}}));
argsList = mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955825907000, MOVE,
+ generateMotionArgs(downTime, toNs(80ms), MOVE,
{{1432.0, 750.0, 44.0}, {1069.0, 772.0, 13.0}}));
ASSERT_EQ(1u, argsList.size());
ASSERT_EQ(0 /* No FLAG_CANCELED */, argsList[0].flags);
argsList = mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955832736000, MOVE,
+ generateMotionArgs(downTime, toNs(88ms), MOVE,
{{1433.0, 751.0, 44.0}, {1070.0, 771.0, 13.0}}));
ASSERT_EQ(2u, argsList.size());
ASSERT_EQ(POINTER_0_UP, argsList[0].action);
- ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags);
+ ASSERT_EQ(FLAG_CANCELED, argsList[0].flags);
ASSERT_EQ(MOVE, argsList[1].action);
ASSERT_EQ(1u, argsList[1].pointerCount);
ASSERT_EQ(0, argsList[1].flags);
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955842432000, MOVE,
+ generateMotionArgs(downTime, toNs(96ms), MOVE,
{{1433.0, 751.0, 42.0}, {1071.0, 770.0, 13.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955849380000, MOVE,
+ generateMotionArgs(downTime, toNs(104ms), MOVE,
{{1433.0, 751.0, 45.0}, {1072.0, 769.0, 13.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955859046000, MOVE,
+ generateMotionArgs(downTime, toNs(112ms), MOVE,
{{1433.0, 751.0, 43.0}, {1072.0, 768.0, 13.0}}));
argsList = mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955869823000, MOVE,
+ generateMotionArgs(downTime, toNs(120ms), MOVE,
{{1433.0, 751.0, 45.0}, {1072.0, 767.0, 13.0}}));
ASSERT_EQ(1u, argsList.size());
ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, argsList[0].action);
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955875641000, MOVE,
+ generateMotionArgs(downTime, toNs(128ms), MOVE,
{{1433.0, 751.0, 43.0}, {1072.0, 766.0, 13.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955882693000, MOVE,
+ generateMotionArgs(downTime, toNs(136ms), MOVE,
{{1433.0, 750.0, 44.0}, {1072.0, 765.0, 13.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955892324000, MOVE,
+ generateMotionArgs(downTime, toNs(144ms), MOVE,
{{1433.0, 750.0, 42.0}, {1072.0, 763.0, 14.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955899425000, MOVE,
+ generateMotionArgs(downTime, toNs(152ms), MOVE,
{{1434.0, 750.0, 44.0}, {1073.0, 761.0, 14.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955909400000, MOVE,
+ generateMotionArgs(downTime, toNs(160ms), MOVE,
{{1435.0, 750.0, 43.0}, {1073.0, 759.0, 15.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955915885000, MOVE,
+ generateMotionArgs(downTime, toNs(168ms), MOVE,
{{1436.0, 750.0, 45.0}, {1074.0, 757.0, 15.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955925607000, MOVE,
+ generateMotionArgs(downTime, toNs(176ms), MOVE,
{{1436.0, 750.0, 44.0}, {1074.0, 755.0, 15.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955932580000, MOVE,
+ generateMotionArgs(downTime, toNs(184ms), MOVE,
{{1436.0, 750.0, 45.0}, {1074.0, 753.0, 15.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955942231000, MOVE,
+ generateMotionArgs(downTime, toNs(192ms), MOVE,
{{1436.0, 749.0, 44.0}, {1074.0, 751.0, 15.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955949204000, MOVE,
+ generateMotionArgs(downTime, toNs(200ms), MOVE,
{{1435.0, 748.0, 45.0}, {1074.0, 749.0, 15.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955959103000, MOVE,
+ generateMotionArgs(downTime, toNs(208ms), MOVE,
{{1434.0, 746.0, 44.0}, {1074.0, 747.0, 14.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955965884000, MOVE,
+ generateMotionArgs(downTime, toNs(216ms), MOVE,
{{1433.0, 744.0, 44.0}, {1075.0, 745.0, 14.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955975649000, MOVE,
+ generateMotionArgs(downTime, toNs(224ms), MOVE,
{{1431.0, 741.0, 43.0}, {1075.0, 742.0, 13.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955982537000, MOVE,
+ generateMotionArgs(downTime, toNs(232ms), MOVE,
{{1428.0, 738.0, 43.0}, {1076.0, 739.0, 12.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955992284000, MOVE,
+ generateMotionArgs(downTime, toNs(240ms), MOVE,
{{1400.0, 726.0, 54.0}, {1076.0, 739.0, 13.0}}));
argsList = mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955999348000, POINTER_1_UP,
+ generateMotionArgs(downTime, toNs(248ms), POINTER_1_UP,
{{1362.0, 716.0, 55.0}, {1076.0, 739.0, 13.0}}));
ASSERT_TRUE(argsList.empty());
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255955999348000, MOVE, {{1362.0, 716.0, 55.0}}));
+ generateMotionArgs(downTime, toNs(256ms), MOVE, {{1362.0, 716.0, 55.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255956008885000, MOVE, {{1347.0, 707.0, 54.0}}));
+ generateMotionArgs(downTime, toNs(264ms), MOVE, {{1347.0, 707.0, 54.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255956015791000, MOVE, {{1340.0, 698.0, 54.0}}));
+ generateMotionArgs(downTime, toNs(272ms), MOVE, {{1340.0, 698.0, 54.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255956025804000, MOVE, {{1338.0, 694.0, 55.0}}));
+ generateMotionArgs(downTime, toNs(280ms), MOVE, {{1338.0, 694.0, 55.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255956032314000, MOVE, {{1336.0, 690.0, 53.0}}));
+ generateMotionArgs(downTime, toNs(288ms), MOVE, {{1336.0, 690.0, 53.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255956042329000, MOVE, {{1334.0, 685.0, 47.0}}));
+ generateMotionArgs(downTime, toNs(296ms), MOVE, {{1334.0, 685.0, 47.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255956048979000, MOVE, {{1333.0, 679.0, 46.0}}));
+ generateMotionArgs(downTime, toNs(304ms), MOVE, {{1333.0, 679.0, 46.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255956058813000, MOVE, {{1332.0, 672.0, 45.0}}));
+ generateMotionArgs(downTime, toNs(312ms), MOVE, {{1332.0, 672.0, 45.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255956065592000, MOVE, {{1333.0, 666.0, 40.0}}));
+ generateMotionArgs(downTime, toNs(320ms), MOVE, {{1333.0, 666.0, 40.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255956075276000, MOVE, {{1336.0, 661.0, 24.0}}));
+ generateMotionArgs(downTime, toNs(328ms), MOVE, {{1336.0, 661.0, 24.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255956082198000, MOVE, {{1338.0, 656.0, 16.0}}));
+ generateMotionArgs(downTime, toNs(336ms), MOVE, {{1338.0, 656.0, 16.0}}));
mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255956092059000, MOVE, {{1341.0, 649.0, 1.0}}));
+ generateMotionArgs(downTime, toNs(344ms), MOVE, {{1341.0, 649.0, 1.0}}));
argsList = mPalmRejector->processMotion(
- generateMotionArgs(downTime, 255956098764000, UP, {{1341.0, 649.0, 1.0}}));
+ generateMotionArgs(downTime, toNs(352ms), UP, {{1341.0, 649.0, 1.0}}));
ASSERT_TRUE(argsList.empty());
}
@@ -845,7 +980,7 @@
ASSERT_EQ(2u, argsList.size());
// First event - cancel pointer 1
ASSERT_EQ(POINTER_1_UP, argsList[0].action);
- ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags);
+ ASSERT_EQ(FLAG_CANCELED, argsList[0].flags);
// Second event - send MOVE for the remaining pointer
ASSERT_EQ(MOVE, argsList[1].action);
ASSERT_EQ(0, argsList[1].flags);
@@ -886,7 +1021,7 @@
// Cancel all
ASSERT_EQ(CANCEL, argsList[0].action);
ASSERT_EQ(2u, argsList[0].pointerCount);
- ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags);
+ ASSERT_EQ(FLAG_CANCELED, argsList[0].flags);
// Future move events are ignored
argsList = mPalmRejector->processMotion(
@@ -932,7 +1067,7 @@
{{1414.0, 702.0, 41.0}, {1059.0, 731.0, 12.0}}));
ASSERT_EQ(1u, argsList.size());
ASSERT_EQ(CANCEL, argsList[0].action) << MotionEvent::actionToString(argsList[0].action);
- ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags);
+ ASSERT_EQ(FLAG_CANCELED, argsList[0].flags);
// Future move events should not go to the listener.
argsList = mPalmRejector->processMotion(
@@ -966,7 +1101,7 @@
{{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/powermanager/WorkSource.cpp b/services/powermanager/WorkSource.cpp
index 1006a06..64a5499 100644
--- a/services/powermanager/WorkSource.cpp
+++ b/services/powermanager/WorkSource.cpp
@@ -28,9 +28,16 @@
return BAD_VALUE;
}
int32_t num;
+ int32_t workChainCount;
status_t ret = parcel->readInt32(&num)
?: parcel->readInt32Vector(&mUids)
- ?: parcel->readString16Vector(&mNames);
+ ?: parcel->readString16Vector(&mNames)
+ ?: parcel->readInt32(&workChainCount);
+
+ if (ret == OK && workChainCount > 0) {
+ // We don't yet support WorkChains in native WorkSources.
+ return BAD_VALUE;
+ }
return ret;
}
@@ -43,7 +50,8 @@
return parcel->writeInt32(mUids.size())
?: parcel->writeInt32Vector(mUids)
- ?: parcel->writeString16Vector(mNames);
+ ?: parcel->writeString16Vector(mNames)
+ ?: parcel->writeInt32(-1);
}
} // namespace android::os
diff --git a/services/powermanager/tests/Android.bp b/services/powermanager/tests/Android.bp
index 2d1558a..962784c 100644
--- a/services/powermanager/tests/Android.bp
+++ b/services/powermanager/tests/Android.bp
@@ -31,6 +31,7 @@
"PowerHalWrapperAidlTest.cpp",
"PowerHalWrapperHidlV1_0Test.cpp",
"PowerHalWrapperHidlV1_1Test.cpp",
+ "WorkSourceTest.cpp",
],
cflags: [
"-Wall",
diff --git a/services/powermanager/tests/WorkSourceTest.cpp b/services/powermanager/tests/WorkSourceTest.cpp
new file mode 100644
index 0000000..bb9164a
--- /dev/null
+++ b/services/powermanager/tests/WorkSourceTest.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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 <optional>
+#define LOG_TAG "PowerHalLoaderTest"
+
+#include <android-base/logging.h>
+#include <android/WorkSource.h>
+#include <binder/Parcel.h>
+#include <gtest/gtest.h>
+
+#include <future>
+
+using namespace android;
+using namespace testing;
+
+TEST(WorkSourceTest, Parcel) {
+ std::vector<int32_t> uids = {1, 2};
+ using Names = std::vector<std::optional<String16>>;
+ std::optional<Names> names = std::make_optional<Names>({std::make_optional(String16("name"))});
+ os::WorkSource ws{uids, names};
+
+ Parcel p;
+ ws.writeToParcel(&p);
+ p.setDataPosition(0);
+
+ os::WorkSource otherWs;
+ otherWs.readFromParcel(&p);
+
+ EXPECT_EQ(ws, otherWs);
+ EXPECT_EQ(uids, otherWs.getUids());
+ EXPECT_EQ(names, otherWs.getNames());
+}
diff --git a/services/sensorservice/AidlSensorHalWrapper.cpp b/services/sensorservice/AidlSensorHalWrapper.cpp
index 4d1de96..f67c610 100644
--- a/services/sensorservice/AidlSensorHalWrapper.cpp
+++ b/services/sensorservice/AidlSensorHalWrapper.cpp
@@ -726,7 +726,7 @@
.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 948692b..e0a4f03 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -1328,6 +1328,7 @@
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 @@
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 @@
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 @@
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 @@
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 234dc9c..4ba3c51 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -377,6 +377,7 @@
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/Android.bp b/services/surfaceflinger/Android.bp
index 000a2cb..7548380 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -26,6 +26,7 @@
defaults: [
"surfaceflinger_defaults",
"skia_renderengine_deps",
+ "surfaceflinger_qcom_ext_defaults",
],
cflags: [
"-DLOG_TAG=\"SurfaceFlinger\"",
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 11a9e19..abc3478 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -59,7 +59,10 @@
cc_library {
name: "libcompositionengine",
- defaults: ["libcompositionengine_defaults"],
+ defaults: [
+ "libcompositionengine_defaults",
+ "surfaceflinger_udfps_lib_defaults",
+ ],
srcs: [
"src/planner/CachedSet.cpp",
"src/planner/Flattener.cpp",
@@ -81,6 +84,7 @@
"src/OutputLayer.cpp",
"src/OutputLayerCompositionState.cpp",
"src/RenderSurface.cpp",
+ "src/UdfpsExtension.cpp",
],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
@@ -109,6 +113,14 @@
export_include_dirs: ["include"],
}
+cc_library_static {
+ name: "surfaceflinger_udfps_lib",
+ srcs: [
+ "src/UdfpsExtension.cpp",
+ ],
+ export_include_dirs: ["include"],
+}
+
cc_test {
name: "libcompositionengine_test",
test_suites: ["device-tests"],
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
index 16cb41b..5e84be1 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
@@ -56,6 +56,9 @@
// similar requests if needed.
virtual void createClientCompositionCache(uint32_t cacheSize) = 0;
+ // Sends the brightness setting to HWC
+ virtual void applyDisplayBrightness(const bool applyImmediately) = 0;
+
protected:
~Display() = default;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index db2fd1b..2203639 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -27,6 +27,7 @@
#include <compositionengine/LayerFE.h>
#include <renderengine/LayerSettings.h>
#include <ui/Fence.h>
+#include <ui/FenceTime.h>
#include <ui/GraphicTypes.h>
#include <ui/LayerStack.h>
#include <ui/Region.h>
@@ -311,6 +312,8 @@
const Region& flashRegion,
std::vector<LayerFE::LayerSettings>& clientCompositionLayers) = 0;
virtual void setExpensiveRenderingExpected(bool enabled) = 0;
+ virtual void setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) = 0;
+ virtual bool isPowerHintSessionEnabled() = 0;
virtual void cacheClientCompositionRequests(uint32_t cacheSize) = 0;
virtual bool canPredictCompositionStrategy(const CompositionRefreshArgs&) = 0;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/UdfpsExtension.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/UdfpsExtension.h
new file mode 100644
index 0000000..4306cb4
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/UdfpsExtension.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2021-2022 The LineageOS 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 <stdint.h>
+
+#ifndef __UDFPS_EXTENSION__H__
+#define __UDFPS_EXTENSION__H__
+
+#define UDFPS_BIOMETRIC_PROMPT_LAYER_NAME "BiometricPrompt"
+#define UDFPS_LAYER_NAME "UdfpsControllerOverlay"
+#define UDFPS_TOUCHED_LAYER_NAME "SurfaceView[UdfpsControllerOverlay](BLAST)"
+
+extern uint32_t getUdfpsZOrder(uint32_t z, bool touched);
+extern uint64_t getUdfpsUsageBits(uint64_t usageBits, bool touched);
+
+#endif /* __UDFPS_EXTENSION__H__ */
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index 61a0e6a..33a10a3 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -72,6 +72,7 @@
const compositionengine::DisplayColorProfileCreationArgs&) override;
void createRenderSurface(const compositionengine::RenderSurfaceCreationArgs&) override;
void createClientCompositionCache(uint32_t cacheSize) override;
+ void applyDisplayBrightness(const bool applyImmediately) override;
// Internal helpers used by chooseCompositionStrategy()
using ChangedTypes = android::HWComposer::DeviceRequestedChanges::ChangedTypes;
@@ -89,6 +90,8 @@
std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(const sp<LayerFE>&) const;
private:
+ bool isPowerHintSessionEnabled() override;
+ void setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) override;
DisplayId mId;
bool mIsDisconnected = false;
Hwc2::PowerAdvisor* mPowerAdvisor = nullptr;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 31c51e6..428c19f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -140,6 +140,8 @@
std::vector<LayerFE*> &outLayerFEs) override;
void appendRegionFlashRequests(const Region&, std::vector<LayerFE::LayerSettings>&) override;
void setExpensiveRenderingExpected(bool enabled) override;
+ void setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) override;
+ bool isPowerHintSessionEnabled() override;
void dumpBase(std::string&) const;
// Implemented by the final implementation for the final state it uses.
@@ -150,6 +152,8 @@
virtual const compositionengine::CompositionEngine& getCompositionEngine() const = 0;
virtual void dumpState(std::string& out) const = 0;
+ bool mustRecompose() const;
+
private:
void dirtyEntireOutput();
compositionengine::OutputLayer* findLayerRequestingBackgroundComposition() const;
@@ -168,6 +172,9 @@
std::unique_ptr<ClientCompositionRequestCache> mClientCompositionRequestCache;
std::unique_ptr<planner::Planner> mPlanner;
std::unique_ptr<HwcAsyncWorker> mHwComposerAsyncWorker;
+
+ // Whether the content must be recomposed this frame.
+ bool mMustRecompose = false;
};
// This template factory function standardizes the implementation details of the
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
index e65aa73..8e4e9af 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 @@
// 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 92cc484..f934cb2 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 @@
// 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 b7ebca6..c968df7 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 @@
// 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 72e6f3b..7e99ec2 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
@@ -41,6 +41,7 @@
MOCK_METHOD1(createDisplayColorProfile, void(const DisplayColorProfileCreationArgs&));
MOCK_METHOD1(createRenderSurface, void(const RenderSurfaceCreationArgs&));
MOCK_METHOD1(createClientCompositionCache, void(uint32_t));
+ MOCK_METHOD1(applyDisplayBrightness, void(const bool));
MOCK_METHOD1(setPredictCompositionStrategy, void(bool));
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index cb9fbad..2a04949 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -133,6 +133,8 @@
MOCK_METHOD1(canPredictCompositionStrategy, bool(const CompositionRefreshArgs&));
MOCK_METHOD1(setPredictCompositionStrategy, void(bool));
MOCK_METHOD1(setTreat170mAsSrgb, void(bool));
+ MOCK_METHOD(void, setHintSessionGpuFence, (std::unique_ptr<FenceTime> && gpuFence));
+ MOCK_METHOD(bool, isPowerHintSessionEnabled, ());
};
} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index b79b46b..163d9a3 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -203,6 +203,24 @@
setReleasedLayers(std::move(releasedLayers));
}
+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 = applyImmediately})
+ .get();
+ ALOGE_IF(result != NO_ERROR, "setDisplayBrightness failed for %s: %d, (%s)",
+ getName().c_str(), result, strerror(-result));
+ }
+ // Clear out the display brightness now that it's been communicated to composer.
+ editState().displayBrightness.reset();
+}
+
void Display::beginFrame() {
Output::beginFrame();
@@ -212,20 +230,7 @@
return;
}
- auto& hwc = getCompositionEngine().getHwComposer();
- 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})
- .get();
- ALOGE_IF(result != NO_ERROR, "setDisplayBrightness failed for %s: %d, (%s)",
- getName().c_str(), result, strerror(-result));
- }
- // Clear out the display brightness now that it's been communicated to composer.
- editState().displayBrightness.reset();
+ applyDisplayBrightness(false);
}
bool Display::chooseCompositionStrategy(
@@ -243,11 +248,14 @@
return false;
}
+ const nsecs_t startTime = systemTime();
+
// Get any composition changes requested by the HWC device, and apply them.
std::optional<android::HWComposer::DeviceRequestedChanges> changes;
auto& hwc = getCompositionEngine().getHwComposer();
+ const bool requiresClientComposition = anyLayersRequireClientComposition();
if (status_t result =
- hwc.getDeviceCompositionChanges(*halDisplayId, anyLayersRequireClientComposition(),
+ hwc.getDeviceCompositionChanges(*halDisplayId, requiresClientComposition,
getState().earliestPresentTime,
getState().previousPresentFence,
getState().expectedPresentTime, outChanges);
@@ -257,6 +265,11 @@
return false;
}
+ if (isPowerHintSessionEnabled()) {
+ mPowerAdvisor->setHwcValidateTiming(mId, startTime, systemTime());
+ mPowerAdvisor->setRequiresClientComposition(mId, requiresClientComposition);
+ }
+
return true;
}
@@ -356,9 +369,24 @@
}
auto& hwc = getCompositionEngine().getHwComposer();
+
+ const nsecs_t startTime = systemTime();
+
+ if (isPowerHintSessionEnabled()) {
+ if (!getCompositionEngine().getHwComposer().getComposer()->isSupported(
+ Hwc2::Composer::OptionalFeature::ExpectedPresentTime) &&
+ getState().previousPresentFence->getSignalTime() != Fence::SIGNAL_TIME_PENDING) {
+ mPowerAdvisor->setHwcPresentDelayedTime(mId, getState().earliestPresentTime);
+ }
+ }
+
hwc.presentAndGetReleaseFences(*halDisplayIdOpt, getState().earliestPresentTime,
getState().previousPresentFence);
+ if (isPowerHintSessionEnabled()) {
+ mPowerAdvisor->setHwcPresentTiming(mId, startTime, systemTime());
+ }
+
fences.presentFence = hwc.getPresentFence(*halDisplayIdOpt);
// TODO(b/121291683): Change HWComposer call to return entire map
@@ -384,18 +412,33 @@
}
}
+bool Display::isPowerHintSessionEnabled() {
+ return mPowerAdvisor != nullptr && mPowerAdvisor->usePowerHintSession();
+}
+
+void Display::setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) {
+ mPowerAdvisor->setGpuFenceTime(mId, std::move(gpuFence));
+}
+
void Display::finishFrame(const compositionengine::CompositionRefreshArgs& refreshArgs,
GpuCompositionResult&& result) {
// We only need to actually compose the display if:
// 1) It is being handled by hardware composer, which may need this to
// keep its virtual display state machine in sync, or
// 2) There is work to be done (the dirty region isn't empty)
- if (GpuVirtualDisplayId::tryCast(mId) && getDirtyRegion().isEmpty()) {
+ if (GpuVirtualDisplayId::tryCast(mId) && !mustRecompose()) {
ALOGV("Skipping display composition");
return;
}
impl::Output::finishFrame(refreshArgs, std::move(result));
+
+ if (isPowerHintSessionEnabled()) {
+ auto& hwc = getCompositionEngine().getHwComposer();
+ if (auto halDisplayId = HalDisplayId::tryCast(mId)) {
+ mPowerAdvisor->setSkippedValidate(mId, hwc.getValidateSkipped(*halDisplayId));
+ }
+ }
}
} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index c3385a8..1598fd1 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -22,6 +22,7 @@
#include <compositionengine/LayerFE.h>
#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/RenderSurface.h>
+#include <compositionengine/UdfpsExtension.h>
#include <compositionengine/impl/HwcAsyncWorker.h>
#include <compositionengine/impl/Output.h>
#include <compositionengine/impl/OutputCompositionState.h>
@@ -586,8 +587,29 @@
// Remove the transparent area from the visible region
if (!layerFEState->isOpaque) {
if (tr.preserveRects()) {
- // transform the transparent region
- transparentRegion = tr.transform(layerFEState->transparentRegionHint);
+ // Clip the transparent region to geomLayerBounds first
+ // The transparent region may be influenced by applications, for
+ // instance, by overriding ViewGroup#gatherTransparentRegion with a
+ // custom view. Once the layer stack -> display mapping is known, we
+ // must guard against very wrong inputs to prevent underflow or
+ // overflow errors. We do this here by constraining the transparent
+ // region to be within the pre-transform layer bounds, since the
+ // layer bounds are expected to play nicely with the full
+ // transform.
+ const Region clippedTransparentRegionHint =
+ layerFEState->transparentRegionHint.intersect(
+ Rect(layerFEState->geomLayerBounds));
+
+ if (clippedTransparentRegionHint.isEmpty()) {
+ if (!layerFEState->transparentRegionHint.isEmpty()) {
+ ALOGD("Layer: %s had an out of bounds transparent region",
+ layerFE->getDebugName());
+ layerFEState->transparentRegionHint.dump("transparentRegionHint");
+ }
+ transparentRegion.clear();
+ } else {
+ transparentRegion = tr.transform(clippedTransparentRegionHint);
+ }
} else {
// transformation too complex, can't do the
// transparent region optimization.
@@ -814,7 +836,10 @@
compositionengine::OutputLayer* Output::findLayerRequestingBackgroundComposition() const {
compositionengine::OutputLayer* layerRequestingBgComposition = nullptr;
- for (auto* layer : getOutputLayersOrderedByZ()) {
+ for (size_t i = 0; i < getOutputLayerCount(); i++) {
+ compositionengine::OutputLayer* layer = getOutputLayerOrderedByZByIndex(i);
+ compositionengine::OutputLayer* nextLayer = getOutputLayerOrderedByZByIndex(i + 1);
+
auto* compState = layer->getLayerFE().getCompositionState();
// If any layer has a sideband stream, we will disable blurs. In that case, we don't
@@ -828,6 +853,16 @@
if (compState->backgroundBlurRadius > 0 || compState->blurRegions.size() > 0) {
layerRequestingBgComposition = layer;
}
+
+ // If the next layer is the Udfps touched layer, enable client composition for it
+ // because that somehow leads to the Udfps touched layer getting device composition
+ // consistently.
+ if ((nextLayer != nullptr && layerRequestingBgComposition == nullptr) &&
+ (strncmp(nextLayer->getLayerFE().getDebugName(), UDFPS_TOUCHED_LAYER_NAME,
+ strlen(UDFPS_TOUCHED_LAYER_NAME)) == 0)) {
+ layerRequestingBgComposition = layer;
+ break;
+ }
}
return layerRequestingBgComposition;
}
@@ -956,17 +991,17 @@
// frame, then nothing more until we get new layers.
// - When a display is created with a private layer stack, we won't
// emit any black frames until a layer is added to the layer stack.
- const bool mustRecompose = dirty && !(empty && wasEmpty);
+ mMustRecompose = dirty && !(empty && wasEmpty);
const char flagPrefix[] = {'-', '+'};
static_cast<void>(flagPrefix);
- ALOGV_IF("%s: %s composition for %s (%cdirty %cempty %cwasEmpty)", __FUNCTION__,
- mustRecompose ? "doing" : "skipping", getName().c_str(), flagPrefix[dirty],
- flagPrefix[empty], flagPrefix[wasEmpty]);
+ ALOGV("%s: %s composition for %s (%cdirty %cempty %cwasEmpty)", __func__,
+ mMustRecompose ? "doing" : "skipping", getName().c_str(), flagPrefix[dirty],
+ flagPrefix[empty], flagPrefix[wasEmpty]);
- mRenderSurface->beginFrame(mustRecompose);
+ mRenderSurface->beginFrame(mMustRecompose);
- if (mustRecompose) {
+ if (mMustRecompose) {
outputState.lastCompositionHadVisibleLayers = !empty;
}
}
@@ -1099,6 +1134,10 @@
return;
}
+ if (isPowerHintSessionEnabled()) {
+ // get fence end time to know when gpu is complete in display
+ setHintSessionGpuFence(std::make_unique<FenceTime>(new Fence(dup(optReadyFence->get()))));
+ }
// swap buffers (presentation)
mRenderSurface->queueBuffer(std::move(*optReadyFence));
}
@@ -1218,7 +1257,8 @@
ATRACE_NAME("ClientCompositionCacheHit");
outputCompositionState.reusedClientComposition = true;
setExpensiveRenderingExpected(false);
- return base::unique_fd();
+ // b/239944175 pass the fence associated with the buffer.
+ return base::unique_fd(std::move(fd));
}
ATRACE_NAME("ClientCompositionCacheMiss");
mClientCompositionRequestCache->add(tex->getBuffer()->getId(), clientCompositionDisplay,
@@ -1403,6 +1443,14 @@
// The base class does nothing with this call.
}
+void Output::setHintSessionGpuFence(std::unique_ptr<FenceTime>&&) {
+ // The base class does nothing with this call.
+}
+
+bool Output::isPowerHintSessionEnabled() {
+ return false;
+}
+
void Output::postFramebuffer() {
ATRACE_CALL();
ALOGV(__FUNCTION__);
@@ -1460,7 +1508,8 @@
void Output::renderCachedSets(const CompositionRefreshArgs& refreshArgs) {
if (mPlanner) {
- mPlanner->renderCachedSets(getState(), refreshArgs.scheduledFrameTime);
+ mPlanner->renderCachedSets(getState(), refreshArgs.scheduledFrameTime,
+ getState().usesDeviceComposition || getSkipColorTransform());
}
}
@@ -1555,5 +1604,9 @@
mRenderSurface->prepareFrame(state.usesClientComposition, state.usesDeviceComposition);
}
+bool Output::mustRecompose() const {
+ return mMustRecompose;
+}
+
} // namespace impl
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 1bb9d0eb..2129dfe 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -19,6 +19,7 @@
#include <compositionengine/DisplayColorProfile.h>
#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/Output.h>
+#include <compositionengine/UdfpsExtension.h>
#include <compositionengine/impl/HwcBufferCache.h>
#include <compositionengine/impl/OutputCompositionState.h>
#include <compositionengine/impl/OutputLayer.h>
@@ -441,7 +442,17 @@
sourceCrop.bottom, to_string(error).c_str(), static_cast<int32_t>(error));
}
- if (auto error = hwcLayer->setZOrder(z); error != hal::Error::NONE) {
+ uint32_t z_udfps = z;
+ if ((strncmp(getLayerFE().getDebugName(), UDFPS_LAYER_NAME, strlen(UDFPS_LAYER_NAME)) == 0) ||
+ (strncmp(getLayerFE().getDebugName(), UDFPS_BIOMETRIC_PROMPT_LAYER_NAME,
+ strlen(UDFPS_BIOMETRIC_PROMPT_LAYER_NAME)) == 0)) {
+ z_udfps = getUdfpsZOrder(z, false);
+ } else if (strncmp(getLayerFE().getDebugName(), UDFPS_TOUCHED_LAYER_NAME,
+ strlen(UDFPS_TOUCHED_LAYER_NAME)) == 0) {
+ z_udfps = getUdfpsZOrder(z, true);
+ }
+
+ if (auto error = hwcLayer->setZOrder(z_udfps); error != hal::Error::NONE) {
ALOGE("[%s] Failed to set Z %u: %s (%d)", getLayerFE().getDebugName(), z,
to_string(error).c_str(), static_cast<int32_t>(error));
}
diff --git a/services/surfaceflinger/CompositionEngine/src/UdfpsExtension.cpp b/services/surfaceflinger/CompositionEngine/src/UdfpsExtension.cpp
new file mode 100644
index 0000000..2d9d086
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/UdfpsExtension.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2020 The LineageOS 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.
+ */
+
+#ifndef TARGET_PROVIDES_UDFPS_LIB
+#include <compositionengine/UdfpsExtension.h>
+
+uint32_t getUdfpsZOrder(uint32_t z, __unused bool touched) {
+ return z;
+}
+
+uint64_t getUdfpsUsageBits(uint64_t usageBits, __unused bool touched) {
+ return usageBits;
+}
+#endif
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index 641b806..d6f02ee 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -159,7 +159,8 @@
}
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 @@
.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 1062b70..9175dd0 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -99,7 +99,8 @@
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 @@
}
}
- 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 c8413eb..54133d9 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -201,11 +201,11 @@
finalPlan);
}
-void Planner::renderCachedSets(
- const OutputCompositionState& outputState,
- std::optional<std::chrono::steady_clock::time_point> renderDeadline) {
+void Planner::renderCachedSets(const OutputCompositionState& outputState,
+ std::optional<std::chrono::steady_clock::time_point> renderDeadline,
+ bool deviceHandlesColorTransform) {
ATRACE_CALL();
- mFlattener.renderCachedSets(outputState, renderDeadline);
+ mFlattener.renderCachedSets(outputState, renderDeadline, deviceHandlesColorTransform);
}
void Planner::dump(const Vector<String16>& args, std::string& result) {
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 0e5a7b6..5369642 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -169,6 +169,7 @@
EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine));
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mPowerAdvisor, usePowerHintSession()).WillRepeatedly(Return(false));
}
DisplayCreationArgs getDisplayCreationArgsForPhysicalDisplay() {
@@ -970,16 +971,40 @@
// We expect no calls to queueBuffer if composition was skipped.
EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(0);
+ EXPECT_CALL(*renderSurface, beginFrame(false));
gpuDisplay->editState().isEnabled = true;
gpuDisplay->editState().usesClientComposition = false;
gpuDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1));
gpuDisplay->editState().dirtyRegion = Region::INVALID_REGION;
+ gpuDisplay->editState().lastCompositionHadVisibleLayers = true;
+ gpuDisplay->beginFrame();
gpuDisplay->finishFrame({}, std::move(mResultWithoutBuffer));
}
-TEST_F(DisplayFinishFrameTest, performsCompositionIfDirty) {
+TEST_F(DisplayFinishFrameTest, skipsCompositionIfEmpty) {
+ auto args = getDisplayCreationArgsForGpuVirtualDisplay();
+ std::shared_ptr<impl::Display> gpuDisplay = impl::createDisplay(mCompositionEngine, args);
+
+ mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
+ gpuDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
+
+ // We expect no calls to queueBuffer if composition was skipped.
+ EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(0);
+ EXPECT_CALL(*renderSurface, beginFrame(false));
+
+ gpuDisplay->editState().isEnabled = true;
+ gpuDisplay->editState().usesClientComposition = false;
+ gpuDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1));
+ gpuDisplay->editState().dirtyRegion = Region(Rect(0, 0, 1, 1));
+ gpuDisplay->editState().lastCompositionHadVisibleLayers = false;
+
+ gpuDisplay->beginFrame();
+ gpuDisplay->finishFrame({}, std::move(mResultWithoutBuffer));
+}
+
+TEST_F(DisplayFinishFrameTest, performsCompositionIfDirtyAndNotEmpty) {
auto args = getDisplayCreationArgsForGpuVirtualDisplay();
std::shared_ptr<impl::Display> gpuDisplay = impl::createDisplay(mCompositionEngine, args);
@@ -988,11 +1013,15 @@
// We expect a single call to queueBuffer when composition is not skipped.
EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(1);
+ EXPECT_CALL(*renderSurface, beginFrame(true));
gpuDisplay->editState().isEnabled = true;
gpuDisplay->editState().usesClientComposition = false;
gpuDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1));
gpuDisplay->editState().dirtyRegion = Region(Rect(0, 0, 1, 1));
+ gpuDisplay->editState().lastCompositionHadVisibleLayers = true;
+
+ gpuDisplay->beginFrame();
gpuDisplay->finishFrame({}, std::move(mResultWithBuffer));
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 9b12b08..d7704a8 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -137,6 +137,7 @@
MOCK_METHOD(bool, hasDisplayIdleTimerCapability, (PhysicalDisplayId), (const, override));
MOCK_METHOD(Hwc2::AidlTransform, getPhysicalDisplayOrientation, (PhysicalDisplayId),
(const, override));
+ MOCK_METHOD(bool, getValidateSkipped, (HalDisplayId), (const, override));
};
} // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
index 50adcfb..c8bd5e4 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
@@ -38,11 +38,33 @@
MOCK_METHOD(bool, usePowerHintSession, (), (override));
MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override));
- MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDurationNanos), (override));
- MOCK_METHOD(void, sendActualWorkDuration, (int64_t actualDurationNanos, nsecs_t timestamp),
- (override));
+ MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDuration), (override));
+ MOCK_METHOD(void, sendActualWorkDuration, (), (override));
+ MOCK_METHOD(void, sendPredictedWorkDuration, (), (override));
MOCK_METHOD(void, enablePowerHint, (bool enabled), (override));
MOCK_METHOD(bool, startPowerHintSession, (const std::vector<int32_t>& threadIds), (override));
+ MOCK_METHOD(void, setGpuFenceTime,
+ (DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime), (override));
+ MOCK_METHOD(void, setHwcValidateTiming,
+ (DisplayId displayId, nsecs_t valiateStartTime, nsecs_t validateEndTime),
+ (override));
+ MOCK_METHOD(void, setHwcPresentTiming,
+ (DisplayId displayId, nsecs_t presentStartTime, nsecs_t presentEndTime),
+ (override));
+ MOCK_METHOD(void, setSkippedValidate, (DisplayId displayId, bool skipped), (override));
+ MOCK_METHOD(void, setRequiresClientComposition,
+ (DisplayId displayId, bool requiresClientComposition), (override));
+ MOCK_METHOD(void, setExpectedPresentTime, (nsecs_t expectedPresentTime), (override));
+ MOCK_METHOD(void, setSfPresentTiming, (nsecs_t presentFenceTime, nsecs_t presentEndTime),
+ (override));
+ MOCK_METHOD(void, setHwcPresentDelayedTime,
+ (DisplayId displayId,
+ std::chrono::steady_clock::time_point earliestFrameStartTime));
+ MOCK_METHOD(void, setFrameDelay, (nsecs_t frameDelayDuration), (override));
+ MOCK_METHOD(void, setCommitStart, (nsecs_t commitStartTime), (override));
+ MOCK_METHOD(void, setCompositeEnd, (nsecs_t compositeEndtime), (override));
+ MOCK_METHOD(void, setDisplays, (std::vector<DisplayId> & displayIds), (override));
+ MOCK_METHOD(void, setTotalFrameTargetWorkDuration, (int64_t targetDuration), (override));
};
} // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 063726b..cf12890 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -1505,6 +1505,8 @@
static const Region kTransparentRegionHint;
static const Region kTransparentRegionHintTwo;
static const Region kTransparentRegionHintTwo90Rotation;
+ static const Region kTransparentRegionHintNegative;
+ static const Region kTransparentRegionHintNegativeIntersectsBounds;
StrictMock<OutputPartialMock> mOutput;
LayerFESet mGeomSnapshots;
@@ -1528,6 +1530,10 @@
Region(Rect(25, 20, 50, 75));
const Region OutputEnsureOutputLayerIfVisibleTest::kTransparentRegionHintTwo90Rotation =
Region(Rect(125, 25, 180, 50));
+const Region OutputEnsureOutputLayerIfVisibleTest::kTransparentRegionHintNegative =
+ Region(Rect(INT32_MIN, INT32_MIN, INT32_MIN + 100, INT32_MIN + 200));
+const Region OutputEnsureOutputLayerIfVisibleTest::kTransparentRegionHintNegativeIntersectsBounds =
+ Region(Rect(INT32_MIN, INT32_MIN, 100, 100));
TEST_F(OutputEnsureOutputLayerIfVisibleTest, performsGeomLatchBeforeCheckingIfLayerIncluded) {
EXPECT_CALL(mOutput, includesLayer(sp<LayerFE>(mLayer.layerFE))).WillOnce(Return(false));
@@ -1997,6 +2003,41 @@
RegionEq(kTransparentRegionHintTwo90Rotation));
}
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, transparentRegionExcludesOutputLayer) {
+ mLayer.layerFEState.isOpaque = false;
+ mLayer.layerFEState.contentDirty = true;
+ mLayer.layerFEState.geomLayerBounds = kFullBoundsNoRotation.bounds().toFloatRect();
+ mLayer.layerFEState.transparentRegionHint = kFullBoundsNoRotation;
+
+ EXPECT_CALL(mOutput, ensureOutputLayer(_, _)).Times(0);
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, transparentRegionIgnoredWhenOutsideBounds) {
+ mLayer.layerFEState.isOpaque = false;
+ mLayer.layerFEState.contentDirty = true;
+ mLayer.layerFEState.geomLayerBounds = kFullBoundsNoRotation.bounds().toFloatRect();
+ mLayer.layerFEState.transparentRegionHint = kTransparentRegionHintNegative;
+
+ EXPECT_CALL(mOutput, ensureOutputLayer(_, _)).Times(0);
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, transparentRegionClipsWhenOutsideBounds) {
+ mLayer.layerFEState.isOpaque = false;
+ mLayer.layerFEState.contentDirty = true;
+ mLayer.layerFEState.compositionType =
+ aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION;
+ mLayer.layerFEState.transparentRegionHint = kTransparentRegionHintNegativeIntersectsBounds;
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer.layerFE)))
+ .WillOnce(Return(&mLayer.outputLayer));
+ ensureOutputLayerIfVisible();
+
+ // Check that the blocking region clips an out-of-bounds transparent region.
+ EXPECT_THAT(mLayer.outputLayerState.outputSpaceBlockingRegionHint,
+ RegionEq(kTransparentRegionHint));
+}
+
/*
* Output::present()
*/
@@ -3319,6 +3360,9 @@
MOCK_METHOD2(appendRegionFlashRequests,
void(const Region&, std::vector<LayerFE::LayerSettings>&));
MOCK_METHOD1(setExpensiveRenderingExpected, void(bool));
+ MOCK_METHOD(void, setHintSessionGpuFence, (std::unique_ptr<FenceTime> && gpuFence),
+ (override));
+ MOCK_METHOD(bool, isPowerHintSessionEnabled, (), (override));
};
OutputComposeSurfacesTest() {
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index 8a99e4e..0e9db36 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -376,7 +376,7 @@
.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 @@
.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 @@
.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 @@
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 @@
};
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 @@
};
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 @@
};
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 50e3a28..96021ec 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -159,13 +159,13 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 a915b61..b49c95d 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -104,7 +104,7 @@
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 @@
}
void DisplayDevice::setPowerMode(hal::PowerMode mode) {
+ if (mode == hal::PowerMode::OFF || mode == hal::PowerMode::ON) {
+ if (mStagedBrightness && mBrightness != mStagedBrightness) {
+ getCompositionDisplay()->setNextBrightness(*mStagedBrightness);
+ mBrightness = *mStagedBrightness;
+ }
+ mStagedBrightness = std::nullopt;
+ getCompositionDisplay()->applyDisplayBrightness(true);
+ }
+
mPowerMode = mode;
- getCompositionDisplay()->setCompositionEnabled(mPowerMode != hal::PowerMode::OFF);
+
+ getCompositionDisplay()->setCompositionEnabled(mPowerMode.has_value() &&
+ *mPowerMode != hal::PowerMode::OFF);
}
void DisplayDevice::enableLayerCaching(bool enable) {
getCompositionDisplay()->setLayerCachingEnabled(enable);
}
-hal::PowerMode DisplayDevice::getPowerMode() const {
+std::optional<hal::PowerMode> DisplayDevice::getPowerMode() const {
return mPowerMode;
}
bool DisplayDevice::isPoweredOn() const {
- return mPowerMode != hal::PowerMode::OFF;
+ return mPowerMode && *mPowerMode != hal::PowerMode::OFF;
}
void DisplayDevice::setActiveMode(DisplayModeId id) {
@@ -211,6 +222,7 @@
to_string(getId()).c_str());
return BAD_VALUE;
}
+ mNumModeSwitchesInPolicy++;
mUpcomingActiveMode = info;
ATRACE_INT(mActiveModeFPSHwcTrace.c_str(), info.mode->getFps().getIntValue());
return mHwComposer.setActiveModeWithConstraints(getPhysicalId(), info.mode->getHwcId(),
@@ -324,8 +336,10 @@
}
void DisplayDevice::persistBrightness(bool needsComposite) {
- if (needsComposite && mStagedBrightness && mBrightness != *mStagedBrightness) {
- getCompositionDisplay()->setNextBrightness(*mStagedBrightness);
+ if (mStagedBrightness && mBrightness != mStagedBrightness) {
+ if (needsComposite) {
+ getCompositionDisplay()->setNextBrightness(*mStagedBrightness);
+ }
mBrightness = *mStagedBrightness;
}
mStagedBrightness = std::nullopt;
@@ -372,7 +386,7 @@
}
result += "\n powerMode="s;
- result += to_string(mPowerMode);
+ result += mPowerMode.has_value() ? to_string(mPowerMode.value()) : "OFF(reset)";
result += '\n';
if (mRefreshRateConfigs) {
@@ -497,7 +511,7 @@
}
}
-bool DisplayDevice::setDesiredActiveMode(const ActiveModeInfo& info) {
+bool DisplayDevice::setDesiredActiveMode(const ActiveModeInfo& info, bool force) {
ATRACE_CALL();
LOG_ALWAYS_FATAL_IF(!info.mode, "desired mode not provided");
@@ -515,7 +529,7 @@
}
// Check if we are already at the desired mode
- if (getActiveMode()->getId() == info.mode->getId()) {
+ if (!force && getActiveMode()->getId() == info.mode->getId()) {
return false;
}
@@ -537,6 +551,27 @@
mDesiredActiveModeChanged = false;
}
+status_t DisplayDevice::setRefreshRatePolicy(
+ const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy) {
+ const auto oldPolicy = mRefreshRateConfigs->getCurrentPolicy();
+ const status_t setPolicyResult = overridePolicy
+ ? mRefreshRateConfigs->setOverridePolicy(policy)
+ : mRefreshRateConfigs->setDisplayManagerPolicy(*policy);
+
+ if (setPolicyResult == OK) {
+ const int numModeChanges = mNumModeSwitchesInPolicy.exchange(0);
+
+ ALOGI("Display %s policy changed\n"
+ "Previous: {%s}\n"
+ "Current: {%s}\n"
+ "%d mode changes were performed under the previous policy",
+ to_string(getId()).c_str(), oldPolicy.toString().c_str(),
+ policy ? policy->toString().c_str() : "null", numModeChanges);
+ }
+
+ return setPolicyResult;
+}
+
std::atomic<int32_t> DisplayDeviceState::sNextSequenceId(1);
} // namespace android
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index d5d87b4..b91dece 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -181,7 +181,7 @@
/* ------------------------------------------------------------------------
* 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;
@@ -204,7 +204,7 @@
}
};
- bool setDesiredActiveMode(const ActiveModeInfo&) EXCLUDES(mActiveModeLock);
+ bool setDesiredActiveMode(const ActiveModeInfo&, bool force = false) EXCLUDES(mActiveModeLock);
std::optional<ActiveModeInfo> getDesiredActiveMode() const EXCLUDES(mActiveModeLock);
void clearDesiredActiveModeState() EXCLUDES(mActiveModeLock);
ActiveModeInfo getUpcomingActiveMode() const REQUIRES(kMainThreadContext) {
@@ -249,6 +249,10 @@
nsecs_t getVsyncPeriodFromHWC() const;
nsecs_t getRefreshTimestamp() const;
+ status_t setRefreshRatePolicy(
+ const std::optional<scheduler::RefreshRateConfigs::Policy>& policy,
+ bool overridePolicy);
+
// release HWC resources (if any) for removable displays
void disconnect();
@@ -277,11 +281,11 @@
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;
+ std::optional<float> mStagedBrightness;
+ std::optional<float> mBrightness;
const DisplayModes mSupportedModes;
std::atomic<nsecs_t> mLastHwVsync = 0;
@@ -303,6 +307,8 @@
TracedOrdinal<bool> mDesiredActiveModeChanged
GUARDED_BY(mActiveModeLock) = {"DesiredActiveModeChanged", false};
ActiveModeInfo mUpcomingActiveMode GUARDED_BY(kMainThreadContext);
+
+ std::atomic_int mNumModeSwitchesInPolicy = 0;
};
struct DisplayDeviceState {
@@ -360,8 +366,7 @@
HdrCapabilities hdrCapabilities;
int32_t supportedPerFrameMetadata{0};
std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> hwcColorModes;
- hardware::graphics::composer::hal::PowerMode initialPowerMode{
- hardware::graphics::composer::hal::PowerMode::ON};
+ std::optional<hardware::graphics::composer::hal::PowerMode> initialPowerMode;
bool isPrimary{false};
DisplayModes supportedModes;
DisplayModeId activeModeId;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 0da8ece..a6aee1f 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -738,6 +738,13 @@
});
}
+bool HWComposer::getValidateSkipped(HalDisplayId displayId) const {
+ if (mDisplayData.count(displayId) == 0) {
+ return false;
+ }
+ return mDisplayData.at(displayId).validateWasSkipped;
+}
+
status_t HWComposer::setBootDisplayMode(PhysicalDisplayId displayId,
hal::HWConfigId displayModeId) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 4c0ecd8..92a8f30 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -199,6 +199,9 @@
PhysicalDisplayId, float brightness, float brightnessNits,
const Hwc2::Composer::DisplayBrightnessOptions&) = 0;
+ // Get whether the display skipped validation on the latest present
+ virtual bool getValidateSkipped(HalDisplayId displayId) const = 0;
+
// Events handling ---------------------------------------------------------
// Returns stable display ID (and display name on connection of new or previously disconnected
@@ -397,6 +400,8 @@
status_t setActiveColorMode(PhysicalDisplayId, ui::ColorMode, ui::RenderIntent) override;
+ bool getValidateSkipped(HalDisplayId displayId) const override;
+
// Composer 2.4
ui::DisplayConnectionType getDisplayConnectionType(PhysicalDisplayId) const override;
bool isVsyncPeriodSwitchSupported(PhysicalDisplayId) const override;
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index b5678b4..a0350b7 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -187,7 +187,7 @@
if (!mSupportsPowerHint.has_value()) {
std::lock_guard lock(mPowerHalMutex);
HalWrapper* const halWrapper = getPowerHal();
- mSupportsPowerHint = halWrapper->supportsPowerHintSession();
+ mSupportsPowerHint = halWrapper && halWrapper->supportsPowerHintSession();
}
return *mSupportsPowerHint;
}
@@ -196,7 +196,7 @@
return mPowerHintSessionRunning;
}
-void PowerAdvisor::setTargetWorkDuration(int64_t targetDurationNanos) {
+void PowerAdvisor::setTargetWorkDuration(int64_t targetDuration) {
if (!usePowerHintSession()) {
ALOGV("Power hint session target duration cannot be set, skipping");
return;
@@ -205,26 +205,45 @@
std::lock_guard lock(mPowerHalMutex);
HalWrapper* const halWrapper = getPowerHal();
if (halWrapper != nullptr) {
- halWrapper->setTargetWorkDuration(targetDurationNanos - kTargetSafetyMargin.count());
+ halWrapper->setTargetWorkDuration(targetDuration);
}
}
}
-void PowerAdvisor::sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timeStampNanos) {
+void PowerAdvisor::sendActualWorkDuration() {
if (!mBootFinished || !usePowerHintSession()) {
ALOGV("Actual work duration power hint cannot be sent, skipping");
return;
}
- {
+ const std::optional<nsecs_t> actualDuration = estimateWorkDuration(false);
+ if (actualDuration.has_value()) {
std::lock_guard lock(mPowerHalMutex);
HalWrapper* const halWrapper = getPowerHal();
if (halWrapper != nullptr) {
- halWrapper->sendActualWorkDuration(actualDurationNanos, timeStampNanos);
+ halWrapper->sendActualWorkDuration(*actualDuration + kTargetSafetyMargin.count(),
+ systemTime());
}
}
}
-// needs to be set after the flag is known but before PowerAdvisor enters onBootFinished
+void PowerAdvisor::sendPredictedWorkDuration() {
+ if (!mBootFinished || !usePowerHintSession()) {
+ ALOGV("Actual work duration power hint cannot be sent, skipping");
+ return;
+ }
+
+ const std::optional<nsecs_t> predictedDuration = estimateWorkDuration(true);
+
+ if (predictedDuration.has_value()) {
+ std::lock_guard lock(mPowerHalMutex);
+ HalWrapper* const halWrapper = getPowerHal();
+ if (halWrapper != nullptr) {
+ halWrapper->sendActualWorkDuration(*predictedDuration + kTargetSafetyMargin.count(),
+ systemTime());
+ }
+ }
+}
+
void PowerAdvisor::enablePowerHint(bool enabled) {
mPowerHintEnabled = enabled;
}
@@ -244,6 +263,302 @@
return mPowerHintSessionRunning;
}
+void PowerAdvisor::setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime) {
+ DisplayTimingData& displayData = mDisplayTimingData[displayId];
+ if (displayData.gpuEndFenceTime) {
+ nsecs_t signalTime = displayData.gpuEndFenceTime->getSignalTime();
+ if (signalTime != Fence::SIGNAL_TIME_INVALID && signalTime != Fence::SIGNAL_TIME_PENDING) {
+ for (auto&& [_, otherDisplayData] : mDisplayTimingData) {
+ // If the previous display started before us but ended after we should have
+ // started, then it likely delayed our start time and we must compensate for that.
+ // Displays finishing earlier should have already made their way through this call
+ // and swapped their timing into "lastValid" from "latest", so we check that here.
+ if (!otherDisplayData.lastValidGpuStartTime.has_value()) continue;
+ if ((*otherDisplayData.lastValidGpuStartTime < *displayData.gpuStartTime) &&
+ (*otherDisplayData.lastValidGpuEndTime > *displayData.gpuStartTime)) {
+ displayData.lastValidGpuStartTime = *otherDisplayData.lastValidGpuEndTime;
+ break;
+ }
+ }
+ displayData.lastValidGpuStartTime = displayData.gpuStartTime;
+ displayData.lastValidGpuEndTime = signalTime;
+ }
+ }
+ displayData.gpuEndFenceTime = std::move(fenceTime);
+ displayData.gpuStartTime = systemTime();
+}
+
+void PowerAdvisor::setHwcValidateTiming(DisplayId displayId, nsecs_t validateStartTime,
+ nsecs_t validateEndTime) {
+ DisplayTimingData& displayData = mDisplayTimingData[displayId];
+ displayData.hwcValidateStartTime = validateStartTime;
+ displayData.hwcValidateEndTime = validateEndTime;
+}
+
+void PowerAdvisor::setHwcPresentTiming(DisplayId displayId, nsecs_t presentStartTime,
+ nsecs_t presentEndTime) {
+ DisplayTimingData& displayData = mDisplayTimingData[displayId];
+ displayData.hwcPresentStartTime = presentStartTime;
+ displayData.hwcPresentEndTime = presentEndTime;
+}
+
+void PowerAdvisor::setSkippedValidate(DisplayId displayId, bool skipped) {
+ mDisplayTimingData[displayId].skippedValidate = skipped;
+}
+
+void PowerAdvisor::setRequiresClientComposition(DisplayId displayId,
+ bool requiresClientComposition) {
+ mDisplayTimingData[displayId].usedClientComposition = requiresClientComposition;
+}
+
+void PowerAdvisor::setExpectedPresentTime(nsecs_t expectedPresentTime) {
+ mExpectedPresentTimes.append(expectedPresentTime);
+}
+
+void PowerAdvisor::setSfPresentTiming(nsecs_t presentFenceTime, nsecs_t presentEndTime) {
+ mLastSfPresentEndTime = presentEndTime;
+ mLastPresentFenceTime = presentFenceTime;
+}
+
+void PowerAdvisor::setFrameDelay(nsecs_t frameDelayDuration) {
+ mFrameDelayDuration = frameDelayDuration;
+}
+
+void PowerAdvisor::setHwcPresentDelayedTime(
+ DisplayId displayId, std::chrono::steady_clock::time_point earliestFrameStartTime) {
+ mDisplayTimingData[displayId].hwcPresentDelayedTime =
+ (earliestFrameStartTime - std::chrono::steady_clock::now()).count() + systemTime();
+}
+
+void PowerAdvisor::setCommitStart(nsecs_t commitStartTime) {
+ mCommitStartTimes.append(commitStartTime);
+}
+
+void PowerAdvisor::setCompositeEnd(nsecs_t compositeEnd) {
+ mLastPostcompDuration = compositeEnd - mLastSfPresentEndTime;
+}
+
+void PowerAdvisor::setDisplays(std::vector<DisplayId>& displayIds) {
+ mDisplayIds = displayIds;
+}
+
+void PowerAdvisor::setTotalFrameTargetWorkDuration(nsecs_t targetDuration) {
+ mTotalFrameTargetDuration = targetDuration;
+}
+
+std::vector<DisplayId> PowerAdvisor::getOrderedDisplayIds(
+ std::optional<nsecs_t> DisplayTimingData::*sortBy) {
+ std::vector<DisplayId> sortedDisplays;
+ std::copy_if(mDisplayIds.begin(), mDisplayIds.end(), std::back_inserter(sortedDisplays),
+ [&](DisplayId id) {
+ return mDisplayTimingData.count(id) &&
+ (mDisplayTimingData[id].*sortBy).has_value();
+ });
+ std::sort(sortedDisplays.begin(), sortedDisplays.end(), [&](DisplayId idA, DisplayId idB) {
+ return *(mDisplayTimingData[idA].*sortBy) < *(mDisplayTimingData[idB].*sortBy);
+ });
+ return sortedDisplays;
+}
+
+std::optional<nsecs_t> PowerAdvisor::estimateWorkDuration(bool earlyHint) {
+ if (earlyHint && (!mExpectedPresentTimes.isFull() || !mCommitStartTimes.isFull())) {
+ return std::nullopt;
+ }
+
+ // Tracks when we finish presenting to hwc
+ nsecs_t estimatedEndTime = mCommitStartTimes[0];
+
+ // How long we spent this frame not doing anything, waiting for fences or vsync
+ nsecs_t idleDuration = 0;
+
+ // Most recent previous gpu end time in the current frame, probably from a prior display, used
+ // as the start time for the next gpu operation if it ran over time since it probably blocked
+ std::optional<nsecs_t> previousValidGpuEndTime;
+
+ // The currently estimated gpu end time for the frame,
+ // used to accumulate gpu time as we iterate over the active displays
+ std::optional<nsecs_t> estimatedGpuEndTime;
+
+ // If we're predicting at the start of the frame, we use last frame as our reference point
+ // If we're predicting at the end of the frame, we use the current frame as a reference point
+ nsecs_t referenceFrameStartTime = (earlyHint ? mCommitStartTimes[-1] : mCommitStartTimes[0]);
+
+ // When the prior frame should be presenting to the display
+ // If we're predicting at the start of the frame, we use last frame's expected present time
+ // If we're predicting at the end of the frame, the present fence time is already known
+ nsecs_t lastFramePresentTime = (earlyHint ? mExpectedPresentTimes[-1] : mLastPresentFenceTime);
+
+ // The timing info for the previously calculated display, if there was one
+ std::optional<DisplayTimeline> previousDisplayReferenceTiming;
+ std::vector<DisplayId>&& displayIds =
+ getOrderedDisplayIds(&DisplayTimingData::hwcPresentStartTime);
+ DisplayTimeline referenceTiming, estimatedTiming;
+
+ // Iterate over the displays that use hwc in the same order they are presented
+ for (DisplayId displayId : displayIds) {
+ if (mDisplayTimingData.count(displayId) == 0) {
+ continue;
+ }
+
+ auto& displayData = mDisplayTimingData.at(displayId);
+
+ // mLastPresentFenceTime should always be the time of the reference frame, since it will be
+ // the previous frame's present fence if called at the start, and current frame's if called
+ // at the end
+ referenceTiming = displayData.calculateDisplayTimeline(mLastPresentFenceTime);
+
+ // If this is the first display, include the duration before hwc present starts
+ if (!previousDisplayReferenceTiming.has_value()) {
+ estimatedEndTime += referenceTiming.hwcPresentStartTime - referenceFrameStartTime;
+ } else { // Otherwise add the time since last display's hwc present finished
+ estimatedEndTime += referenceTiming.hwcPresentStartTime -
+ previousDisplayReferenceTiming->hwcPresentEndTime;
+ }
+
+ // Late hint can re-use reference timing here since it's estimating its own reference frame
+ estimatedTiming = earlyHint
+ ? referenceTiming.estimateTimelineFromReference(lastFramePresentTime,
+ estimatedEndTime)
+ : referenceTiming;
+
+ // Update predicted present finish time with this display's present time
+ estimatedEndTime = estimatedTiming.hwcPresentEndTime;
+
+ // Track how long we spent waiting for the fence, can be excluded from the timing estimate
+ idleDuration += estimatedTiming.probablyWaitsForPresentFence
+ ? lastFramePresentTime - estimatedTiming.presentFenceWaitStartTime
+ : 0;
+
+ // Track how long we spent waiting to present, can be excluded from the timing estimate
+ idleDuration += earlyHint ? 0 : referenceTiming.hwcPresentDelayDuration;
+
+ // Estimate the reference frame's gpu timing
+ auto gpuTiming = displayData.estimateGpuTiming(previousValidGpuEndTime);
+ if (gpuTiming.has_value()) {
+ previousValidGpuEndTime = gpuTiming->startTime + gpuTiming->duration;
+
+ // Estimate the prediction frame's gpu end time from the reference frame
+ estimatedGpuEndTime =
+ std::max(estimatedTiming.hwcPresentStartTime, estimatedGpuEndTime.value_or(0)) +
+ gpuTiming->duration;
+ }
+ previousDisplayReferenceTiming = referenceTiming;
+ }
+ ATRACE_INT64("Idle duration", idleDuration);
+
+ nsecs_t estimatedFlingerEndTime = earlyHint ? estimatedEndTime : mLastSfPresentEndTime;
+
+ // Don't count time spent idly waiting in the estimate as we could do more work in that time
+ estimatedEndTime -= idleDuration;
+ estimatedFlingerEndTime -= idleDuration;
+
+ // We finish the frame when both present and the gpu are done, so wait for the later of the two
+ // Also add the frame delay duration since the target did not move while we were delayed
+ nsecs_t totalDuration = mFrameDelayDuration +
+ std::max(estimatedEndTime, estimatedGpuEndTime.value_or(0)) - mCommitStartTimes[0];
+
+ // We finish SurfaceFlinger when post-composition finishes, so add that in here
+ nsecs_t flingerDuration =
+ estimatedFlingerEndTime + mLastPostcompDuration - mCommitStartTimes[0];
+
+ // Combine the two timings into a single normalized one
+ nsecs_t combinedDuration = combineTimingEstimates(totalDuration, flingerDuration);
+
+ return std::make_optional(combinedDuration);
+}
+
+nsecs_t PowerAdvisor::combineTimingEstimates(nsecs_t totalDuration, nsecs_t flingerDuration) {
+ nsecs_t targetDuration;
+ {
+ std::lock_guard lock(mPowerHalMutex);
+ targetDuration = *getPowerHal()->getTargetWorkDuration();
+ }
+ if (!mTotalFrameTargetDuration.has_value()) return flingerDuration;
+
+ // Normalize total to the flinger target (vsync period) since that's how often we actually send
+ // hints
+ nsecs_t normalizedTotalDuration = (targetDuration * totalDuration) / *mTotalFrameTargetDuration;
+ return std::max(flingerDuration, normalizedTotalDuration);
+}
+
+PowerAdvisor::DisplayTimeline PowerAdvisor::DisplayTimeline::estimateTimelineFromReference(
+ nsecs_t fenceTime, nsecs_t displayStartTime) {
+ DisplayTimeline estimated;
+ estimated.hwcPresentStartTime = displayStartTime;
+
+ // We don't predict waiting for vsync alignment yet
+ estimated.hwcPresentDelayDuration = 0;
+
+ // How long we expect to run before we start waiting for the fence
+ // For now just re-use last frame's post-present duration and assume it will not change much
+ // Excludes time spent waiting for vsync since that's not going to be consistent
+ estimated.presentFenceWaitStartTime = estimated.hwcPresentStartTime +
+ (presentFenceWaitStartTime - (hwcPresentStartTime + hwcPresentDelayDuration));
+ estimated.probablyWaitsForPresentFence = fenceTime > estimated.presentFenceWaitStartTime;
+ estimated.hwcPresentEndTime = postPresentFenceHwcPresentDuration +
+ (estimated.probablyWaitsForPresentFence ? fenceTime
+ : estimated.presentFenceWaitStartTime);
+ return estimated;
+}
+
+PowerAdvisor::DisplayTimeline PowerAdvisor::DisplayTimingData::calculateDisplayTimeline(
+ nsecs_t fenceTime) {
+ DisplayTimeline timeline;
+ // How long between calling hwc present and trying to wait on the fence
+ const nsecs_t fenceWaitStartDelay =
+ (skippedValidate ? kFenceWaitStartDelaySkippedValidate : kFenceWaitStartDelayValidated)
+ .count();
+
+ // Did our reference frame wait for an appropriate vsync before calling into hwc
+ const bool waitedOnHwcPresentTime = hwcPresentDelayedTime.has_value() &&
+ *hwcPresentDelayedTime > *hwcPresentStartTime &&
+ *hwcPresentDelayedTime < *hwcPresentEndTime;
+
+ // Use validate start here if we skipped it because we did validate + present together
+ timeline.hwcPresentStartTime = skippedValidate ? *hwcValidateStartTime : *hwcPresentStartTime;
+
+ // Use validate end here if we skipped it because we did validate + present together
+ timeline.hwcPresentEndTime = skippedValidate ? *hwcValidateEndTime : *hwcPresentEndTime;
+
+ // How long hwc present was delayed waiting for the next appropriate vsync
+ timeline.hwcPresentDelayDuration =
+ (waitedOnHwcPresentTime ? *hwcPresentDelayedTime - *hwcPresentStartTime : 0);
+ // When we started waiting for the present fence after calling into hwc present
+ timeline.presentFenceWaitStartTime =
+ timeline.hwcPresentStartTime + timeline.hwcPresentDelayDuration + fenceWaitStartDelay;
+ timeline.probablyWaitsForPresentFence = fenceTime > timeline.presentFenceWaitStartTime &&
+ fenceTime < timeline.hwcPresentEndTime;
+
+ // How long we ran after we finished waiting for the fence but before hwc present finished
+ timeline.postPresentFenceHwcPresentDuration = timeline.hwcPresentEndTime -
+ (timeline.probablyWaitsForPresentFence ? fenceTime
+ : timeline.presentFenceWaitStartTime);
+ return timeline;
+}
+
+std::optional<PowerAdvisor::GpuTimeline> PowerAdvisor::DisplayTimingData::estimateGpuTiming(
+ std::optional<nsecs_t> previousEnd) {
+ if (!(usedClientComposition && lastValidGpuStartTime.has_value() && gpuEndFenceTime)) {
+ return std::nullopt;
+ }
+ const nsecs_t latestGpuStartTime = std::max(previousEnd.value_or(0), *gpuStartTime);
+ const nsecs_t latestGpuEndTime = gpuEndFenceTime->getSignalTime();
+ nsecs_t gpuDuration = 0;
+ if (latestGpuEndTime != Fence::SIGNAL_TIME_INVALID &&
+ latestGpuEndTime != Fence::SIGNAL_TIME_PENDING) {
+ // If we know how long the most recent gpu duration was, use that
+ gpuDuration = latestGpuEndTime - latestGpuStartTime;
+ } else if (lastValidGpuEndTime.has_value()) {
+ // If we don't have the fence data, use the most recent information we do have
+ gpuDuration = *lastValidGpuEndTime - *lastValidGpuStartTime;
+ if (latestGpuEndTime == Fence::SIGNAL_TIME_PENDING) {
+ // If pending but went over the previous duration, use current time as the end
+ gpuDuration = std::max(gpuDuration, systemTime() - latestGpuStartTime);
+ }
+ }
+ return GpuTimeline{.duration = gpuDuration, .startTime = latestGpuStartTime};
+}
+
class HidlPowerHalWrapper : public PowerAdvisor::HalWrapper {
public:
HidlPowerHalWrapper(sp<V1_3::IPower> powerHal) : mPowerHal(std::move(powerHal)) {}
@@ -325,6 +640,9 @@
}
mSupportsPowerHint = checkPowerHintSessionSupported();
+
+ // Currently set to 0 to disable rate limiter by default
+ mAllowedActualDeviation = base::GetIntProperty<nsecs_t>("debug.sf.allowed_actual_deviation", 0);
}
AidlPowerHalWrapper::~AidlPowerHalWrapper() {
@@ -332,7 +650,7 @@
mPowerHintSession->close();
mPowerHintSession = nullptr;
}
-};
+}
std::unique_ptr<PowerAdvisor::HalWrapper> AidlPowerHalWrapper::connect() {
// This only waits if the service is actually declared
@@ -370,7 +688,7 @@
return ret.isOk();
}
-// only version 2+ of the aidl supports power hint sessions, hidl has no support
+// Only version 2+ of the aidl supports power hint sessions, hidl has no support
bool AidlPowerHalWrapper::supportsPowerHintSession() {
return mSupportsPowerHint;
}
@@ -424,30 +742,14 @@
return isPowerHintSessionRunning();
}
-bool AidlPowerHalWrapper::shouldSetTargetDuration(int64_t targetDurationNanos) {
- if (targetDurationNanos <= 0) {
- return false;
- }
- // report if the change in target from our last submission to now exceeds the threshold
- return abs(1.0 -
- static_cast<double>(mLastTargetDurationSent) /
- static_cast<double>(targetDurationNanos)) >= kAllowedTargetDeviationPercent;
-}
-
-void AidlPowerHalWrapper::setTargetWorkDuration(int64_t targetDurationNanos) {
+void AidlPowerHalWrapper::setTargetWorkDuration(int64_t targetDuration) {
ATRACE_CALL();
- mTargetDuration = targetDurationNanos;
- if (sTraceHintSessionData) ATRACE_INT64("Time target", targetDurationNanos);
- if (!sNormalizeTarget && isPowerHintSessionRunning() &&
- shouldSetTargetDuration(targetDurationNanos)) {
- if (mLastActualDurationSent.has_value()) {
- // update the error term here since we are actually sending an update to powerhal
- if (sTraceHintSessionData)
- ATRACE_INT64("Target error term", targetDurationNanos - *mLastActualDurationSent);
- }
- ALOGV("Sending target time: %" PRId64 "ns", targetDurationNanos);
- mLastTargetDurationSent = targetDurationNanos;
- auto ret = mPowerHintSession->updateTargetWorkDuration(targetDurationNanos);
+ mTargetDuration = targetDuration;
+ if (sTraceHintSessionData) ATRACE_INT64("Time target", targetDuration);
+ if (isPowerHintSessionRunning() && (targetDuration != mLastTargetDurationSent)) {
+ ALOGV("Sending target time: %" PRId64 "ns", targetDuration);
+ mLastTargetDurationSent = targetDuration;
+ auto ret = mPowerHintSession->updateTargetWorkDuration(targetDuration);
if (!ret.isOk()) {
ALOGW("Failed to set power hint target work duration with error: %s",
ret.exceptionMessage().c_str());
@@ -456,8 +758,8 @@
}
}
-bool AidlPowerHalWrapper::shouldReportActualDurationsNow() {
- // report if we have never reported before or are approaching a stale session
+bool AidlPowerHalWrapper::shouldReportActualDurations() {
+ // Report if we have never reported before or are approaching a stale session
if (!mLastActualDurationSent.has_value() ||
(systemTime() - mLastActualReportTimestamp) > kStaleTimeout.count()) {
return true;
@@ -466,65 +768,42 @@
if (!mActualDuration.has_value()) {
return false;
}
-
- // duration of most recent timing
- const double mostRecentActualDuration = static_cast<double>(*mActualDuration);
- // duration of the last timing actually reported to the powerhal
- const double lastReportedActualDuration = static_cast<double>(*mLastActualDurationSent);
-
- // report if the change in duration from then to now exceeds the threshold
- return abs(1.0 - mostRecentActualDuration / lastReportedActualDuration) >=
- kAllowedActualDeviationPercent;
+ // Report if the change in actual duration exceeds the threshold
+ return abs(*mActualDuration - *mLastActualDurationSent) > mAllowedActualDeviation;
}
-void AidlPowerHalWrapper::sendActualWorkDuration(int64_t actualDurationNanos,
- nsecs_t timeStampNanos) {
+void AidlPowerHalWrapper::sendActualWorkDuration(int64_t actualDuration, nsecs_t timestamp) {
ATRACE_CALL();
- if (actualDurationNanos < 0 || !isPowerHintSessionRunning()) {
+ if (actualDuration < 0 || !isPowerHintSessionRunning()) {
ALOGV("Failed to send actual work duration, skipping");
return;
}
- nsecs_t reportedDuration = actualDurationNanos;
+ const nsecs_t reportedDuration = actualDuration;
- // normalize the sent values to a pre-set target
- if (sNormalizeTarget) {
- reportedDuration += mLastTargetDurationSent - mTargetDuration;
- } else {
- // when target duration change is within deviation and not updated, adjust the actual
- // duration proportionally based on the difference, e.g. if new target is 5ms longer than
- // last reported but actual duration is the same as last target, we want to report a smaller
- // actual work duration now to indicate that we are overshooting
- if (mLastTargetDurationSent != kDefaultTarget.count() && mTargetDuration != 0) {
- reportedDuration =
- static_cast<int64_t>(static_cast<long double>(mLastTargetDurationSent) /
- mTargetDuration * actualDurationNanos);
- mActualDuration = reportedDuration;
- }
- }
mActualDuration = reportedDuration;
WorkDuration duration;
duration.durationNanos = reportedDuration;
- duration.timeStampNanos = timeStampNanos;
+ duration.timeStampNanos = timestamp;
mPowerHintQueue.push_back(duration);
if (sTraceHintSessionData) {
- ATRACE_INT64("Measured duration", actualDurationNanos);
- ATRACE_INT64("Target error term", mTargetDuration - actualDurationNanos);
+ ATRACE_INT64("Measured duration", actualDuration);
+ ATRACE_INT64("Target error term", actualDuration - mTargetDuration);
ATRACE_INT64("Reported duration", reportedDuration);
ATRACE_INT64("Reported target", mLastTargetDurationSent);
- ATRACE_INT64("Reported target error term", mLastTargetDurationSent - reportedDuration);
+ ATRACE_INT64("Reported target error term", reportedDuration - mLastTargetDurationSent);
}
ALOGV("Sending actual work duration of: %" PRId64 " on reported target: %" PRId64
" with error: %" PRId64,
- reportedDuration, mLastTargetDurationSent, mLastTargetDurationSent - reportedDuration);
+ reportedDuration, mLastTargetDurationSent, reportedDuration - mLastTargetDurationSent);
// This rate limiter queues similar duration reports to the powerhal into
// batches to avoid excessive binder calls. The criteria to send a given batch
// are outlined in shouldReportActualDurationsNow()
- if (shouldReportActualDurationsNow()) {
+ if (shouldReportActualDurations()) {
ALOGV("Sending hint update batch");
mLastActualReportTimestamp = systemTime();
auto ret = mPowerHintSession->reportActualWorkDuration(mPowerHintQueue);
@@ -534,8 +813,8 @@
mShouldReconnectHal = true;
}
mPowerHintQueue.clear();
- // we save the non-normalized value here to detect % changes
- mLastActualDurationSent = reportedDuration;
+ // We save the actual duration here for rate limiting
+ mLastActualDurationSent = actualDuration;
}
}
@@ -551,76 +830,73 @@
return mTargetDuration;
}
+void AidlPowerHalWrapper::setAllowedActualDeviation(nsecs_t allowedDeviation) {
+ mAllowedActualDeviation = allowedDeviation;
+}
+
const bool AidlPowerHalWrapper::sTraceHintSessionData =
base::GetBoolProperty(std::string("debug.sf.trace_hint_sessions"), false);
-const bool AidlPowerHalWrapper::sNormalizeTarget =
- base::GetBoolProperty(std::string("debug.sf.normalize_hint_session_durations"), false);
-
PowerAdvisor::HalWrapper* PowerAdvisor::getPowerHal() {
- static std::unique_ptr<HalWrapper> sHalWrapper = nullptr;
- static bool sHasHal = true;
-
- if (!sHasHal) {
+ if (!mHasHal) {
return nullptr;
}
- // grab old hint session values before we destroy any existing wrapper
+ // Grab old hint session values before we destroy any existing wrapper
std::vector<int32_t> oldPowerHintSessionThreadIds;
std::optional<int64_t> oldTargetWorkDuration;
- if (sHalWrapper != nullptr) {
- oldPowerHintSessionThreadIds = sHalWrapper->getPowerHintSessionThreadIds();
- oldTargetWorkDuration = sHalWrapper->getTargetWorkDuration();
+ if (mHalWrapper != nullptr) {
+ oldPowerHintSessionThreadIds = mHalWrapper->getPowerHintSessionThreadIds();
+ oldTargetWorkDuration = mHalWrapper->getTargetWorkDuration();
}
// If we used to have a HAL, but it stopped responding, attempt to reconnect
if (mReconnectPowerHal) {
- sHalWrapper = nullptr;
+ mHalWrapper = nullptr;
mReconnectPowerHal = false;
}
- if (sHalWrapper != nullptr) {
- auto wrapper = sHalWrapper.get();
- // if the wrapper is fine, return it, but if it indicates a reconnect, remake it
+ if (mHalWrapper != nullptr) {
+ auto wrapper = mHalWrapper.get();
+ // If the wrapper is fine, return it, but if it indicates a reconnect, remake it
if (!wrapper->shouldReconnectHAL()) {
return wrapper;
}
ALOGD("Reconnecting Power HAL");
- sHalWrapper = nullptr;
+ mHalWrapper = nullptr;
}
- // at this point, we know for sure there is no running session
+ // At this point, we know for sure there is no running session
mPowerHintSessionRunning = false;
// First attempt to connect to the AIDL Power HAL
- sHalWrapper = AidlPowerHalWrapper::connect();
+ mHalWrapper = AidlPowerHalWrapper::connect();
// If that didn't succeed, attempt to connect to the HIDL Power HAL
- if (sHalWrapper == nullptr) {
- sHalWrapper = HidlPowerHalWrapper::connect();
+ if (mHalWrapper == nullptr) {
+ mHalWrapper = HidlPowerHalWrapper::connect();
} else {
ALOGD("Successfully connecting AIDL Power HAL");
- // if AIDL, pass on any existing hint session values
- // thread ids always safe to set
- sHalWrapper->setPowerHintSessionThreadIds(oldPowerHintSessionThreadIds);
- // only set duration and start if duration is defined
+ // If AIDL, pass on any existing hint session values
+ mHalWrapper->setPowerHintSessionThreadIds(oldPowerHintSessionThreadIds);
+ // Only set duration and start if duration is defined
if (oldTargetWorkDuration.has_value()) {
- sHalWrapper->setTargetWorkDuration(*oldTargetWorkDuration);
- // only start if possible to run and both threadids and duration are defined
+ mHalWrapper->setTargetWorkDuration(*oldTargetWorkDuration);
+ // Only start if possible to run and both threadids and duration are defined
if (usePowerHintSession() && !oldPowerHintSessionThreadIds.empty()) {
- mPowerHintSessionRunning = sHalWrapper->startPowerHintSession();
+ mPowerHintSessionRunning = mHalWrapper->startPowerHintSession();
}
}
}
// If we make it to this point and still don't have a HAL, it's unlikely we
// will, so stop trying
- if (sHalWrapper == nullptr) {
- sHasHal = false;
+ if (mHalWrapper == nullptr) {
+ mHasHal = false;
}
- return sHalWrapper.get();
+ return mHalWrapper.get();
}
} // namespace impl
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index 7c10e19..6e25f78 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -18,11 +18,15 @@
#include <atomic>
#include <chrono>
+#include <unordered_map>
#include <unordered_set>
+#include <ui/DisplayId.h>
+#include <ui/FenceTime.h>
#include <utils/Mutex.h>
#include <android/hardware/power/IPower.h>
+#include <compositionengine/impl/OutputCompositionState.h>
#include <ui/DisplayIdentification.h>
#include "../Scheduler/OneShotTimer.h"
@@ -44,13 +48,50 @@
virtual void setExpensiveRenderingExpected(DisplayId displayId, bool expected) = 0;
virtual bool isUsingExpensiveRendering() = 0;
virtual void notifyDisplayUpdateImminent() = 0;
+ // Checks both if it supports and if it's enabled
virtual bool usePowerHintSession() = 0;
virtual bool supportsPowerHintSession() = 0;
virtual bool isPowerHintSessionRunning() = 0;
- virtual void setTargetWorkDuration(int64_t targetDurationNanos) = 0;
- virtual void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timestamp) = 0;
+ // Sends a power hint that updates to the target work duration for the frame
+ virtual void setTargetWorkDuration(nsecs_t targetDuration) = 0;
+ // Sends a power hint for the actual known work duration at the end of the frame
+ virtual void sendActualWorkDuration() = 0;
+ // Sends a power hint for the upcoming frame predicted from previous frame timing
+ virtual void sendPredictedWorkDuration() = 0;
+ // Sets whether the power hint session is enabled
virtual void enablePowerHint(bool enabled) = 0;
+ // Initializes the power hint session
virtual bool startPowerHintSession(const std::vector<int32_t>& threadIds) = 0;
+ // Provides PowerAdvisor with a copy of the gpu fence so it can determine the gpu end time
+ virtual void setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime) = 0;
+ // Reports the start and end times of a hwc validate call this frame for a given display
+ virtual void setHwcValidateTiming(DisplayId displayId, nsecs_t validateStartTime,
+ nsecs_t validateEndTime) = 0;
+ // Reports the start and end times of a hwc present call this frame for a given display
+ virtual void setHwcPresentTiming(DisplayId displayId, nsecs_t presentStartTime,
+ nsecs_t presentEndTime) = 0;
+ // Reports the expected time that the current frame will present to the display
+ virtual void setExpectedPresentTime(nsecs_t expectedPresentTime) = 0;
+ // Reports the most recent present fence time and end time once known
+ virtual void setSfPresentTiming(nsecs_t presentFenceTime, nsecs_t presentEndTime) = 0;
+ // Reports whether a display used client composition this frame
+ virtual void setRequiresClientComposition(DisplayId displayId,
+ bool requiresClientComposition) = 0;
+ // Reports whether a given display skipped validation this frame
+ virtual void setSkippedValidate(DisplayId displayId, bool skipped) = 0;
+ // Reports when a hwc present is delayed, and the time that it will resume
+ virtual void setHwcPresentDelayedTime(
+ DisplayId displayId, std::chrono::steady_clock::time_point earliestFrameStartTime) = 0;
+ // Reports the start delay for SurfaceFlinger this frame
+ virtual void setFrameDelay(nsecs_t frameDelayDuration) = 0;
+ // Reports the SurfaceFlinger commit start time this frame
+ virtual void setCommitStart(nsecs_t commitStartTime) = 0;
+ // Reports the SurfaceFlinger composite end time this frame
+ virtual void setCompositeEnd(nsecs_t compositeEndTime) = 0;
+ // Reports the list of the currently active displays
+ virtual void setDisplays(std::vector<DisplayId>& displayIds) = 0;
+ // Sets the target duration for the entire pipeline including the gpu
+ virtual void setTotalFrameTargetWorkDuration(nsecs_t targetDuration) = 0;
};
namespace impl {
@@ -70,12 +111,11 @@
virtual void restartPowerHintSession() = 0;
virtual void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) = 0;
virtual bool startPowerHintSession() = 0;
- virtual void setTargetWorkDuration(int64_t targetDurationNanos) = 0;
- virtual void sendActualWorkDuration(int64_t actualDurationNanos,
- nsecs_t timeStampNanos) = 0;
+ virtual void setTargetWorkDuration(nsecs_t targetDuration) = 0;
+ virtual void sendActualWorkDuration(nsecs_t actualDuration, nsecs_t timestamp) = 0;
virtual bool shouldReconnectHAL() = 0;
virtual std::vector<int32_t> getPowerHintSessionThreadIds() = 0;
- virtual std::optional<int64_t> getTargetWorkDuration() = 0;
+ virtual std::optional<nsecs_t> getTargetWorkDuration() = 0;
};
PowerAdvisor(SurfaceFlinger& flinger);
@@ -89,26 +129,43 @@
bool usePowerHintSession() override;
bool supportsPowerHintSession() override;
bool isPowerHintSessionRunning() override;
- void setTargetWorkDuration(int64_t targetDurationNanos) override;
- void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timestamp) override;
+ void setTargetWorkDuration(nsecs_t targetDuration) override;
+ void sendActualWorkDuration() override;
+ void sendPredictedWorkDuration() override;
void enablePowerHint(bool enabled) override;
bool startPowerHintSession(const std::vector<int32_t>& threadIds) override;
+ void setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime);
+ void setHwcValidateTiming(DisplayId displayId, nsecs_t valiateStartTime,
+ nsecs_t validateEndTime) override;
+ void setHwcPresentTiming(DisplayId displayId, nsecs_t presentStartTime,
+ nsecs_t presentEndTime) override;
+ void setSkippedValidate(DisplayId displayId, bool skipped) override;
+ void setRequiresClientComposition(DisplayId displayId, bool requiresClientComposition) override;
+ void setExpectedPresentTime(nsecs_t expectedPresentTime) override;
+ void setSfPresentTiming(nsecs_t presentFenceTime, nsecs_t presentEndTime) override;
+ void setHwcPresentDelayedTime(
+ DisplayId displayId,
+ std::chrono::steady_clock::time_point earliestFrameStartTime) override;
+
+ void setFrameDelay(nsecs_t frameDelayDuration) override;
+ void setCommitStart(nsecs_t commitStartTime) override;
+ void setCompositeEnd(nsecs_t compositeEndTime) override;
+ void setDisplays(std::vector<DisplayId>& displayIds) override;
+ void setTotalFrameTargetWorkDuration(nsecs_t targetDuration) override;
private:
+ friend class PowerAdvisorTest;
+
+ // Tracks if powerhal exists
+ bool mHasHal = true;
+ // Holds the hal wrapper for getPowerHal
+ std::unique_ptr<HalWrapper> mHalWrapper GUARDED_BY(mPowerHalMutex) = nullptr;
+
HalWrapper* getPowerHal() REQUIRES(mPowerHalMutex);
bool mReconnectPowerHal GUARDED_BY(mPowerHalMutex) = false;
std::mutex mPowerHalMutex;
std::atomic_bool mBootFinished = false;
- std::optional<bool> mPowerHintEnabled;
- std::optional<bool> mSupportsPowerHint;
- bool mPowerHintSessionRunning = false;
-
- // An adjustable safety margin which moves the "target" earlier to allow flinger to
- // go a bit over without dropping a frame, especially since we can't measure
- // the exact time HWC finishes composition so "actual" durations are measured
- // from the end of present() instead, which is a bit later.
- static constexpr const std::chrono::nanoseconds kTargetSafetyMargin = 2ms;
std::unordered_set<DisplayId> mExpensiveDisplays;
bool mNotifiedExpensiveRendering = false;
@@ -117,6 +174,110 @@
std::atomic_bool mSendUpdateImminent = true;
std::atomic<nsecs_t> mLastScreenUpdatedTime = 0;
std::optional<scheduler::OneShotTimer> mScreenUpdateTimer;
+
+ // Higher-level timing data used for estimation
+ struct DisplayTimeline {
+ // The start of hwc present, or the start of validate if it happened there instead
+ nsecs_t hwcPresentStartTime = -1;
+ // The end of hwc present or validate, whichever one actually presented
+ nsecs_t hwcPresentEndTime = -1;
+ // How long the actual hwc present was delayed after hwcPresentStartTime
+ nsecs_t hwcPresentDelayDuration = 0;
+ // When we think we started waiting for the present fence after calling into hwc present and
+ // after potentially waiting for the earliest present time
+ nsecs_t presentFenceWaitStartTime = -1;
+ // How long we ran after we finished waiting for the fence but before hwc present finished
+ nsecs_t postPresentFenceHwcPresentDuration = 0;
+ // Are we likely to have waited for the present fence during composition
+ bool probablyWaitsForPresentFence = false;
+ // Estimate one frame's timeline from that of a previous frame
+ DisplayTimeline estimateTimelineFromReference(nsecs_t fenceTime, nsecs_t displayStartTime);
+ };
+
+ struct GpuTimeline {
+ nsecs_t duration = 0;
+ nsecs_t startTime = -1;
+ };
+
+ // Power hint session data recorded from the pipeline
+ struct DisplayTimingData {
+ std::unique_ptr<FenceTime> gpuEndFenceTime;
+ std::optional<nsecs_t> gpuStartTime;
+ std::optional<nsecs_t> lastValidGpuEndTime;
+ std::optional<nsecs_t> lastValidGpuStartTime;
+ std::optional<nsecs_t> hwcPresentStartTime;
+ std::optional<nsecs_t> hwcPresentEndTime;
+ std::optional<nsecs_t> hwcValidateStartTime;
+ std::optional<nsecs_t> hwcValidateEndTime;
+ std::optional<nsecs_t> hwcPresentDelayedTime;
+ bool usedClientComposition = false;
+ bool skippedValidate = false;
+ // Calculate high-level timing milestones from more granular display timing data
+ DisplayTimeline calculateDisplayTimeline(nsecs_t fenceTime);
+ // Estimate the gpu duration for a given display from previous gpu timing data
+ std::optional<GpuTimeline> estimateGpuTiming(std::optional<nsecs_t> previousEnd);
+ };
+
+ template <class T, size_t N>
+ class RingBuffer {
+ std::array<T, N> elements = {};
+ size_t mIndex = 0;
+ size_t numElements = 0;
+
+ public:
+ void append(T item) {
+ mIndex = (mIndex + 1) % N;
+ numElements = std::min(N, numElements + 1);
+ elements[mIndex] = item;
+ }
+ bool isFull() const { return numElements == N; }
+ // Allows access like [0] == current, [-1] = previous, etc..
+ T& operator[](int offset) {
+ size_t positiveOffset =
+ static_cast<size_t>((offset % static_cast<int>(N)) + static_cast<int>(N));
+ return elements[(mIndex + positiveOffset) % N];
+ }
+ };
+
+ // Filter and sort the display ids by a given property
+ std::vector<DisplayId> getOrderedDisplayIds(std::optional<nsecs_t> DisplayTimingData::*sortBy);
+ // Estimates a frame's total work duration including gpu time.
+ // Runs either at the beginning or end of a frame, using the most recent data available
+ std::optional<nsecs_t> estimateWorkDuration(bool earlyHint);
+ // There are two different targets and actual work durations we care about,
+ // this normalizes them together and takes the max of the two
+ nsecs_t combineTimingEstimates(nsecs_t totalDuration, nsecs_t flingerDuration);
+
+ std::unordered_map<DisplayId, DisplayTimingData> mDisplayTimingData;
+
+ // Current frame's delay
+ nsecs_t mFrameDelayDuration = 0;
+ // Last frame's post-composition duration
+ nsecs_t mLastPostcompDuration = 0;
+ // Buffer of recent commit start times
+ RingBuffer<nsecs_t, 2> mCommitStartTimes;
+ // Buffer of recent expected present times
+ RingBuffer<nsecs_t, 2> mExpectedPresentTimes;
+ // Most recent present fence time, set at the end of the frame once known
+ nsecs_t mLastPresentFenceTime = -1;
+ // Most recent present fence time, set at the end of the frame once known
+ nsecs_t mLastSfPresentEndTime = -1;
+ // Target for the entire pipeline including gpu
+ std::optional<nsecs_t> mTotalFrameTargetDuration;
+ // Updated list of display IDs
+ std::vector<DisplayId> mDisplayIds;
+
+ std::optional<bool> mPowerHintEnabled;
+ std::optional<bool> mSupportsPowerHint;
+ bool mPowerHintSessionRunning = false;
+
+ // An adjustable safety margin which pads the "actual" value sent to PowerHAL,
+ // encouraging more aggressive boosting to give SurfaceFlinger a larger margin for error
+ static constexpr const std::chrono::nanoseconds kTargetSafetyMargin = 1ms;
+
+ // How long we expect hwc to run after the present call until it waits for the fence
+ static constexpr const std::chrono::nanoseconds kFenceWaitStartDelayValidated = 150us;
+ static constexpr const std::chrono::nanoseconds kFenceWaitStartDelaySkippedValidate = 250us;
};
class AidlPowerHalWrapper : public PowerAdvisor::HalWrapper {
@@ -133,50 +294,50 @@
void restartPowerHintSession() override;
void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) override;
bool startPowerHintSession() override;
- void setTargetWorkDuration(int64_t targetDurationNanos) override;
- void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timeStampNanos) override;
+ void setTargetWorkDuration(nsecs_t targetDuration) override;
+ void sendActualWorkDuration(nsecs_t actualDuration, nsecs_t timestamp) override;
bool shouldReconnectHAL() override;
std::vector<int32_t> getPowerHintSessionThreadIds() override;
- std::optional<int64_t> getTargetWorkDuration() override;
+ std::optional<nsecs_t> getTargetWorkDuration() override;
private:
+ friend class AidlPowerHalWrapperTest;
+
bool checkPowerHintSessionSupported();
void closePowerHintSession();
- bool shouldReportActualDurationsNow();
- bool shouldSetTargetDuration(int64_t targetDurationNanos);
+ bool shouldReportActualDurations();
+
+ // Used for testing
+ void setAllowedActualDeviation(nsecs_t);
const sp<hardware::power::IPower> mPowerHal = nullptr;
bool mHasExpensiveRendering = false;
bool mHasDisplayUpdateImminent = false;
// Used to indicate an error state and need for reconstruction
bool mShouldReconnectHal = false;
- // This is not thread safe, but is currently protected by mPowerHalMutex so it needs no lock
+
+ // Power hint session data
+
+ // Concurrent access for this is protected by mPowerHalMutex
sp<hardware::power::IPowerHintSession> mPowerHintSession = nullptr;
// Queue of actual durations saved to report
std::vector<hardware::power::WorkDuration> mPowerHintQueue;
- // The latest un-normalized values we have received for target and actual
- int64_t mTargetDuration = kDefaultTarget.count();
- std::optional<int64_t> mActualDuration;
+ // The latest values we have received for target and actual
+ nsecs_t mTargetDuration = kDefaultTarget.count();
+ std::optional<nsecs_t> mActualDuration;
// The list of thread ids, stored so we can restart the session from this class if needed
std::vector<int32_t> mPowerHintThreadIds;
- bool mSupportsPowerHint;
+ bool mSupportsPowerHint = false;
// Keep track of the last messages sent for rate limiter change detection
- std::optional<int64_t> mLastActualDurationSent;
- // timestamp of the last report we sent, used to avoid stale sessions
- int64_t mLastActualReportTimestamp = 0;
- int64_t mLastTargetDurationSent = kDefaultTarget.count();
- // Whether to normalize all the actual values as error terms relative to a constant target
- // This saves a binder call by not setting the target, and should not affect the pid values
- static const bool sNormalizeTarget;
+ std::optional<nsecs_t> mLastActualDurationSent;
+ // Timestamp of the last report we sent, used to avoid stale sessions
+ nsecs_t mLastActualReportTimestamp = 0;
+ nsecs_t mLastTargetDurationSent = kDefaultTarget.count();
+ // Max amount the error term can vary without causing an actual value report
+ nsecs_t mAllowedActualDeviation = -1;
// Whether we should emit ATRACE_INT data for hint sessions
static const bool sTraceHintSessionData;
-
- // Max percent the actual duration can vary without causing a report (eg: 0.1 = 10%)
- static constexpr double kAllowedActualDeviationPercent = 0.1;
- // Max percent the target duration can vary without causing a report (eg: 0.1 = 10%)
- static constexpr double kAllowedTargetDeviationPercent = 0.1;
- // Target used for init and normalization, the actual value does not really matter
- static constexpr const std::chrono::nanoseconds kDefaultTarget = 50ms;
+ static constexpr const std::chrono::nanoseconds kDefaultTarget = 16ms;
// Amount of time after the last message was sent before the session goes stale
// actually 100ms but we use 80 here to ideally avoid going stale
static constexpr const std::chrono::nanoseconds kStaleTimeout = 80ms;
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 3803a78..7c7200a 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -50,7 +50,7 @@
const sp<IGraphicBufferProducer>& sink,
const sp<IGraphicBufferProducer>& bqProducer,
const sp<IGraphicBufferConsumer>& bqConsumer,
- const std::string& name)
+ const std::string& name, bool secure)
: ConsumerBase(bqConsumer),
mHwc(hwc),
mDisplayId(displayId),
@@ -69,7 +69,9 @@
mOutputFence(Fence::NO_FENCE),
mFbProducerSlot(BufferQueue::INVALID_BUFFER_SLOT),
mOutputProducerSlot(BufferQueue::INVALID_BUFFER_SLOT),
- mForceHwcCopy(SurfaceFlinger::useHwcForRgbToYuv) {
+ mForceHwcCopy(SurfaceFlinger::useHwcForRgbToYuv),
+ mSecure(secure),
+ mSinkUsage(0) {
mSource[SOURCE_SINK] = sink;
mSource[SOURCE_SCRATCH] = bqProducer;
@@ -87,6 +89,8 @@
// on usage bits.
int sinkUsage;
sink->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &sinkUsage);
+ mSinkUsage |= (GRALLOC_USAGE_HW_COMPOSER | sinkUsage);
+ setOutputUsage(mSinkUsage);
if (sinkUsage & (GRALLOC_USAGE_SW_READ_MASK | GRALLOC_USAGE_SW_WRITE_MASK)) {
int sinkFormat;
sink->query(NATIVE_WINDOW_FORMAT, &sinkFormat);
@@ -115,7 +119,11 @@
}
mMustRecompose = mustRecompose;
-
+ //For WFD use cases we must always set the recompose flag in order
+ //to support pause/resume functionality
+ if (mOutputUsage & GRALLOC_USAGE_HW_VIDEO_ENCODER) {
+ mMustRecompose = true;
+ }
VDS_LOGW_IF(mDebugState != DebugState::Idle, "Unexpected %s in %s state", __func__,
ftl::enum_string(mDebugState).c_str());
mDebugState = DebugState::Begun;
@@ -152,7 +160,7 @@
}
if (mCompositionType != CompositionType::Gpu &&
- (mOutputFormat != mDefaultOutputFormat || mOutputUsage != GRALLOC_USAGE_HW_COMPOSER)) {
+ (mOutputFormat != mDefaultOutputFormat || !(mOutputUsage & GRALLOC_USAGE_HW_COMPOSER))) {
// We must have just switched from GPU-only to MIXED or HWC
// composition. Stop using the format and usage requested by the GPU
// driver; they may be suboptimal when HWC is writing to the output
@@ -164,7 +172,7 @@
// format/usage and get a new buffer when the GPU driver calls
// dequeueBuffer().
mOutputFormat = mDefaultOutputFormat;
- mOutputUsage = GRALLOC_USAGE_HW_COMPOSER;
+ setOutputUsage(GRALLOC_USAGE_HW_COMPOSER);
refreshOutputBuffer();
}
@@ -248,7 +256,7 @@
int sslot = mapProducer2SourceSlot(SOURCE_SINK, mOutputProducerSlot);
QueueBufferOutput qbo;
VDS_LOGV("%s: queue sink sslot=%d", __func__, sslot);
- if (mMustRecompose) {
+ if (retireFence->isValid() && mMustRecompose) {
status_t result = mSource[SOURCE_SINK]->queueBuffer(sslot,
QueueBufferInput(
systemTime(), false /* isAutoTimestamp */,
@@ -312,6 +320,14 @@
PixelFormat format, uint64_t usage, int* sslot, sp<Fence>* fence) {
LOG_ALWAYS_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId).has_value());
+ // Exclude video encoder usage flag from scratch buffer usage flags.
+ if (source == SOURCE_SCRATCH) {
+ usage |= GRALLOC_USAGE_HW_FB;
+ usage &= ~(GRALLOC_USAGE_HW_VIDEO_ENCODER);
+ VDS_LOGV("%s(%s): updated scratch buffer usage flags=%#" PRIx64,
+ __func__, ftl::enum_string(source).c_str(), usage);
+ }
+
status_t result =
mSource[source]->dequeueBuffer(sslot, fence, mSinkBufferWidth, mSinkBufferHeight,
format, usage, nullptr, nullptr);
@@ -341,11 +357,11 @@
}
}
if (result & BUFFER_NEEDS_REALLOCATION) {
- result = mSource[source]->requestBuffer(*sslot, &mProducerBuffers[pslot]);
- if (result < 0) {
+ auto status = mSource[source]->requestBuffer(*sslot, &mProducerBuffers[pslot]);
+ if (status < 0) {
mProducerBuffers[pslot].clear();
mSource[source]->cancelBuffer(*sslot, *fence);
- return result;
+ return status;
}
VDS_LOGV("%s(%s): buffers[%d]=%p fmt=%d usage=%#" PRIx64, __func__,
ftl::enum_string(source).c_str(), pslot, mProducerBuffers[pslot].get(),
@@ -406,7 +422,7 @@
__func__, w, h, format, usage, mSinkBufferWidth, mSinkBufferHeight,
buf->getPixelFormat(), buf->getUsage());
mOutputFormat = format;
- mOutputUsage = usage;
+ setOutputUsage(usage);
result = refreshOutputBuffer();
if (result < 0)
return result;
@@ -660,6 +676,21 @@
return type == CompositionType::Unknown ? "Unknown"s : ftl::Flags(type).string();
}
+/* Helper to update the output usage when the display is secure */
+
+void VirtualDisplaySurface::setOutputUsage(uint64_t /*flag*/) {
+
+ mOutputUsage = mSinkUsage;
+ if (mSecure && (mOutputUsage & GRALLOC_USAGE_HW_VIDEO_ENCODER)) {
+ /*TODO: Currently, the framework can only say whether the display
+ * and its subsequent session are secure or not. However, there is
+ * no mechanism to distinguish the different levels of security.
+ * The current solution assumes WV L3 protection.
+ */
+ mOutputUsage |= GRALLOC_USAGE_PROTECTED;
+ }
+}
+
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index e21095a..851d91f 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -77,7 +77,8 @@
public:
VirtualDisplaySurface(HWComposer&, VirtualDisplayId, const sp<IGraphicBufferProducer>& sink,
const sp<IGraphicBufferProducer>& bqProducer,
- const sp<IGraphicBufferConsumer>& bqConsumer, const std::string& name);
+ const sp<IGraphicBufferConsumer>& bqConsumer,
+ const std::string& name, bool secure);
//
// DisplaySurface interface
@@ -134,6 +135,7 @@
sp<Fence>* outFence, float outTransformMatrix[16]) override;
virtual status_t getUniqueId(uint64_t* outId) const override;
virtual status_t getConsumerUsage(uint64_t* outUsage) const override;
+ virtual void setOutputUsage(uint64_t flag);
//
// Utility methods
@@ -263,6 +265,8 @@
compositionengine::impl::HwcBufferCache mHwcBufferCache;
bool mForceHwcCopy;
+ bool mSecure;
+ int mSinkUsage;
};
} // namespace android
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index be16942..aff94d1 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -81,6 +81,7 @@
namespace android {
namespace {
constexpr int kDumpTableRowLength = 159;
+const ui::Transform kIdentityTransform;
} // namespace
using namespace ftl::flag_operators;
@@ -405,7 +406,7 @@
const auto& drawingState{getDrawingState()};
const auto alpha = static_cast<float>(getAlpha());
const bool opaque = isOpaque(drawingState);
- const bool usesRoundedCorners = getRoundedCornerState().radius != 0.f;
+ const bool usesRoundedCorners = hasRoundedCorners();
auto blendMode = Hwc2::IComposerClient::BlendMode::NONE;
if (!opaque || alpha != 1.0f) {
@@ -481,7 +482,7 @@
compositionState->hasProtectedContent = isProtected();
compositionState->dimmingEnabled = isDimmingEnabled();
- const bool usesRoundedCorners = getRoundedCornerState().radius != 0.f;
+ const bool usesRoundedCorners = hasRoundedCorners();
compositionState->isOpaque =
isOpaque(drawingState) && !usesRoundedCorners && getAlpha() == 1.0_hf;
@@ -1914,27 +1915,22 @@
const auto& parent = mDrawingParent.promote();
if (parent != nullptr) {
parentSettings = parent->getRoundedCornerState();
- if (parentSettings.radius > 0) {
+ if (parentSettings.hasRoundedCorners()) {
ui::Transform t = getActiveTransform(getDrawingState());
t = t.inverse();
parentSettings.cropRect = t.transform(parentSettings.cropRect);
- // The rounded corners shader only accepts 1 corner radius for performance reasons,
- // but a transform matrix can define horizontal and vertical scales.
- // Let's take the average between both of them and pass into the shader, practically we
- // never do this type of transformation on windows anyway.
- auto scaleX = sqrtf(t[0][0] * t[0][0] + t[0][1] * t[0][1]);
- auto scaleY = sqrtf(t[1][0] * t[1][0] + t[1][1] * t[1][1]);
- parentSettings.radius *= (scaleX + scaleY) / 2.0f;
+ parentSettings.radius.x *= t.getScaleX();
+ parentSettings.radius.y *= t.getScaleY();
}
}
// Get layer settings
Rect layerCropRect = getCroppedBufferSize(getDrawingState());
- const float radius = getDrawingState().cornerRadius;
+ const vec2 radius(getDrawingState().cornerRadius, getDrawingState().cornerRadius);
RoundedCornerState layerSettings(layerCropRect.toFloatRect(), radius);
- const bool layerSettingsValid = layerSettings.radius > 0 && layerCropRect.isValid();
+ const bool layerSettingsValid = layerSettings.hasRoundedCorners() && layerCropRect.isValid();
- if (layerSettingsValid && parentSettings.radius > 0) {
+ if (layerSettingsValid && parentSettings.hasRoundedCorners()) {
// If the parent and the layer have rounded corner settings, use the parent settings if the
// parent crop is entirely inside the layer crop.
// This has limitations and cause rendering artifacts. See b/200300845 for correct fix.
@@ -1948,7 +1944,7 @@
}
} else if (layerSettingsValid) {
return layerSettings;
- } else if (parentSettings.radius > 0) {
+ } else if (parentSettings.hasRoundedCorners()) {
return parentSettings;
}
return {};
@@ -2069,7 +2065,8 @@
layerInfo->set_effective_scaling_mode(getEffectiveScalingMode());
layerInfo->set_requested_corner_radius(getDrawingState().cornerRadius);
- layerInfo->set_corner_radius(getRoundedCornerState().radius);
+ layerInfo->set_corner_radius(
+ (getRoundedCornerState().radius.x + getRoundedCornerState().radius.y) / 2.0);
layerInfo->set_background_blur_radius(getBackgroundBlurRadius());
layerInfo->set_is_trusted_overlay(isTrustedOverlay());
LayerProtoHelper::writeToProtoDeprecated(transform, layerInfo->mutable_transform());
@@ -2162,7 +2159,8 @@
if ((traceFlags & LayerTracing::TRACE_INPUT) && needsInputInfo()) {
WindowInfo info;
if (useDrawing) {
- info = fillInputInfo(ui::Transform(), /* displayIsSecure */ true);
+ info = fillInputInfo(
+ InputDisplayArgs{.transform = &kIdentityTransform, .isSecure = true});
} else {
info = state.inputInfo;
}
@@ -2369,7 +2367,7 @@
}
}
-WindowInfo Layer::fillInputInfo(const ui::Transform& displayTransform, bool displayIsSecure) {
+WindowInfo Layer::fillInputInfo(const InputDisplayArgs& displayArgs) {
if (!hasInputInfo()) {
mDrawingState.inputInfo.name = getName();
mDrawingState.inputInfo.ownerUid = mOwnerUid;
@@ -2378,12 +2376,21 @@
mDrawingState.inputInfo.displayId = getLayerStack().id;
}
+ const ui::Transform& displayTransform =
+ displayArgs.transform != nullptr ? *displayArgs.transform : kIdentityTransform;
+
WindowInfo info = mDrawingState.inputInfo;
info.id = sequence;
info.displayId = getLayerStack().id;
fillInputFrameInfo(info, displayTransform);
+ if (displayArgs.transform == nullptr) {
+ // Do not let the window receive touches if it is not associated with a valid display
+ // transform. We still allow the window to receive keys and prevent ANRs.
+ info.inputConfig |= WindowInfo::InputConfig::NOT_TOUCHABLE;
+ }
+
// For compatibility reasons we let layers which can receive input
// receive input before they have actually submitted a buffer. Because
// of this we use canReceiveInput instead of isVisible to check the
@@ -2401,7 +2408,7 @@
// If the window will be blacked out on a display because the display does not have the secure
// flag and the layer has the secure flag set, then drop input.
- if (!displayIsSecure && isSecure()) {
+ if (!displayArgs.isSecure && isSecure()) {
info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT;
}
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 24abad9..200baf0 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -134,13 +134,14 @@
struct RoundedCornerState {
RoundedCornerState() = default;
- RoundedCornerState(FloatRect cropRect, float radius)
+ RoundedCornerState(const FloatRect& cropRect, const vec2& radius)
: cropRect(cropRect), radius(radius) {}
// Rounded rectangle in local layer coordinate space.
FloatRect cropRect = FloatRect();
// Radius of the rounded rectangle.
- float radius = 0.0f;
+ vec2 radius;
+ bool hasRoundedCorners() const { return radius.x > 0.0f && radius.y > 0.0f; }
};
using FrameRate = scheduler::LayerInfo::FrameRate;
@@ -596,7 +597,7 @@
// corner crop does not intersect with its own rounded corner crop.
virtual RoundedCornerState getRoundedCornerState() const;
- bool hasRoundedCorners() const override { return getRoundedCornerState().radius > .0f; }
+ bool hasRoundedCorners() const override { return getRoundedCornerState().hasRoundedCorners(); }
virtual PixelFormat getPixelFormat() const { return PIXEL_FORMAT_NONE; }
/**
@@ -844,7 +845,11 @@
bool getPremultipledAlpha() const;
void setInputInfo(const gui::WindowInfo& info);
- gui::WindowInfo fillInputInfo(const ui::Transform& displayTransform, bool displayIsSecure);
+ struct InputDisplayArgs {
+ const ui::Transform* transform = nullptr;
+ bool isSecure = false;
+ };
+ gui::WindowInfo fillInputInfo(const InputDisplayArgs& displayArgs);
/**
* Returns whether this layer has an explicitly set input-info.
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index d4435c2..a9180d4 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -16,9 +16,10 @@
#include <algorithm>
-#include "RefreshRateOverlay.h"
+#include "BackgroundExecutor.h"
#include "Client.h"
#include "Layer.h"
+#include "RefreshRateOverlay.h"
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
@@ -56,6 +57,14 @@
} // namespace
+SurfaceControlHolder::~SurfaceControlHolder() {
+ // Hand the sp<SurfaceControl> to the helper thread to release the last
+ // reference. This makes sure that the SurfaceControl is destructed without
+ // SurfaceFlinger::mStateLock held.
+ BackgroundExecutor::getInstance().sendCallbacks(
+ {[sc = std::move(mSurfaceControl)]() mutable { sc.clear(); }});
+}
+
void RefreshRateOverlay::SevenSegmentDrawer::drawSegment(Segment segment, int left, SkColor color,
SkCanvas& canvas) {
const SkRect rect = [&]() {
@@ -210,21 +219,27 @@
return buffers;
}
+std::unique_ptr<SurfaceControlHolder> createSurfaceControlHolder() {
+ sp<SurfaceControl> surfaceControl =
+ SurfaceComposerClient::getDefault()
+ ->createSurface(String8("RefreshRateOverlay"), kBufferWidth, kBufferHeight,
+ PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceBufferState);
+ return std::make_unique<SurfaceControlHolder>(std::move(surfaceControl));
+}
+
RefreshRateOverlay::RefreshRateOverlay(FpsRange fpsRange, bool showSpinner)
: mFpsRange(fpsRange),
mShowSpinner(showSpinner),
- mSurfaceControl(SurfaceComposerClient::getDefault()
- ->createSurface(String8("RefreshRateOverlay"), kBufferWidth,
- kBufferHeight, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceBufferState)) {
+ mSurfaceControl(createSurfaceControlHolder()) {
if (!mSurfaceControl) {
ALOGE("%s: Failed to create buffer state layer", __func__);
return;
}
- createTransaction(mSurfaceControl)
- .setLayer(mSurfaceControl, INT32_MAX - 2)
- .setTrustedOverlay(mSurfaceControl, true)
+ createTransaction(mSurfaceControl->get())
+ .setLayer(mSurfaceControl->get(), INT32_MAX - 2)
+ .setTrustedOverlay(mSurfaceControl->get(), true)
.apply();
}
@@ -233,7 +248,7 @@
if (!mSurfaceControl) return kNoBuffers;
const auto transformHint =
- static_cast<ui::Transform::RotationFlags>(mSurfaceControl->getTransformHint());
+ static_cast<ui::Transform::RotationFlags>(mSurfaceControl->get()->getTransformHint());
// Tell SurfaceFlinger about the pre-rotation on the buffer.
const auto transform = [&] {
@@ -247,7 +262,9 @@
}
}();
- createTransaction(mSurfaceControl).setTransform(mSurfaceControl, transform).apply();
+ createTransaction(mSurfaceControl->get())
+ .setTransform(mSurfaceControl->get(), transform)
+ .apply();
BufferCache::const_iterator it = mBufferCache.find({fps.getIntValue(), transformHint});
if (it == mBufferCache.end()) {
@@ -289,21 +306,21 @@
Rect frame((3 * width) >> 4, height >> 5);
frame.offsetBy(width >> 5, height >> 4);
- createTransaction(mSurfaceControl)
- .setMatrix(mSurfaceControl, frame.getWidth() / static_cast<float>(kBufferWidth), 0, 0,
- frame.getHeight() / static_cast<float>(kBufferHeight))
- .setPosition(mSurfaceControl, frame.left, frame.top)
+ createTransaction(mSurfaceControl->get())
+ .setMatrix(mSurfaceControl->get(), frame.getWidth() / static_cast<float>(kBufferWidth),
+ 0, 0, frame.getHeight() / static_cast<float>(kBufferHeight))
+ .setPosition(mSurfaceControl->get(), frame.left, frame.top)
.apply();
}
void RefreshRateOverlay::setLayerStack(ui::LayerStack stack) {
- createTransaction(mSurfaceControl).setLayerStack(mSurfaceControl, stack).apply();
+ createTransaction(mSurfaceControl->get()).setLayerStack(mSurfaceControl->get(), stack).apply();
}
void RefreshRateOverlay::changeRefreshRate(Fps fps) {
mCurrentFps = fps;
const auto buffer = getOrCreateBuffers(fps)[mFrame];
- createTransaction(mSurfaceControl).setBuffer(mSurfaceControl, buffer).apply();
+ createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply();
}
void RefreshRateOverlay::animate() {
@@ -312,7 +329,7 @@
const auto& buffers = getOrCreateBuffers(*mCurrentFps);
mFrame = (mFrame + 1) % buffers.size();
const auto buffer = buffers[mFrame];
- createTransaction(mSurfaceControl).setBuffer(mSurfaceControl, buffer).apply();
+ createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply();
}
} // namespace android
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index a465a36..a2966e6 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -33,6 +33,20 @@
class GraphicBuffer;
class SurfaceControl;
+class SurfaceFlinger;
+
+// Helper class to delete the SurfaceControl on a helper thread as
+// SurfaceControl assumes its destruction happens without SurfaceFlinger::mStateLock held.
+class SurfaceControlHolder {
+public:
+ explicit SurfaceControlHolder(sp<SurfaceControl> sc) : mSurfaceControl(std::move(sc)){};
+ ~SurfaceControlHolder();
+
+ const sp<SurfaceControl>& get() const { return mSurfaceControl; }
+
+private:
+ sp<SurfaceControl> mSurfaceControl;
+};
class RefreshRateOverlay {
public:
@@ -75,7 +89,7 @@
const FpsRange mFpsRange; // For color interpolation.
const bool mShowSpinner;
- const sp<SurfaceControl> mSurfaceControl;
+ const std::unique_ptr<SurfaceControlHolder> mSurfaceControl;
};
} // namespace android
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 2487dbd..e126931 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -203,25 +203,14 @@
return 0.0f;
}
- // (b/133849373) ROT_90 screencap images produced upside down
- auto area = sample_area;
- if (orientation & ui::Transform::ROT_90) {
- area.top = height - area.top;
- area.bottom = height - area.bottom;
- std::swap(area.top, area.bottom);
-
- area.left = width - area.left;
- area.right = width - area.right;
- std::swap(area.left, area.right);
- }
-
- const uint32_t pixelCount = (area.bottom - area.top) * (area.right - area.left);
+ const uint32_t pixelCount =
+ (sample_area.bottom - sample_area.top) * (sample_area.right - sample_area.left);
uint32_t accumulatedLuma = 0;
// Calculates luma with approximation of Rec. 709 primaries
- for (int32_t row = area.top; row < area.bottom; ++row) {
+ for (int32_t row = sample_area.top; row < sample_area.bottom; ++row) {
const uint32_t* rowBase = data + row * stride;
- for (int32_t column = area.left; column < area.right; ++column) {
+ for (int32_t column = sample_area.left; column < sample_area.right; ++column) {
uint32_t pixel = rowBase[column];
const uint32_t r = pixel & 0xFF;
const uint32_t g = (pixel >> 8) & 0xFF;
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
index 747032b..4af1f5c 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
@@ -188,10 +188,10 @@
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 ca83496..a48c921 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -40,22 +40,26 @@
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 @@
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::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::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 @@
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 @@
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;
- score += 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;
+ }
}
}
- // 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.
+ // 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);
+ }();
+
+ // 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
+ // 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 @@
// 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 @@
continue;
}
- for (auto& [_, score] : scores) {
+ for (auto& [_, score, _1] : scores) {
score = 0;
}
@@ -587,7 +637,7 @@
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 @@
// 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 05a8692..a79002e 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -353,9 +353,6 @@
const Policy* getCurrentPolicyLocked() const REQUIRES(mLock);
bool isPolicyValidLocked(const Policy& policy) const REQUIRES(mLock);
- // Returns whether the layer is allowed to vote for the given refresh rate.
- bool isVoteAllowed(const LayerRequirement&, Fps) const;
-
// calculates a score for a layer. Used to determine the display refresh rate
// and the frame rate override for certains applications.
float calculateLayerScoreLocked(const LayerRequirement&, Fps refreshRate,
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 37f0fec..8b1a5d9 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -25,6 +25,7 @@
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
#include <configstore/Utils.h>
+#include <ftl/fake_guard.h>
#include <gui/WindowInfo.h>
#include <system/window.h>
#include <ui/DisplayStatInfo.h>
@@ -94,9 +95,13 @@
}
void Scheduler::setRefreshRateConfigs(std::shared_ptr<RefreshRateConfigs> configs) {
+ // The current RefreshRateConfigs instance may outlive this call, so unbind its idle timer.
{
- // The current RefreshRateConfigs instance may outlive this call, so unbind its idle timer.
- std::scoped_lock lock(mRefreshRateConfigsLock);
+ // mRefreshRateConfigsLock is not locked here to avoid the deadlock
+ // as the callback can attempt to acquire the lock before stopIdleTimer can finish
+ // the execution. It's safe to FakeGuard as main thread is the only thread that
+ // writes to the mRefreshRateConfigs.
+ ftl::FakeGuard guard(mRefreshRateConfigsLock);
if (mRefreshRateConfigs) {
mRefreshRateConfigs->stopIdleTimer();
mRefreshRateConfigs->clearIdleTimerCallbacks();
@@ -554,11 +559,12 @@
}
}
-void Scheduler::setDisplayPowerState(bool normal) {
+void Scheduler::setDisplayPowerMode(hal::PowerMode powerMode) {
{
std::lock_guard<std::mutex> lock(mPolicyLock);
- mPolicy.isDisplayPowerStateNormal = normal;
+ mPolicy.displayPowerMode = powerMode;
}
+ mVsyncSchedule->getController().setDisplayPowerMode(powerMode);
if (mDisplayPowerTimer) {
mDisplayPowerTimer->reset();
@@ -706,7 +712,8 @@
// If Display Power is not in normal operation we want to be in performance mode. When coming
// back to normal mode, a grace period is given with DisplayPowerTimer.
if (mDisplayPowerTimer &&
- (!mPolicy.isDisplayPowerStateNormal || mPolicy.displayPowerTimer == TimerState::Reset)) {
+ (mPolicy.displayPowerMode != hal::PowerMode::ON ||
+ mPolicy.displayPowerTimer == TimerState::Reset)) {
constexpr GlobalSignals kNoSignals;
return {configs->getMaxRefreshRateByPolicy(), kNoSignals};
}
@@ -720,9 +727,7 @@
DisplayModePtr Scheduler::getPreferredDisplayMode() {
std::lock_guard<std::mutex> lock(mPolicyLock);
// Make sure the stored mode is up to date.
- if (mPolicy.mode) {
- mPolicy.mode = chooseDisplayMode().first;
- }
+ mPolicy.mode = chooseDisplayMode().first;
return mPolicy.mode;
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 0c72124..a8043bf 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -186,7 +186,7 @@
// Indicates that touch interaction is taking place.
void onTouchHint();
- void setDisplayPowerState(bool normal);
+ void setDisplayPowerMode(hal::PowerMode powerMode);
VSyncDispatch& getVsyncDispatch() { return mVsyncSchedule->getDispatch(); }
@@ -325,7 +325,7 @@
TimerState idleTimer = TimerState::Reset;
TouchState touch = TouchState::Inactive;
TimerState displayPowerTimer = TimerState::Expired;
- bool isDisplayPowerStateNormal = true;
+ hal::PowerMode displayPowerMode = hal::PowerMode::ON;
// Chosen display mode.
DisplayModePtr mode;
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index bdcab51..13cd304 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -146,6 +146,11 @@
return false;
}
+ if (mDisplayPowerMode == hal::PowerMode::DOZE ||
+ mDisplayPowerMode == hal::PowerMode::DOZE_SUSPEND) {
+ return true;
+ }
+
if (!mLastHwVsync && !HwcVsyncPeriod) {
return false;
}
@@ -206,6 +211,11 @@
return mMoreSamplesNeeded;
}
+void VSyncReactor::setDisplayPowerMode(hal::PowerMode powerMode) {
+ std::scoped_lock lock(mMutex);
+ mDisplayPowerMode = powerMode;
+}
+
void VSyncReactor::dump(std::string& result) const {
std::lock_guard lock(mMutex);
StringAppendF(&result, "VsyncReactor in use\n");
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index 6a1950a..4501487 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -49,6 +49,8 @@
bool addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
bool* periodFlushed) final;
+ void setDisplayPowerMode(hal::PowerMode powerMode) final;
+
void dump(std::string& result) const final;
private:
@@ -73,6 +75,8 @@
std::optional<nsecs_t> mPeriodTransitioningTo GUARDED_BY(mMutex);
std::optional<nsecs_t> mLastHwVsync GUARDED_BY(mMutex);
+ hal::PowerMode mDisplayPowerMode GUARDED_BY(mMutex) = hal::PowerMode::ON;
+
const bool mSupportKernelIdleTimer = false;
};
diff --git a/services/surfaceflinger/Scheduler/VsyncController.h b/services/surfaceflinger/Scheduler/VsyncController.h
index 59f6537..726a420 100644
--- a/services/surfaceflinger/Scheduler/VsyncController.h
+++ b/services/surfaceflinger/Scheduler/VsyncController.h
@@ -18,7 +18,10 @@
#include <cstddef>
#include <memory>
+#include <mutex>
+#include <DisplayHardware/HWComposer.h>
+#include <DisplayHardware/Hal.h>
#include <ui/FenceTime.h>
#include <utils/Mutex.h>
#include <utils/RefBase.h>
@@ -70,6 +73,13 @@
*/
virtual void setIgnorePresentFences(bool ignore) = 0;
+ /*
+ * Sets the primary display power mode to the controller.
+ *
+ * \param [in] powerMode
+ */
+ virtual void setDisplayPowerMode(hal::PowerMode powerMode) = 0;
+
virtual void dump(std::string& result) const = 0;
protected:
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index b6fe16e..96b85d1 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -146,6 +146,14 @@
#include "TunnelModeEnabledReporter.h"
#include "WindowInfosListenerInvoker.h"
+#ifdef QCOM_UM_FAMILY
+#if __has_include("QtiGralloc.h")
+#include "QtiGralloc.h"
+#else
+#include "gralloc_priv.h"
+#endif
+#endif
+
#include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
#include <aidl/android/hardware/graphics/composer3/RenderIntent.h>
@@ -444,6 +452,11 @@
}
mIgnoreHdrCameraLayers = ignore_hdr_camera_layers(false);
+
+ // Power hint session mode, representing which hint(s) to send: early, late, or both)
+ mPowerHintSessionMode =
+ {.late = base::GetBoolProperty("debug.sf.send_late_power_session_hint"s, true),
+ .early = base::GetBoolProperty("debug.sf.send_early_power_session_hint"s, false)};
}
LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() {
@@ -550,8 +563,10 @@
}
VirtualDisplayId SurfaceFlinger::acquireVirtualDisplay(ui::Size resolution,
- ui::PixelFormat format) {
- if (auto& generator = mVirtualDisplayIdGenerators.hal) {
+ ui::PixelFormat format,
+ bool canAllocateHwcForVDS) {
+ auto& generator = mVirtualDisplayIdGenerators.hal;
+ if (canAllocateHwcForVDS && generator) {
if (const auto id = generator->generateId()) {
if (getHwComposer().allocateVirtualDisplay(*id, resolution, &format)) {
return *id;
@@ -798,9 +813,9 @@
enableLatchUnsignaledConfig = getLatchUnsignaledConfig();
- if (base::GetBoolProperty("debug.sf.enable_hwc_vds"s, false)) {
- enableHalVirtualDisplays(true);
- }
+ mAllowHwcForWFD = base::GetBoolProperty("vendor.display.vds_allow_hwc"s, false);
+ mAllowHwcForVDS = mAllowHwcForWFD && base::GetBoolProperty("debug.sf.enable_hwc_vds"s, false);
+ mFirstApiLevel = android::base::GetIntProperty("ro.product.first_api_level", 0);
// Process any initial hotplug and resulting display changes.
processDisplayHotplugEventsLocked();
@@ -1065,7 +1080,7 @@
return NO_ERROR;
}
-void SurfaceFlinger::setDesiredActiveMode(const ActiveModeInfo& info) {
+void SurfaceFlinger::setDesiredActiveMode(const ActiveModeInfo& info, bool force) {
ATRACE_CALL();
if (!info.mode) {
@@ -1078,7 +1093,7 @@
return;
}
- if (display->setDesiredActiveMode(info)) {
+ if (display->setDesiredActiveMode(info, force)) {
scheduleComposite(FrameHint::kNone);
// Start receiving vsync samples now, so that we can detect a period
@@ -1991,12 +2006,6 @@
bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expectedVsyncTime)
FTL_FAKE_GUARD(kMainThreadContext) {
- // we set this once at the beginning of commit to ensure consistency throughout the whole frame
- mPowerHintSessionData.sessionEnabled = mPowerAdvisor->usePowerHintSession();
- if (mPowerHintSessionData.sessionEnabled) {
- mPowerHintSessionData.commitStart = systemTime();
- }
-
// calculate the expected present time once and use the cached
// value throughout this frame to make sure all layers are
// seeing this same value.
@@ -2010,10 +2019,6 @@
const nsecs_t lastScheduledPresentTime = mScheduledPresentTime;
mScheduledPresentTime = expectedVsyncTime;
- if (mPowerHintSessionData.sessionEnabled) {
- mPowerAdvisor->setTargetWorkDuration(mExpectedPresentTime -
- mPowerHintSessionData.commitStart);
- }
const auto vsyncIn = [&] {
if (!ATRACE_ENABLED()) return 0.f;
return (mExpectedPresentTime - systemTime()) / 1e6f;
@@ -2089,6 +2094,32 @@
}
}
+ // Save this once per commit + composite to ensure consistency
+ // TODO (b/240619471): consider removing active display check once AOD is fixed
+ const auto activeDisplay =
+ FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(mActiveDisplayToken));
+ mPowerHintSessionEnabled = mPowerAdvisor->usePowerHintSession() && activeDisplay &&
+ activeDisplay->getPowerMode() == hal::PowerMode::ON;
+ if (mPowerHintSessionEnabled) {
+ const auto& display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()).get();
+ // get stable vsync period from display mode
+ const nsecs_t vsyncPeriod = display->getActiveMode()->getVsyncPeriod();
+ mPowerAdvisor->setCommitStart(frameTime);
+ mPowerAdvisor->setExpectedPresentTime(mExpectedPresentTime);
+ const nsecs_t idealSfWorkDuration =
+ mVsyncModulator->getVsyncConfig().sfWorkDuration.count();
+ // Frame delay is how long we should have minus how long we actually have
+ mPowerAdvisor->setFrameDelay(idealSfWorkDuration - (mExpectedPresentTime - frameTime));
+ mPowerAdvisor->setTotalFrameTargetWorkDuration(idealSfWorkDuration);
+ mPowerAdvisor->setTargetWorkDuration(vsyncPeriod);
+
+ // Send early hint here to make sure there's not another frame pending
+ if (mPowerHintSessionMode.early) {
+ // Send a rough prediction for this frame based on last frame's timing info
+ mPowerAdvisor->sendPredictedWorkDuration();
+ }
+ }
+
if (mTracingEnabledChanged) {
mLayerTracingEnabled = mLayerTracing.isEnabled();
mTracingEnabledChanged = false;
@@ -2165,16 +2196,15 @@
FTL_FAKE_GUARD(kMainThreadContext) {
ATRACE_FORMAT("%s %" PRId64, __func__, vsyncId);
- if (mPowerHintSessionData.sessionEnabled) {
- mPowerHintSessionData.compositeStart = systemTime();
- }
-
compositionengine::CompositionRefreshArgs refreshArgs;
const auto& displays = FTL_FAKE_GUARD(mStateLock, mDisplays);
refreshArgs.outputs.reserve(displays.size());
+ std::vector<DisplayId> displayIds;
for (const auto& [_, display] : displays) {
refreshArgs.outputs.push_back(display->getCompositionDisplay());
+ displayIds.push_back(display->getId());
}
+ mPowerAdvisor->setDisplays(displayIds);
mDrawingState.traverseInZOrder([&refreshArgs](Layer* layer) {
if (auto layerFE = layer->getCompositionEngineLayerFE())
refreshArgs.layers.push_back(layerFE);
@@ -2222,12 +2252,17 @@
mCompositionEngine->present(refreshArgs);
- if (mPowerHintSessionData.sessionEnabled) {
- mPowerHintSessionData.presentEnd = systemTime();
- }
-
mTimeStats->recordFrameDuration(frameTime, systemTime());
+ // Send a power hint hint after presentation is finished
+ if (mPowerHintSessionEnabled) {
+ mPowerAdvisor->setSfPresentTiming(mPreviousPresentFences[0].fenceTime->getSignalTime(),
+ systemTime());
+ if (mPowerHintSessionMode.late) {
+ mPowerAdvisor->sendActualWorkDuration();
+ }
+ }
+
if (mScheduler->onPostComposition(presentTime)) {
scheduleComposite(FrameHint::kNone);
}
@@ -2272,11 +2307,8 @@
scheduleCommit(FrameHint::kNone);
}
- // calculate total render time for performance hinting if adpf cpu hint is enabled,
- if (mPowerHintSessionData.sessionEnabled) {
- const nsecs_t flingerDuration =
- (mPowerHintSessionData.presentEnd - mPowerHintSessionData.commitStart);
- mPowerAdvisor->sendActualWorkDuration(flingerDuration, mPowerHintSessionData.presentEnd);
+ if (mPowerHintSessionEnabled) {
+ mPowerAdvisor->setCompositeEnd(systemTime());
}
}
@@ -2867,7 +2899,8 @@
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);
@@ -2900,6 +2933,11 @@
void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken,
const DisplayDeviceState& state) {
+#ifdef QCOM_UM_FAMILY
+ bool canAllocateHwcForVDS = false;
+#else
+ bool canAllocateHwcForVDS = true;
+#endif
ui::Size resolution(0, 0);
ui::PixelFormat pixelFormat = static_cast<ui::PixelFormat>(PIXEL_FORMAT_UNKNOWN);
if (state.physical) {
@@ -2914,6 +2952,20 @@
status = state.surface->query(NATIVE_WINDOW_FORMAT, &format);
ALOGE_IF(status != NO_ERROR, "Unable to query format (%d)", status);
pixelFormat = static_cast<ui::PixelFormat>(format);
+#ifdef QCOM_UM_FAMILY
+ // Check if VDS is allowed to use HWC
+ size_t maxVirtualDisplaySize = getHwComposer().getMaxVirtualDisplayDimension();
+ if (maxVirtualDisplaySize == 0 || ((uint64_t)resolution.width <= maxVirtualDisplaySize &&
+ (uint64_t)resolution.height <= maxVirtualDisplaySize)) {
+ uint64_t usage = 0;
+ // Replace with native_window_get_consumer_usage ?
+ status = state .surface->getConsumerUsage(&usage);
+ ALOGW_IF(status != NO_ERROR, "Unable to query usage (%d)", status);
+ if ((status == NO_ERROR) && canAllocateHwcDisplayIdForVDS(usage)) {
+ canAllocateHwcForVDS = true;
+ }
+ }
+#endif
} else {
// Virtual displays without a surface are dormant:
// they have external state (layer stack, projection,
@@ -2925,7 +2977,7 @@
if (const auto& physical = state.physical) {
builder.setId(physical->id);
} else {
- builder.setId(acquireVirtualDisplay(resolution, pixelFormat));
+ builder.setId(acquireVirtualDisplay(resolution, pixelFormat, canAllocateHwcForVDS));
}
builder.setPixels(resolution);
@@ -2945,7 +2997,8 @@
const auto displayId = VirtualDisplayId::tryCast(compositionDisplay->getId());
LOG_FATAL_IF(!displayId);
auto surface = sp<VirtualDisplaySurface>::make(getHwComposer(), *displayId, state.surface,
- bqProducer, bqConsumer, state.displayName);
+ bqProducer, bqConsumer, state.displayName,
+ state.isSecure);
displaySurface = surface;
producer = std::move(surface);
} else {
@@ -3310,11 +3363,11 @@
mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
if (!layer->needsInputInfo()) return;
- // Do not create WindowInfos for windows on displays that cannot receive input.
- if (const auto opt = displayInputInfos.get(layer->getLayerStack())) {
- const auto& info = opt->get();
- outWindowInfos.push_back(layer->fillInputInfo(info.transform, info.isSecure));
- }
+ const auto opt = displayInputInfos.get(layer->getLayerStack(),
+ [](const auto& info) -> Layer::InputDisplayArgs {
+ return {&info.transform, info.isSecure};
+ });
+ outWindowInfos.push_back(layer->fillInputInfo(opt.value_or(Layer::InputDisplayArgs{})));
});
sNumWindowInfos = outWindowInfos.size();
@@ -3349,8 +3402,14 @@
}
ATRACE_CALL();
+ if (display->isInternal() && !isDisplayActiveLocked(display)) {
+ ALOGV("%s(%s): Inactive display", __func__, to_string(display->getId()).c_str());
+ return;
+ }
+
if (!display->refreshRateConfigs().isModeAllowed(mode->getId())) {
- ALOGV("Skipping disallowed mode %d", mode->getId().value());
+ ALOGV("%s(%s): Disallowed mode %d", __func__, to_string(display->getId()).c_str(),
+ mode->getId().value());
return;
}
@@ -4849,8 +4908,8 @@
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;
}
@@ -4866,7 +4925,7 @@
mInterceptor->savePowerModeUpdate(display->getSequenceId(), static_cast<int32_t>(mode));
}
const auto refreshRate = display->refreshRateConfigs().getActiveMode()->getFps();
- if (currentMode == hal::PowerMode::OFF) {
+ if (!currentMode || *currentMode == hal::PowerMode::OFF) {
// Turn on the display
if (display->isInternal() && (!activeDisplay || !activeDisplay->isPoweredOn())) {
onActiveDisplayChangedLocked(display);
@@ -4897,7 +4956,7 @@
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);
}
@@ -4911,7 +4970,7 @@
} 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);
}
@@ -4930,7 +4989,7 @@
if (isDisplayActiveLocked(display)) {
mTimeStats->setPowerMode(mode);
mRefreshRateStats->setPowerMode(mode);
- mScheduler->setDisplayPowerState(mode == hal::PowerMode::ON);
+ mScheduler->setDisplayPowerMode(mode);
}
ALOGD("Finished setting power mode %d on display %s", mode, to_string(displayId).c_str());
@@ -4983,6 +5042,25 @@
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__);
@@ -4995,7 +5073,7 @@
(it->second)(args, asProto, result);
dumpLayers = false;
} else if (!asProto) {
- dumpAllLocked(args, result);
+ dumpAllLocked(args, compositionLayers, result);
}
}
@@ -5294,7 +5372,8 @@
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);
@@ -5345,18 +5424,7 @@
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());
@@ -6621,8 +6689,13 @@
1 /* layerCount */, usage, "screenshot");
const status_t bufferStatus = buffer->initCheck();
- LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "captureScreenCommon: Buffer failed to allocate: %d",
- bufferStatus);
+ if (bufferStatus != OK) {
+ // Animations may end up being really janky, but don't crash here.
+ // Otherwise an irreponsible process may cause an SF crash by allocating
+ // too much.
+ ALOGE("%s: Buffer failed to allocate: %d", __func__, bufferStatus);
+ return ftl::yield<FenceResult>(base::unexpected(bufferStatus)).share();
+ }
const std::shared_ptr<renderengine::ExternalTexture> texture = std::make_shared<
renderengine::impl::ExternalTexture>(buffer, getRenderEngine(),
renderengine::impl::ExternalTexture::Usage::
@@ -6645,8 +6718,10 @@
std::unique_ptr<RenderArea> renderArea = renderAreaFuture.get();
if (!renderArea) {
ALOGW("Skipping screen capture because of invalid render area.");
- captureResults.result = NO_MEMORY;
- captureListener->onScreenCaptureCompleted(captureResults);
+ if (captureListener) {
+ captureResults.result = NO_MEMORY;
+ captureListener->onScreenCaptureCompleted(captureResults);
+ }
return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share();
}
@@ -6896,9 +6971,7 @@
return NO_ERROR;
}
- status_t setPolicyResult = overridePolicy
- ? display->refreshRateConfigs().setOverridePolicy(policy)
- : display->refreshRateConfigs().setDisplayManagerPolicy(*policy);
+ const status_t setPolicyResult = display->setRefreshRatePolicy(policy, overridePolicy);
if (setPolicyResult < 0) {
return BAD_VALUE;
}
@@ -6906,9 +6979,19 @@
return NO_ERROR;
}
- scheduler::RefreshRateConfigs::Policy currentPolicy =
- display->refreshRateConfigs().getCurrentPolicy();
+ if (display->isInternal() && !isDisplayActiveLocked(display)) {
+ // The policy will be be applied when the display becomes active.
+ ALOGV("%s(%s): Inactive display", __func__, to_string(display->getId()).c_str());
+ return NO_ERROR;
+ }
+ return applyRefreshRateConfigsPolicy(display);
+}
+
+status_t SurfaceFlinger::applyRefreshRateConfigsPolicy(const sp<DisplayDevice>& display,
+ bool force) {
+ const scheduler::RefreshRateConfigs::Policy currentPolicy =
+ display->refreshRateConfigs().getCurrentPolicy();
ALOGV("Setting desired display mode specs: %s", currentPolicy.toString().c_str());
// TODO(b/140204874): Leave the event in until we do proper testing with all apps that might
@@ -6936,7 +7019,7 @@
if (display->refreshRateConfigs().isModeAllowed(preferredDisplayMode->getId())) {
ALOGV("switching to Scheduler preferred display mode %d",
preferredDisplayMode->getId().value());
- setDesiredActiveMode({preferredDisplayMode, DisplayModeEvent::Changed});
+ setDesiredActiveMode({preferredDisplayMode, DisplayModeEvent::Changed}, force);
} else {
LOG_ALWAYS_FATAL("Desired display mode not allowed: %d",
preferredDisplayMode->getId().value());
@@ -6945,6 +7028,35 @@
return NO_ERROR;
}
+#ifdef QCOM_UM_FAMILY
+bool SurfaceFlinger::canAllocateHwcDisplayIdForVDS(uint64_t usage) {
+ uint64_t flag_mask_pvt_wfd = ~0;
+ uint64_t flag_mask_hw_video = ~0;
+ // Reserve hardware acceleration for WFD use-case
+ // GRALLOC_USAGE_PRIVATE_WFD + GRALLOC_USAGE_HW_VIDEO_ENCODER = WFD using HW composer.
+ flag_mask_pvt_wfd = GRALLOC_USAGE_PRIVATE_WFD;
+ flag_mask_hw_video = GRALLOC_USAGE_HW_VIDEO_ENCODER;
+ bool isWfd = (usage & flag_mask_pvt_wfd) && (usage & flag_mask_hw_video);
+ // Enabling only the vendor property would allow WFD to use HWC
+ // Enabling both the aosp and vendor properties would allow all other VDS to use HWC
+ // Disabling both would set all virtual displays to fall back to GPU
+ // In vendor frozen targets, allow WFD to use HWC without any property settings.
+ bool canAllocate = mAllowHwcForVDS || (isWfd && mAllowHwcForWFD) || (isWfd &&
+ mFirstApiLevel < __ANDROID_API_T__);
+
+ if (canAllocate) {
+ enableHalVirtualDisplays(true);
+ }
+
+ return canAllocate;
+
+}
+#else
+bool SurfaceFlinger::canAllocateHwcDisplayIdForVDS(uint64_t) {
+ return true;
+}
+#endif
+
status_t SurfaceFlinger::setDesiredDisplayModeSpecs(
const sp<IBinder>& displayToken, ui::DisplayModeId defaultMode, bool allowGroupSwitching,
float primaryRefreshRateMin, float primaryRefreshRateMax, float appRequestRefreshRateMin,
@@ -7265,14 +7377,23 @@
void SurfaceFlinger::onActiveDisplayChangedLocked(const sp<DisplayDevice>& activeDisplay) {
ATRACE_CALL();
+ // During boot, SF powers on the primary display, which is the first display to be active. In
+ // that case, there is no need to force setDesiredActiveMode, because DM is about to send its
+ // policy via setDesiredDisplayModeSpecs.
+ bool forceApplyPolicy = false;
+
if (const auto display = getDisplayDeviceLocked(mActiveDisplayToken)) {
display->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(false);
+ forceApplyPolicy = true;
}
if (!activeDisplay) {
ALOGE("%s: activeDisplay is null", __func__);
return;
}
+
+ ALOGI("Active display is %s", to_string(activeDisplay->getPhysicalId()).c_str());
+
mActiveDisplayToken = activeDisplay->getDisplayToken();
activeDisplay->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true);
updateInternalDisplayVsyncLocked(activeDisplay);
@@ -7281,9 +7402,11 @@
onActiveDisplaySizeChanged(activeDisplay);
mActiveDisplayTransformHint = activeDisplay->getTransformHint();
- // Update the kernel timer for the current active display, since the policy
- // for this display might have changed when it was not the active display.
- toggleKernelIdleTimer();
+ // The policy of the new active/leader display may have changed while it was inactive. In that
+ // case, its preferred mode has not been propagated to HWC (via setDesiredActiveMode). In either
+ // case, the Scheduler's cachedModeChangedParams must be initialized to the newly active mode,
+ // and the kernel idle timer of the newly active display must be toggled.
+ applyRefreshRateConfigsPolicy(activeDisplay, forceApplyPolicy);
}
status_t SurfaceFlinger::addWindowInfosListener(
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index dcf3be8..41ad635 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -712,7 +712,7 @@
// Called on the main thread in response to initializeDisplays()
void onInitializeDisplays() REQUIRES(mStateLock);
// Sets the desired active mode bit. It obtains the lock, and sets mDesiredActiveMode.
- void setDesiredActiveMode(const ActiveModeInfo& info) REQUIRES(mStateLock);
+ void setDesiredActiveMode(const ActiveModeInfo& info, bool force = false) REQUIRES(mStateLock);
status_t setActiveModeFromBackdoor(const sp<IBinder>& displayToken, int id);
// Sets the active mode and a new refresh rate in SF.
void updateInternalStateWithChangedMode() REQUIRES(mStateLock);
@@ -735,6 +735,9 @@
const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy)
EXCLUDES(mStateLock);
+ status_t applyRefreshRateConfigsPolicy(const sp<DisplayDevice>&, bool force = false)
+ REQUIRES(mStateLock);
+
void commitTransactions() EXCLUDES(mStateLock);
void commitTransactionsLocked(uint32_t transactionFlags) REQUIRES(mStateLock);
void doCommitTransactions() REQUIRES(mStateLock);
@@ -878,6 +881,8 @@
const std::shared_ptr<renderengine::ExternalTexture>&, bool canCaptureBlackoutContent,
bool regionSampling, bool grayscale, ScreenCaptureResults&) EXCLUDES(mStateLock);
+ bool canAllocateHwcDisplayIdForVDS(uint64_t usage);
+
// If the uid provided is not UNSET_UID, the traverse will skip any layers that don't have a
// matching ownerUid
void traverseLayersInLayerStack(ui::LayerStack, const int32_t uid, const LayerVector::Visitor&);
@@ -1075,7 +1080,8 @@
void enableHalVirtualDisplays(bool);
// Virtual display lifecycle for ID generation and HAL allocation.
- VirtualDisplayId acquireVirtualDisplay(ui::Size, ui::PixelFormat) REQUIRES(mStateLock);
+ VirtualDisplayId acquireVirtualDisplay(ui::Size, ui::PixelFormat, bool canAllocateHwcForVDS)
+ REQUIRES(mStateLock);
void releaseVirtualDisplay(VirtualDisplayId);
void onActiveDisplayChangedLocked(const sp<DisplayDevice>& activeDisplay) REQUIRES(mStateLock);
@@ -1085,7 +1091,8 @@
/*
* 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;
@@ -1429,18 +1436,21 @@
const sp<WindowInfosListenerInvoker> mWindowInfosListenerInvoker;
FlagManager mFlagManager;
+ bool mAllowHwcForVDS = false;
+ bool mAllowHwcForWFD = false;
+ int mFirstApiLevel = 0;
// returns the framerate of the layer with the given sequence ID
float getLayerFramerate(nsecs_t now, int32_t id) const {
return mScheduler->getLayerFramerate(now, id);
}
+ bool mPowerHintSessionEnabled;
+
struct {
- bool sessionEnabled = false;
- nsecs_t commitStart;
- nsecs_t compositeStart;
- nsecs_t presentEnd;
- } mPowerHintSessionData GUARDED_BY(kMainThreadContext);
+ bool late = false;
+ bool early = false;
+ } mPowerHintSessionMode;
nsecs_t mAnimationTransactionTimeout = s2ns(5);
diff --git a/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp b/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp
index 9ab35d7..53de4a6 100644
--- a/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp
+++ b/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp
@@ -52,6 +52,8 @@
void verifyAndClearExpectations();
void sendActualWorkDurationGroup(std::vector<WorkDuration> durations,
std::chrono::nanoseconds sleepBeforeLastSend);
+ std::chrono::nanoseconds mAllowedDeviation;
+ std::chrono::nanoseconds mStaleTimeout;
};
void AidlPowerHalWrapperTest::SetUp() {
@@ -59,6 +61,9 @@
mMockSession = new NiceMock<MockIPowerHintSession>();
ON_CALL(*mMockHal.get(), getHintSessionPreferredRate(_)).WillByDefault(Return(Status::ok()));
mWrapper = std::make_unique<AidlPowerHalWrapper>(mMockHal);
+ mWrapper->setAllowedActualDeviation(std::chrono::nanoseconds{10ms}.count());
+ mAllowedDeviation = std::chrono::nanoseconds{mWrapper->mAllowedActualDeviation};
+ mStaleTimeout = AidlPowerHalWrapper::kStaleTimeout;
}
void AidlPowerHalWrapperTest::verifyAndClearExpectations() {
@@ -76,6 +81,7 @@
mWrapper->sendActualWorkDuration(duration.durationNanos, duration.timeStampNanos);
}
}
+
WorkDuration toWorkDuration(std::chrono::nanoseconds durationNanos, int64_t timeStampNanos) {
WorkDuration duration;
duration.durationNanos = durationNanos.count();
@@ -83,6 +89,10 @@
return duration;
}
+WorkDuration toWorkDuration(std::pair<std::chrono::nanoseconds, nsecs_t> timePair) {
+ return toWorkDuration(timePair.first, timePair.second);
+}
+
std::string printWorkDurations(const ::std::vector<WorkDuration>& durations) {
std::ostringstream os;
for (auto duration : durations) {
@@ -112,7 +122,7 @@
EXPECT_FALSE(mWrapper->startPowerHintSession());
}
-TEST_F(AidlPowerHalWrapperTest, restartNewPoserHintSessionWithNewThreadIds) {
+TEST_F(AidlPowerHalWrapperTest, restartNewPowerHintSessionWithNewThreadIds) {
ASSERT_TRUE(mWrapper->supportsPowerHintSession());
std::vector<int32_t> threadIds = {1, 2};
@@ -149,12 +159,8 @@
std::chrono::nanoseconds base = 100ms;
// test cases with target work duration and whether it should update hint against baseline 100ms
- const std::vector<std::pair<std::chrono::nanoseconds, bool>> testCases = {{0ms, false},
- {-1ms, false},
- {200ms, true},
- {2ms, true},
- {91ms, false},
- {109ms, false}};
+ const std::vector<std::pair<std::chrono::nanoseconds, bool>> testCases =
+ {{0ms, true}, {-1ms, true}, {200ms, true}, {2ms, true}, {100ms, false}, {109ms, true}};
for (const auto& test : testCases) {
// reset to 100ms baseline
@@ -200,21 +206,21 @@
// 100ms
const std::vector<std::pair<std::vector<std::pair<std::chrono::nanoseconds, nsecs_t>>, bool>>
testCases = {{{{-1ms, 100}}, false},
- {{{91ms, 100}}, false},
- {{{109ms, 100}}, false},
+ {{{100ms - (mAllowedDeviation / 2), 100}}, false},
+ {{{100ms + (mAllowedDeviation / 2), 100}}, false},
+ {{{100ms + (mAllowedDeviation + 1ms), 100}}, true},
+ {{{100ms - (mAllowedDeviation + 1ms), 100}}, true},
{{{100ms, 100}, {200ms, 200}}, true},
{{{100ms, 500}, {100ms, 600}, {3ms, 600}}, true}};
for (const auto& test : testCases) {
// reset actual duration
- sendActualWorkDurationGroup({base}, 80ms);
+ sendActualWorkDurationGroup({base}, mStaleTimeout);
auto raw = test.first;
std::vector<WorkDuration> durations(raw.size());
std::transform(raw.begin(), raw.end(), durations.begin(),
- [](std::pair<std::chrono::nanoseconds, nsecs_t> d) {
- return toWorkDuration(d.first, d.second);
- });
+ [](auto d) { return toWorkDuration(d); });
EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(durations))
.Times(test.second ? 1 : 0);
sendActualWorkDurationGroup(durations, 0ms);
@@ -222,40 +228,6 @@
}
}
-TEST_F(AidlPowerHalWrapperTest, sendAdjustedActualWorkDuration) {
- ASSERT_TRUE(mWrapper->supportsPowerHintSession());
-
- std::vector<int32_t> threadIds = {1, 2};
- mWrapper->setPowerHintSessionThreadIds(threadIds);
- EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _))
- .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok())));
- ASSERT_TRUE(mWrapper->startPowerHintSession());
- verifyAndClearExpectations();
-
- std::chrono::nanoseconds lastTarget = 100ms;
- EXPECT_CALL(*mMockSession.get(), updateTargetWorkDuration(lastTarget.count())).Times(1);
- mWrapper->setTargetWorkDuration(lastTarget.count());
- std::chrono::nanoseconds newTarget = 105ms;
- mWrapper->setTargetWorkDuration(newTarget.count());
- EXPECT_CALL(*mMockSession.get(), updateTargetWorkDuration(newTarget.count())).Times(0);
- std::chrono::nanoseconds actual = 21ms;
- // 100 / 105 * 21ms = 20ms
- std::chrono::nanoseconds expectedActualSent = 20ms;
- std::vector<WorkDuration> expectedDurations = {toWorkDuration(expectedActualSent, 1)};
-
- EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(_))
- .WillOnce(DoAll(
- [expectedDurations](const ::std::vector<WorkDuration>& durationsSent) {
- EXPECT_EQ(expectedDurations, durationsSent)
- << base::StringPrintf("actual sent: %s vs expected: %s",
- printWorkDurations(durationsSent).c_str(),
- printWorkDurations(expectedDurations)
- .c_str());
- },
- Return(Status::ok())));
- mWrapper->sendActualWorkDuration(actual.count(), 1);
-}
-
TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration_exceedsStaleTime) {
ASSERT_TRUE(mWrapper->supportsPowerHintSession());
@@ -269,22 +241,23 @@
auto base = toWorkDuration(100ms, 0);
// test cases with actual work durations and whether it should update hint against baseline
// 100ms
- const std::vector<std::pair<std::vector<std::pair<std::chrono::nanoseconds, nsecs_t>>, bool>>
- testCases = {{{{91ms, 100}}, true}, {{{109ms, 100}}, true}};
+ const std::vector<std::tuple<std::vector<std::pair<std::chrono::nanoseconds, nsecs_t>>,
+ std::chrono::nanoseconds, bool>>
+ testCases = {{{{100ms, 100}}, mStaleTimeout, true},
+ {{{100ms + (mAllowedDeviation / 2), 100}}, mStaleTimeout, true},
+ {{{100ms, 100}}, mStaleTimeout / 2, false}};
for (const auto& test : testCases) {
// reset actual duration
- sendActualWorkDurationGroup({base}, 80ms);
+ sendActualWorkDurationGroup({base}, mStaleTimeout);
- auto raw = test.first;
+ auto raw = std::get<0>(test);
std::vector<WorkDuration> durations(raw.size());
std::transform(raw.begin(), raw.end(), durations.begin(),
- [](std::pair<std::chrono::nanoseconds, nsecs_t> d) {
- return toWorkDuration(d.first, d.second);
- });
+ [](auto d) { return toWorkDuration(d); });
EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(durations))
- .Times(test.second ? 1 : 0);
- sendActualWorkDurationGroup(durations, 80ms);
+ .Times(std::get<2>(test) ? 1 : 0);
+ sendActualWorkDurationGroup(durations, std::get<1>(test));
verifyAndClearExpectations();
}
}
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 7823363..004f31c 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -24,6 +24,7 @@
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 @@
"LayerTest.cpp",
"LayerTestUtils.cpp",
"MessageQueueTest.cpp",
+ "PowerAdvisorTest.cpp",
"SurfaceFlinger_CreateDisplayTest.cpp",
"SurfaceFlinger_DestroyDisplayTest.cpp",
"SurfaceFlinger_DisplayModeSwitching.cpp",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index bbfedc7..fbc532e 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -664,7 +664,8 @@
EXPECT_EQ(false, layer.source.buffer.isY410BT2020);
EXPECT_EQ(true, layer.source.buffer.usePremultipliedAlpha);
EXPECT_EQ(false, layer.source.buffer.isOpaque);
- EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
+ EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.x);
+ EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.y);
EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
EXPECT_EQ(LayerProperties::COLOR[3], layer.alpha);
return resultFuture;
@@ -714,7 +715,8 @@
EXPECT_EQ(half3(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
LayerProperties::COLOR[2]),
layer.source.solidColor);
- EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
+ EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.x);
+ EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.y);
EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
EXPECT_EQ(LayerProperties::COLOR[3], layer.alpha);
return resultFuture;
@@ -792,7 +794,8 @@
const renderengine::LayerSettings layer = layerSettings.back();
EXPECT_THAT(layer.source.buffer.buffer, IsNull());
EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), layer.source.solidColor);
- EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
+ EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.x);
+ EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.y);
EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
EXPECT_EQ(1.0f, layer.alpha);
return resultFuture;
diff --git a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
index ec27eda..67ace1a 100644
--- a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
@@ -292,9 +292,10 @@
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 @@
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/DisplayDevice_SetDisplayBrightnessTest.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_SetDisplayBrightnessTest.cpp
index 225ad16..ac5e927 100644
--- a/services/surfaceflinger/tests/unittests/DisplayDevice_SetDisplayBrightnessTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayDevice_SetDisplayBrightnessTest.cpp
@@ -96,5 +96,23 @@
EXPECT_EQ(std::nullopt, displayDevice->getCompositionDisplay()->getState().displayBrightness);
}
+TEST_F(SetDisplayBrightnessTest, firstDisplayBrightnessWithComposite) {
+ ftl::FakeGuard guard(kMainThreadContext);
+ sp<DisplayDevice> displayDevice = getDisplayDevice();
+
+ EXPECT_EQ(std::nullopt, displayDevice->getStagedBrightness());
+
+ constexpr float kDisplayBrightness = -1.0f;
+ displayDevice->stageBrightness(kDisplayBrightness);
+
+ EXPECT_EQ(-1.0f, displayDevice->getStagedBrightness());
+
+ displayDevice->persistBrightness(true);
+
+ EXPECT_EQ(std::nullopt, displayDevice->getStagedBrightness());
+ EXPECT_EQ(kDisplayBrightness,
+ displayDevice->getCompositionDisplay()->getState().displayBrightness);
+}
+
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index f04221c..5ddd1c8 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -120,51 +120,6 @@
});
}
-sp<DisplayDevice> DisplayTransactionTest::injectDefaultInternalDisplay(
- std::function<void(FakeDisplayDeviceInjector&)> injectExtra) {
- constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(255u);
- constexpr int DEFAULT_DISPLAY_WIDTH = 1080;
- constexpr int DEFAULT_DISPLAY_HEIGHT = 1920;
- constexpr HWDisplayId DEFAULT_DISPLAY_HWC_DISPLAY_ID = 0;
-
- // The DisplayDevice is required to have a framebuffer (behind the
- // ANativeWindow interface) which uses the actual hardware display
- // size.
- EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_WIDTH), Return(0)));
- EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_HEIGHT), Return(0)));
- EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT));
- EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT));
- EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64));
- EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(AnyNumber());
-
- auto compositionDisplay =
- compositionengine::impl::createDisplay(mFlinger.getCompositionEngine(),
- compositionengine::DisplayCreationArgsBuilder()
- .setId(DEFAULT_DISPLAY_ID)
- .setPixels({DEFAULT_DISPLAY_WIDTH,
- DEFAULT_DISPLAY_HEIGHT})
- .setPowerAdvisor(&mPowerAdvisor)
- .build());
-
- constexpr bool kIsPrimary = true;
- auto injector = FakeDisplayDeviceInjector(mFlinger, compositionDisplay,
- ui::DisplayConnectionType::Internal,
- DEFAULT_DISPLAY_HWC_DISPLAY_ID, kIsPrimary);
-
- injector.setNativeWindow(mNativeWindow);
- if (injectExtra) {
- injectExtra(injector);
- }
-
- auto displayDevice = injector.inject();
-
- Mock::VerifyAndClear(mNativeWindow.get());
-
- return displayDevice;
-}
-
bool DisplayTransactionTest::hasPhysicalHwcDisplay(HWDisplayId hwcDisplayId) const {
return mFlinger.hwcPhysicalDisplayIdMap().count(hwcDisplayId) == 1;
}
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index f5235ce..60f773f 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -42,6 +42,7 @@
#include <renderengine/mock/RenderEngine.h>
#include <ui/DebugUtils.h>
+#include "FakeDisplayInjector.h"
#include "TestableScheduler.h"
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockComposer.h"
@@ -90,7 +91,9 @@
void injectFakeBufferQueueFactory();
void injectFakeNativeWindowSurfaceFactory();
sp<DisplayDevice> injectDefaultInternalDisplay(
- std::function<void(TestableSurfaceFlinger::FakeDisplayDeviceInjector&)>);
+ std::function<void(TestableSurfaceFlinger::FakeDisplayDeviceInjector&)> injectExtra) {
+ return mFakeDisplayInjector.injectInternalDisplay(injectExtra);
+ }
// --------------------------------------------------------------------
// Postcondition helpers
@@ -115,6 +118,8 @@
sp<GraphicBuffer> mBuffer = new GraphicBuffer();
Hwc2::mock::PowerAdvisor mPowerAdvisor;
+ FakeDisplayInjector mFakeDisplayInjector{mFlinger, mPowerAdvisor, mNativeWindow};
+
// These mocks are created by the test, but are destroyed by SurfaceFlinger
// by virtue of being stored into a std::unique_ptr. However we still need
// to keep a reference to them for use in setting up call expectations.
diff --git a/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h b/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h
new file mode 100644
index 0000000..6e4bf2b
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockPowerAdvisor.h"
+#include "mock/system/window/MockNativeWindow.h"
+
+namespace android {
+
+using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
+using android::hardware::graphics::composer::hal::HWDisplayId;
+using android::Hwc2::mock::PowerAdvisor;
+
+struct FakeDisplayInjectorArgs {
+ PhysicalDisplayId displayId = PhysicalDisplayId::fromPort(255u);
+ HWDisplayId hwcDisplayId = 0;
+ bool isPrimary = true;
+};
+
+class FakeDisplayInjector {
+public:
+ FakeDisplayInjector(TestableSurfaceFlinger& flinger, Hwc2::mock::PowerAdvisor& powerAdvisor,
+ sp<mock::NativeWindow> nativeWindow)
+ : mFlinger(flinger), mPowerAdvisor(powerAdvisor), mNativeWindow(nativeWindow) {}
+
+ sp<DisplayDevice> injectInternalDisplay(
+ const std::function<void(FakeDisplayDeviceInjector&)>& injectExtra,
+ FakeDisplayInjectorArgs args = {}) {
+ using testing::_;
+ using testing::AnyNumber;
+ using testing::DoAll;
+ using testing::Mock;
+ using testing::Return;
+ using testing::SetArgPointee;
+
+ constexpr ui::Size kResolution = {1080, 1920};
+
+ // The DisplayDevice is required to have a framebuffer (behind the
+ // ANativeWindow interface) which uses the actual hardware display
+ // size.
+ EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(kResolution.getWidth()), Return(0)));
+ EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(kResolution.getHeight()), Return(0)));
+ EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT));
+ EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT));
+ EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64));
+ EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(AnyNumber());
+
+ auto compositionDisplay = compositionengine::impl::
+ createDisplay(mFlinger.getCompositionEngine(),
+ compositionengine::DisplayCreationArgsBuilder()
+ .setId(args.displayId)
+ .setPixels(kResolution)
+ .setPowerAdvisor(&mPowerAdvisor)
+ .build());
+
+ auto injector = FakeDisplayDeviceInjector(mFlinger, compositionDisplay,
+ ui::DisplayConnectionType::Internal,
+ args.hwcDisplayId, args.isPrimary);
+
+ injector.setNativeWindow(mNativeWindow);
+ if (injectExtra) {
+ injectExtra(injector);
+ }
+
+ auto displayDevice = injector.inject();
+
+ Mock::VerifyAndClear(mNativeWindow.get());
+
+ return displayDevice;
+ }
+
+ TestableSurfaceFlinger& mFlinger;
+ Hwc2::mock::PowerAdvisor& mPowerAdvisor;
+ sp<mock::NativeWindow> mNativeWindow;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
new file mode 100644
index 0000000..8711a42
--- /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 fcde532..188fd58 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -564,9 +564,10 @@
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 @@
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/RegionSamplingTest.cpp b/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp
index f19e554..409e1ef 100644
--- a/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp
@@ -106,40 +106,6 @@
testing::Eq(0.0));
}
-// workaround for b/133849373
-TEST_F(RegionSamplingTest, orientation_90) {
- std::generate(buffer.begin(), buffer.end(),
- [n = 0]() mutable { return (n++ > (kStride * kHeight >> 1)) ? kBlack : kWhite; });
-
- Rect tl_region{0, 0, 4, 4};
- EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_0,
- tl_region),
- testing::Eq(1.0));
- EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_180,
- tl_region),
- testing::Eq(1.0));
- EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_90,
- tl_region),
- testing::Eq(0.0));
- EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_270,
- tl_region),
- testing::Eq(0.0));
-
- Rect br_region{kWidth - 4, kHeight - 4, kWidth, kHeight};
- EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_0,
- br_region),
- testing::Eq(0.0));
- EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_180,
- br_region),
- testing::Eq(0.0));
- EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_90,
- br_region),
- testing::Eq(1.0));
- EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_270,
- br_region),
- testing::Eq(1.0));
-}
-
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index aab2795..93c809e 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -159,8 +159,8 @@
mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
- constexpr bool kPowerStateNormal = true;
- mScheduler->setDisplayPowerState(kPowerStateNormal);
+ constexpr hal::PowerMode kPowerModeOn = hal::PowerMode::ON;
+ mScheduler->setDisplayPowerMode(kPowerModeOn);
constexpr uint32_t kDisplayArea = 999'999;
mScheduler->onActiveDisplayAreaChanged(kDisplayArea);
@@ -226,8 +226,8 @@
mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
- constexpr bool kPowerStateNormal = true;
- mScheduler->setDisplayPowerState(kPowerStateNormal);
+ constexpr hal::PowerMode kPowerModeOn = hal::PowerMode::ON;
+ mScheduler->setDisplayPowerMode(kPowerModeOn);
constexpr uint32_t kDisplayArea = 999'999;
mScheduler->onActiveDisplayAreaChanged(kDisplayArea);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index 5872a47..b58add8 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-#include "mock/MockEventThread.h"
#undef LOG_TAG
#define LOG_TAG "LibSurfaceFlingerUnittests"
#include "DisplayTransactionTestHelpers.h"
+#include "mock/DisplayHardware/MockDisplayMode.h"
#include <scheduler/Fps.h>
@@ -42,14 +42,9 @@
mFlinger.onComposerHalHotplug(PrimaryDisplayVariant::HWC_DISPLAY_ID, Connection::CONNECTED);
- {
- DisplayModes modes = makeModes(kMode60, kMode90, kMode120, kMode90_4K);
- auto configs = std::make_shared<scheduler::RefreshRateConfigs>(modes, kModeId60);
-
- mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
- .setDisplayModes(std::move(modes), kModeId60, std::move(configs))
- .inject();
- }
+ mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
+ .setDisplayModes(kModes, kModeId60)
+ .inject();
setupScheduler(mDisplay->holdRefreshRateConfigs());
@@ -77,6 +72,8 @@
static constexpr ui::Size kResolution4K{3840, 2160};
static inline const DisplayModePtr kMode90_4K =
createDisplayMode(kModeId90_4K, 90_Hz, 3, kResolution4K);
+
+ static inline const DisplayModes kModes = makeModes(kMode60, kMode90, kMode120, kMode90_4K);
};
void DisplayModeSwitchingTest::setupScheduler(
@@ -265,5 +262,112 @@
ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId90_4K);
}
+TEST_F(DisplayModeSwitchingTest, multiDisplay) {
+ constexpr HWDisplayId kInnerDisplayHwcId = PrimaryDisplayVariant::HWC_DISPLAY_ID;
+ constexpr HWDisplayId kOuterDisplayHwcId = kInnerDisplayHwcId + 1;
+
+ constexpr PhysicalDisplayId kOuterDisplayId = PhysicalDisplayId::fromPort(254u);
+
+ constexpr bool kIsPrimary = false;
+ TestableSurfaceFlinger::FakeHwcDisplayInjector(kOuterDisplayId, hal::DisplayType::PHYSICAL,
+ kIsPrimary)
+ .setHwcDisplayId(kOuterDisplayHwcId)
+ .inject(&mFlinger, mComposer);
+
+ const auto outerDisplay = mFakeDisplayInjector.injectInternalDisplay(
+ [&](FakeDisplayDeviceInjector& injector) {
+ injector.setDisplayModes(mock::cloneForDisplay(kOuterDisplayId, kModes),
+ kModeId120);
+ },
+ {.displayId = kOuterDisplayId,
+ .hwcDisplayId = kOuterDisplayHwcId,
+ .isPrimary = kIsPrimary});
+
+ const auto& innerDisplay = mDisplay;
+
+ EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
+ EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
+
+ EXPECT_EQ(innerDisplay->getActiveMode()->getId(), kModeId60);
+ EXPECT_EQ(outerDisplay->getActiveMode()->getId(), kModeId120);
+
+ mFlinger.onActiveDisplayChanged(innerDisplay);
+
+ EXPECT_EQ(NO_ERROR,
+ mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(),
+ kModeId90.value(), false, 0.f, 120.f, 0.f,
+ 120.f));
+
+ EXPECT_EQ(NO_ERROR,
+ mFlinger.setDesiredDisplayModeSpecs(outerDisplay->getDisplayToken().promote(),
+ kModeId60.value(), false, 0.f, 120.f, 0.f,
+ 120.f));
+
+ // Transition on the inner display.
+ ASSERT_TRUE(innerDisplay->getDesiredActiveMode());
+ EXPECT_EQ(innerDisplay->getDesiredActiveMode()->mode->getId(), kModeId90);
+
+ // No transition on the outer display.
+ EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
+
+ const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
+ EXPECT_CALL(*mComposer,
+ setActiveConfigWithConstraints(kInnerDisplayHwcId,
+ hal::HWConfigId(kModeId90.value()), _, _))
+ .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+
+ mFlinger.commit();
+
+ // Transition on the inner display.
+ ASSERT_TRUE(innerDisplay->getDesiredActiveMode());
+ EXPECT_EQ(innerDisplay->getDesiredActiveMode()->mode->getId(), kModeId90);
+
+ // No transition on the outer display.
+ EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
+
+ mFlinger.commit();
+
+ // Transition on the inner display.
+ EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
+ EXPECT_EQ(innerDisplay->getActiveMode()->getId(), kModeId90);
+
+ // No transition on the outer display.
+ EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
+ EXPECT_EQ(outerDisplay->getActiveMode()->getId(), kModeId120);
+
+ mFlinger.onActiveDisplayChanged(outerDisplay);
+
+ // No transition on the inner display.
+ EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
+
+ // Transition on the outer display.
+ ASSERT_TRUE(outerDisplay->getDesiredActiveMode());
+ EXPECT_EQ(outerDisplay->getDesiredActiveMode()->mode->getId(), kModeId60);
+
+ EXPECT_CALL(*mComposer,
+ setActiveConfigWithConstraints(kOuterDisplayHwcId,
+ hal::HWConfigId(kModeId60.value()), _, _))
+ .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+
+ mFlinger.commit();
+
+ // No transition on the inner display.
+ EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
+
+ // Transition on the outer display.
+ ASSERT_TRUE(outerDisplay->getDesiredActiveMode());
+ EXPECT_EQ(outerDisplay->getDesiredActiveMode()->mode->getId(), kModeId60);
+
+ mFlinger.commit();
+
+ // No transition on the inner display.
+ EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
+ EXPECT_EQ(innerDisplay->getActiveMode()->getId(), kModeId90);
+
+ // Transition on the outer display.
+ EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
+ EXPECT_EQ(outerDisplay->getActiveMode()->getId(), kModeId60);
+}
+
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
index 8de9e4b..2c9888d 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
@@ -74,6 +74,7 @@
mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
mFlinger.setupTimeStats(std::shared_ptr<TimeStats>(mTimeStats));
mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
+ mFlinger.setPowerHintSessionMode(true, true);
mFlinger.setupPowerAdvisor(std::unique_ptr<Hwc2::PowerAdvisor>(mPowerAdvisor));
static constexpr bool kIsPrimary = true;
FakeHwcDisplayInjector(DEFAULT_DISPLAY_ID, hal::DisplayType::PHYSICAL, kIsPrimary)
@@ -96,6 +97,7 @@
.setNativeWindow(mNativeWindow)
.setPowerMode(hal::PowerMode::ON)
.inject();
+ mFlinger.mutableActiveDisplayToken() = mDisplay->getDisplayToken();
}
void SurfaceFlingerPowerHintTest::setupScheduler() {
@@ -142,10 +144,30 @@
std::this_thread::sleep_for(mockHwcRunTime);
return hardware::graphics::composer::V2_1::Error::NONE;
});
- EXPECT_CALL(*mPowerAdvisor,
- sendActualWorkDuration(Gt(mockHwcRunTime.count()),
- Gt(now + mockHwcRunTime.count())))
+ EXPECT_CALL(*mPowerAdvisor, sendActualWorkDuration()).Times(1);
+ static constexpr bool kVsyncId = 123; // arbitrary
+ mFlinger.commitAndComposite(now, kVsyncId, now + mockVsyncPeriod.count());
+}
+
+TEST_F(SurfaceFlingerPowerHintTest, inactiveOnDisplayDoze) {
+ ON_CALL(*mPowerAdvisor, usePowerHintSession()).WillByDefault(Return(true));
+
+ mDisplay->setPowerMode(hal::PowerMode::DOZE);
+
+ const std::chrono::nanoseconds mockVsyncPeriod = 15ms;
+ EXPECT_CALL(*mPowerAdvisor, setTargetWorkDuration(_)).Times(0);
+
+ const nsecs_t now = systemTime();
+ const std::chrono::nanoseconds mockHwcRunTime = 20ms;
+ EXPECT_CALL(*mDisplaySurface,
+ prepareFrame(compositionengine::DisplaySurface::CompositionType::Hwc))
.Times(1);
+ EXPECT_CALL(*mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _))
+ .WillOnce([mockHwcRunTime] {
+ std::this_thread::sleep_for(mockHwcRunTime);
+ return hardware::graphics::composer::V2_1::Error::NONE;
+ });
+ EXPECT_CALL(*mPowerAdvisor, sendActualWorkDuration()).Times(0);
static constexpr bool kVsyncId = 123; // arbitrary
mFlinger.commitAndComposite(now, kVsyncId, now + mockVsyncPeriod.count());
}
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
index 583cf5f..b560025 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
@@ -323,6 +323,9 @@
public:
template <typename Case>
void transitionDisplayCommon();
+
+ template <bool kBoot>
+ sp<DisplayDevice> activeDisplayTest();
};
template <PowerMode PowerMode>
@@ -499,5 +502,87 @@
transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToUnknownVariant>>();
}
+template <bool kBoot>
+sp<DisplayDevice> SetPowerModeInternalTest::activeDisplayTest() {
+ using Case = SimplePrimaryDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // Inject a primary display.
+ Case::Display::injectHwcDisplay(this);
+ auto injector = Case::Display::makeFakeExistingDisplayInjector(this);
+ injector.setPowerMode(kBoot ? std::nullopt : std::make_optional(PowerMode::OFF));
+
+ const auto display = injector.inject();
+ EXPECT_EQ(display->getDisplayToken(), mFlinger.mutableActiveDisplayToken());
+
+ using PowerCase = PrimaryDisplayPowerCase<TransitionOffToOnVariant>;
+ TransitionOffToOnVariant::template setupCallExpectations<PowerCase>(this);
+
+ constexpr size_t kTimes = kBoot ? 1 : 0;
+ EXPECT_CALL(*mRenderEngine, onActiveDisplaySizeChanged(display->getSize())).Times(kTimes);
+ EXPECT_CALL(*mEventThread, onModeChanged(display->getActiveMode())).Times(kTimes);
+
+ if constexpr (kBoot) {
+ mFlinger.mutableActiveDisplayToken() = nullptr;
+ }
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.setPowerModeInternal(display, PowerMode::ON);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The primary display should be the active display.
+ EXPECT_EQ(display->getDisplayToken(), mFlinger.mutableActiveDisplayToken());
+
+ Mock::VerifyAndClearExpectations(mComposer);
+ Mock::VerifyAndClearExpectations(mRenderEngine);
+ Mock::VerifyAndClearExpectations(mEventThread);
+ Mock::VerifyAndClearExpectations(mVsyncController);
+ Mock::VerifyAndClearExpectations(mVSyncTracker);
+ Mock::VerifyAndClearExpectations(mFlinger.scheduler());
+ Mock::VerifyAndClearExpectations(&mFlinger.mockSchedulerCallback());
+
+ return display;
+}
+
+TEST_F(SetPowerModeInternalTest, activeDisplayBoot) {
+ constexpr bool kBoot = true;
+ activeDisplayTest<kBoot>();
+}
+
+TEST_F(SetPowerModeInternalTest, activeDisplaySingle) {
+ constexpr bool kBoot = false;
+ activeDisplayTest<kBoot>();
+}
+
+TEST_F(SetPowerModeInternalTest, activeDisplayDual) {
+ constexpr bool kBoot = false;
+ const auto innerDisplay = activeDisplayTest<kBoot>();
+
+ // Inject a powered-off outer display.
+ const auto outerDisplay = mFakeDisplayInjector.injectInternalDisplay(
+ [&](FakeDisplayDeviceInjector& injector) { injector.setPowerMode(PowerMode::OFF); },
+ {.displayId = PhysicalDisplayId::fromPort(254u),
+ .hwcDisplayId = 1,
+ .isPrimary = false});
+
+ EXPECT_EQ(innerDisplay->getDisplayToken(), mFlinger.mutableActiveDisplayToken());
+
+ mFlinger.setPowerModeInternal(innerDisplay, PowerMode::OFF);
+ mFlinger.setPowerModeInternal(outerDisplay, PowerMode::ON);
+
+ EXPECT_EQ(outerDisplay->getDisplayToken(), mFlinger.mutableActiveDisplayToken());
+
+ mFlinger.setPowerModeInternal(outerDisplay, PowerMode::OFF);
+ mFlinger.setPowerModeInternal(innerDisplay, PowerMode::ON);
+
+ EXPECT_EQ(innerDisplay->getDisplayToken(), mFlinger.mutableActiveDisplayToken());
+}
+
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index f1a69fb..3cffec1 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -330,6 +330,10 @@
layer->mDrawingParent = drawingParent;
}
+ void setPowerHintSessionMode(bool early, bool late) {
+ mFlinger->mPowerHintSessionMode = {.late = late, .early = early};
+ }
+
/* ------------------------------------------------------------------------
* Forwarding for functions being tested
*/
@@ -741,6 +745,7 @@
mHwcDisplayId(hwcDisplayId) {
mCreationArgs.connectionType = connectionType;
mCreationArgs.isPrimary = isPrimary;
+ mCreationArgs.initialPowerMode = hal::PowerMode::ON;
}
sp<IBinder> token() const { return mDisplayToken; }
@@ -793,7 +798,7 @@
return *this;
}
- auto& setPowerMode(hal::PowerMode mode) {
+ auto& setPowerMode(std::optional<hal::PowerMode> mode) {
mCreationArgs.initialPowerMode = mode;
return *this;
}
@@ -862,6 +867,10 @@
.deviceProductInfo = {},
.supportedModes = modes,
.activeMode = activeMode->get()};
+
+ if (mCreationArgs.isPrimary) {
+ mFlinger.mutableActiveDisplayToken() = mDisplayToken;
+ }
}
state.isSecure = mCreationArgs.isSecure;
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index 4eb9055..30a3f9a 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -349,6 +349,23 @@
}
}
+TEST_F(VSyncReactorTest, addHwVsyncTimestampDozePreempt) {
+ bool periodFlushed = false;
+ nsecs_t const newPeriod = 4000;
+
+ mReactor.startPeriodTransition(newPeriod);
+
+ auto time = 0;
+ // If the power mode is not DOZE or DOZE_SUSPEND, it is still collecting timestamps.
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed));
+ EXPECT_FALSE(periodFlushed);
+
+ // Set power mode to DOZE to trigger period flushing.
+ mReactor.setDisplayPowerMode(hal::PowerMode::DOZE);
+ EXPECT_FALSE(mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(periodFlushed);
+}
+
TEST_F(VSyncReactorTest, addPresentFenceWhileAwaitingPeriodConfirmationRequestsHwVsync) {
auto time = 0;
bool periodFlushed = false;
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.cpp
new file mode 100644
index 0000000..5049b1d
--- /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 0000000..657ced3
--- /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/MockDisplayMode.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
index a83ecbc..6809580 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
@@ -33,4 +33,24 @@
.build();
}
+inline DisplayModePtr cloneForDisplay(PhysicalDisplayId displayId, const DisplayModePtr& modePtr) {
+ return DisplayMode::Builder(modePtr->getHwcId())
+ .setId(modePtr->getId())
+ .setPhysicalDisplayId(displayId)
+ .setVsyncPeriod(modePtr->getVsyncPeriod())
+ .setGroup(modePtr->getGroup())
+ .setResolution(modePtr->getResolution())
+ .build();
+}
+
+inline DisplayModes cloneForDisplay(PhysicalDisplayId displayId, const DisplayModes& modes) {
+ DisplayModes clones;
+
+ for (const auto& [id, modePtr] : modes) {
+ clones.try_emplace(id, cloneForDisplay(displayId, modePtr));
+ }
+
+ return clones;
+}
+
} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
index c598cbc..aede250 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
@@ -36,11 +36,33 @@
MOCK_METHOD(bool, usePowerHintSession, (), (override));
MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override));
- MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDurationNanos), (override));
- MOCK_METHOD(void, sendActualWorkDuration, (int64_t actualDurationNanos, nsecs_t timestamp),
- (override));
+ MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDuration), (override));
+ MOCK_METHOD(void, sendActualWorkDuration, (), (override));
+ MOCK_METHOD(void, sendPredictedWorkDuration, (), (override));
MOCK_METHOD(void, enablePowerHint, (bool enabled), (override));
MOCK_METHOD(bool, startPowerHintSession, (const std::vector<int32_t>& threadIds), (override));
+ MOCK_METHOD(void, setGpuFenceTime,
+ (DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime), (override));
+ MOCK_METHOD(void, setHwcValidateTiming,
+ (DisplayId displayId, nsecs_t valiateStartTime, nsecs_t validateEndTime),
+ (override));
+ MOCK_METHOD(void, setHwcPresentTiming,
+ (DisplayId displayId, nsecs_t presentStartTime, nsecs_t presentEndTime),
+ (override));
+ MOCK_METHOD(void, setSkippedValidate, (DisplayId displayId, bool skipped), (override));
+ MOCK_METHOD(void, setRequiresClientComposition,
+ (DisplayId displayId, bool requiresClientComposition), (override));
+ MOCK_METHOD(void, setExpectedPresentTime, (nsecs_t expectedPresentTime), (override));
+ MOCK_METHOD(void, setSfPresentTiming, (nsecs_t presentFenceTime, nsecs_t presentEndTime),
+ (override));
+ MOCK_METHOD(void, setHwcPresentDelayedTime,
+ (DisplayId displayId,
+ std::chrono::steady_clock::time_point earliestFrameStartTime));
+ MOCK_METHOD(void, setFrameDelay, (nsecs_t frameDelayDuration), (override));
+ MOCK_METHOD(void, setCommitStart, (nsecs_t commitStartTime), (override));
+ MOCK_METHOD(void, setCompositeEnd, (nsecs_t compositeEndtime), (override));
+ MOCK_METHOD(void, setDisplays, (std::vector<DisplayId> & displayIds), (override));
+ MOCK_METHOD(void, setTotalFrameTargetWorkDuration, (int64_t targetDuration), (override));
};
} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
index 314f681..4ef91da 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
@@ -31,6 +31,7 @@
MOCK_METHOD3(addHwVsyncTimestamp, bool(nsecs_t, std::optional<nsecs_t>, bool*));
MOCK_METHOD1(startPeriodTransition, void(nsecs_t));
MOCK_METHOD1(setIgnorePresentFences, void(bool));
+ MOCK_METHOD(void, setDisplayPowerMode, (hal::PowerMode), (override));
MOCK_CONST_METHOD1(dump, void(std::string&));
};