Merge "Skip invisible windows in input list" into main
diff --git a/Android.bp b/Android.bp
index 13954ef..a689b2d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -48,6 +48,7 @@
export_include_dirs: [
"include/",
],
+ product_available: true,
}
ndk_headers {
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index b012243..4758607 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -4656,7 +4656,7 @@
void Dumpstate::TakeScreenshot(const std::string& path) {
const std::string& real_path = path.empty() ? screenshot_path_ : path;
int status =
- RunCommand("", {"/system/bin/screencap", "-p", real_path},
+ RunCommand("", {"screencap", "-p", real_path},
CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build());
if (status == 0) {
MYLOGD("Screenshot saved on %s\n", real_path.c_str());
diff --git a/include/android/OWNERS b/include/android/OWNERS
index fad8c1b..64d7e54 100644
--- a/include/android/OWNERS
+++ b/include/android/OWNERS
@@ -3,3 +3,6 @@
# Window manager
per-file surface_control_input_receiver.h = file:platform/frameworks/base:/services/core/java/com/android/server/wm/OWNERS
per-file input_transfer_token.h = file:platform/frameworks/base:/services/core/java/com/android/server/wm/OWNERS
+
+# CoGS
+per-file *luts* = file:platform/frameworks/base:/graphics/java/android/graphics/OWNERS
\ No newline at end of file
diff --git a/include/android/choreographer.h b/include/android/choreographer.h
index bec3283..2622a01 100644
--- a/include/android/choreographer.h
+++ b/include/android/choreographer.h
@@ -318,7 +318,7 @@
/**
* Gets the token used by the platform to identify the frame timeline at the given \c index.
- * q
+ *
* Available since API level 33.
*
* \param index index of a frame timeline, in \f( [0, FrameTimelinesLength) \f). See
diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h
index 3486e9b..976c7d6 100644
--- a/include/android/performance_hint.h
+++ b/include/android/performance_hint.h
@@ -19,9 +19,23 @@
*
* APerformanceHint allows apps to create performance hint sessions for groups
* of threads, and provide hints to the system about the workload of those threads,
- * to help the system more accurately allocate power for them. It is the NDK
+ * to help the system more accurately allocate resources for them. It is the NDK
* counterpart to the Java PerformanceHintManager SDK API.
*
+ * This API is intended for periodic workloads, such as frame production. Clients are
+ * expected to create an instance of APerformanceHintManager, create a session with
+ * that, and then set a target duration for the session. Then, they can report the actual
+ * work duration at the end of each cycle to inform the framework about how long those
+ * workloads are taking. The framework will then compare the actual durations to the target
+ * duration and attempt to help the client reach a steady state under the target.
+ *
+ * Unlike reportActualWorkDuration, the "notify..." hints are intended to be sent in
+ * advance of large changes in the workload, to prevent them from going over the target
+ * when there is a sudden, unforseen change. Their effects are intended to last for only
+ * one cycle, after which reportActualWorkDuration will have a chance to catch up.
+ * These hints should be used judiciously, only in cases where the workload is changing
+ * substantially. To enforce that, they are tracked using a per-app rate limiter to avoid
+ * excessive hinting and encourage clients to be mindful about when to send them.
* @{
*/
@@ -250,6 +264,54 @@
AWorkDuration* _Nonnull workDuration) __INTRODUCED_IN(__ANDROID_API_V__);
/**
+ * Informs the framework of an upcoming increase in the workload of a graphics pipeline
+ * bound to this session. The user can specify whether the increase is expected to be
+ * on the CPU, GPU, or both.
+ *
+ * Sending hints for both CPU and GPU counts as two separate hints for the purposes of the
+ * rate limiter.
+ *
+ * @param cpu Indicates if the workload increase is expected to affect the CPU.
+ * @param gpu Indicates if the workload increase is expected to affect the GPU.
+ * @param debugName A required string used to identify this specific hint during
+ * tracing. This debug string will only be held for the duration of the
+ * method, and can be safely discarded after.
+ *
+ * @return 0 on success.
+ * EINVAL if no hints were requested.
+ * EBUSY if the hint was rate limited.
+ * EPIPE if communication with the system service has failed.
+ * ENOTSUP if the hint is not supported.
+ */
+int APerformanceHint_notifyWorkloadIncrease(
+ APerformanceHintSession* _Nonnull session,
+ bool cpu, bool gpu, const char* _Nonnull debugName) __INTRODUCED_IN(36);
+
+/**
+ * Informs the framework of an upcoming reset in the workload of a graphics pipeline
+ * bound to this session, or the imminent start of a new workload. The user can specify
+ * whether the reset is expected to affect the CPU, GPU, or both.
+ *
+ * Sending hints for both CPU and GPU counts as two separate hints for the purposes of the
+ * this load tracking.
+ *
+ * @param cpu Indicates if the workload reset is expected to affect the CPU.
+ * @param gpu Indicates if the workload reset is expected to affect the GPU.
+ * @param debugName A required string used to identify this specific hint during
+ * tracing. This debug string will only be held for the duration of the
+ * method, and can be safely discarded after.
+ *
+ * @return 0 on success.
+ * EINVAL if no hints were requested.
+ * EBUSY if the hint was rate limited.
+ * EPIPE if communication with the system service has failed.
+ * ENOTSUP if the hint is not supported.
+ */
+int APerformanceHint_notifyWorkloadReset(
+ APerformanceHintSession* _Nonnull session,
+ bool cpu, bool gpu, const char* _Nonnull debugName) __INTRODUCED_IN(36);
+
+/**
* Creates a new AWorkDuration. When the client finishes using {@link AWorkDuration}, it should
* call {@link AWorkDuration_release()} to destroy {@link AWorkDuration} and release all resources
* associated with it.
diff --git a/include/audiomanager/IAudioManager.h b/include/audiomanager/IAudioManager.h
index 0b7e16b..a35a145 100644
--- a/include/audiomanager/IAudioManager.h
+++ b/include/audiomanager/IAudioManager.h
@@ -56,7 +56,7 @@
/*oneway*/ virtual status_t playerAttributes(audio_unique_id_t piid, audio_usage_t usage,
audio_content_type_t content)= 0;
/*oneway*/ virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event,
- audio_port_handle_t eventId) = 0;
+ const std::vector<audio_port_handle_t>& eventIds) = 0;
/*oneway*/ virtual status_t releasePlayer(audio_unique_id_t piid) = 0;
virtual audio_unique_id_t trackRecorder(const sp<IBinder>& recorder) = 0;
/*oneway*/ virtual status_t recorderEvent(audio_unique_id_t riid, recorder_state_t event) = 0;
diff --git a/include/input/Input.h b/include/input/Input.h
index a8684bd..127046d 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -294,6 +294,8 @@
NONE = AINPUT_KEYBOARD_TYPE_NONE,
NON_ALPHABETIC = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC,
ALPHABETIC = AINPUT_KEYBOARD_TYPE_ALPHABETIC,
+ ftl_first = NONE,
+ ftl_last = ALPHABETIC,
};
bool isStylusToolType(ToolType toolType);
diff --git a/include/input/InputConsumerNoResampling.h b/include/input/InputConsumerNoResampling.h
index 2e346bb..70d00d1 100644
--- a/include/input/InputConsumerNoResampling.h
+++ b/include/input/InputConsumerNoResampling.h
@@ -141,7 +141,7 @@
}
private:
- std::function<int(int events)> mCallback;
+ const std::function<int(int events)> mCallback;
};
sp<LooperEventCallback> mCallback;
/**
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 6a248ef..6b45dd3 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -131,8 +131,9 @@
PLAYER_ID = 1,
KEYBOARD_BACKLIGHT = 2,
KEYBOARD_MIC_MUTE = 3,
+ KEYBOARD_VOLUME_MUTE = 4,
- ftl_last = KEYBOARD_MIC_MUTE
+ ftl_last = KEYBOARD_VOLUME_MUTE
};
enum class InputDeviceLightCapability : uint32_t {
diff --git a/include/private/performance_hint_private.h b/include/private/performance_hint_private.h
index b7308c2..3229e45 100644
--- a/include/private/performance_hint_private.h
+++ b/include/private/performance_hint_private.h
@@ -114,6 +114,18 @@
*/
void APerformanceHint_setUseFMQForTesting(bool enabled);
+/**
+ * Get the rate limiter properties for testing.
+ */
+void APerformanceHint_getRateLimiterPropertiesForTesting(
+ int32_t* maxLoadHintsPerInterval, int64_t* loadHintInterval);
+
+/*
+ * Forces the "new load hint" flag to be disabled for testing.
+ */
+void APerformanceHint_setUseNewLoadHintBehaviorForTesting(bool newBehavior);
+
+
__END_DECLS
#endif // ANDROID_PRIVATE_NATIVE_PERFORMANCE_HINT_PRIVATE_H
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 2ef642a..0a61178 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -829,6 +829,7 @@
backend: {
rust: {
apex_available: [
+ "//apex_available:platform",
"com.android.virt",
],
enabled: true,
@@ -881,7 +882,6 @@
":__subpackages__",
"//packages/modules/Virtualization:__subpackages__",
"//device/google/cuttlefish/shared/minidroid:__subpackages__",
- "//system/software_defined_vehicle:__subpackages__",
"//visibility:any_system_partition",
],
}
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index a7423b3..5710bbf 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -82,7 +82,6 @@
llndk: {
symbol_file: "libbinder_ndk.map.txt",
- export_llndk_headers: ["libvendorsupport_llndk_headers"],
},
cflags: [
@@ -110,11 +109,9 @@
],
header_libs: [
- "libvendorsupport_llndk_headers",
"jni_headers",
],
export_header_lib_headers: [
- "libvendorsupport_llndk_headers",
"jni_headers",
],
diff --git a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
index 0ad110e..c6518d8 100644
--- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
@@ -30,16 +30,14 @@
#include <android/binder_auto_utils.h>
#include <android/binder_ibinder.h>
-#if defined(__ANDROID_VENDOR_API__)
-#include <android/llndk-versioning.h>
-#elif !defined(API_LEVEL_AT_LEAST)
#if defined(__BIONIC__)
-#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) \
- (__builtin_available(android sdk_api_level, *))
+#define API_LEVEL_AT_LEAST(sdk_api_level) __builtin_available(android sdk_api_level, *)
+#elif defined(TRUSTY_USERSPACE)
+// TODO(b/349936395): set to true for Trusty
+#define API_LEVEL_AT_LEAST(sdk_api_level) (false)
#else
-#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) (true)
+#define API_LEVEL_AT_LEAST(sdk_api_level) (true)
#endif // __BIONIC__
-#endif // __ANDROID_VENDOR_API__
#if __has_include(<android/binder_shell.h>)
#include <android/binder_shell.h>
@@ -298,9 +296,8 @@
#endif
#if defined(__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__) || __ANDROID_API__ >= 36
- if API_LEVEL_AT_LEAST (36, 202504) {
- if (codeToFunction != nullptr &&
- (&AIBinder_Class_setTransactionCodeToFunctionNameMap != nullptr)) {
+ if (API_LEVEL_AT_LEAST(36)) {
+ if (codeToFunction != nullptr) {
AIBinder_Class_setTransactionCodeToFunctionNameMap(clazz, codeToFunction,
functionCount);
}
diff --git a/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
index 83976b3..f3f3c38 100644
--- a/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
+++ b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
@@ -22,17 +22,14 @@
#include <set>
#include <sstream>
-// Include llndk-versioning.h only for non-system build as it is not available for NDK headers.
-#if defined(__ANDROID_VENDOR_API__)
-#include <android/llndk-versioning.h>
-#elif !defined(API_LEVEL_AT_LEAST)
#if defined(__BIONIC__)
-#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) \
- (__builtin_available(android sdk_api_level, *))
+#define API_LEVEL_AT_LEAST(sdk_api_level) __builtin_available(android sdk_api_level, *)
+#elif defined(TRUSTY_USERSPACE)
+// TODO(b/349936395): set to true for Trusty
+#define API_LEVEL_AT_LEAST(sdk_api_level) (false)
#else
-#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) (true)
+#define API_LEVEL_AT_LEAST(sdk_api_level) (true)
#endif // __BIONIC__
-#endif // __ANDROID_VENDOR_API__
namespace aidl::android::os {
@@ -44,7 +41,7 @@
class PersistableBundle {
public:
PersistableBundle() noexcept {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
mPBundle = APersistableBundle_new();
}
}
@@ -54,13 +51,13 @@
PersistableBundle(PersistableBundle&& other) noexcept : mPBundle(other.release()) {}
// duplicates, does not take ownership of the APersistableBundle*
PersistableBundle(const PersistableBundle& other) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
mPBundle = APersistableBundle_dup(other.mPBundle);
}
}
// duplicates, does not take ownership of the APersistableBundle*
PersistableBundle& operator=(const PersistableBundle& other) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
mPBundle = APersistableBundle_dup(other.mPBundle);
}
return *this;
@@ -70,7 +67,7 @@
binder_status_t readFromParcel(const AParcel* _Nonnull parcel) {
reset();
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return APersistableBundle_readFromParcel(parcel, &mPBundle);
} else {
return STATUS_INVALID_OPERATION;
@@ -81,7 +78,7 @@
if (!mPBundle) {
return STATUS_BAD_VALUE;
}
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return APersistableBundle_writeToParcel(mPBundle, parcel);
} else {
return STATUS_INVALID_OPERATION;
@@ -96,7 +93,7 @@
*/
void reset(APersistableBundle* _Nullable pBundle = nullptr) noexcept {
if (mPBundle) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
APersistableBundle_delete(mPBundle);
}
mPBundle = nullptr;
@@ -109,7 +106,7 @@
* what should be used to check for equality.
*/
bool deepEquals(const PersistableBundle& rhs) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return APersistableBundle_isEqual(get(), rhs.get());
} else {
return false;
@@ -148,7 +145,7 @@
inline std::string toString() const {
if (!mPBundle) {
return "<PersistableBundle: null>";
- } else if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ } else if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
std::ostringstream os;
os << "<PersistableBundle: ";
os << "size: " << std::to_string(APersistableBundle_size(mPBundle));
@@ -159,7 +156,7 @@
}
int32_t size() const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return APersistableBundle_size(mPBundle);
} else {
return 0;
@@ -167,7 +164,7 @@
}
int32_t erase(const std::string& key) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return APersistableBundle_erase(mPBundle, key.c_str());
} else {
return 0;
@@ -175,37 +172,37 @@
}
void putBoolean(const std::string& key, bool val) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
APersistableBundle_putBoolean(mPBundle, key.c_str(), val);
}
}
void putInt(const std::string& key, int32_t val) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
APersistableBundle_putInt(mPBundle, key.c_str(), val);
}
}
void putLong(const std::string& key, int64_t val) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
APersistableBundle_putLong(mPBundle, key.c_str(), val);
}
}
void putDouble(const std::string& key, double val) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
APersistableBundle_putDouble(mPBundle, key.c_str(), val);
}
}
void putString(const std::string& key, const std::string& val) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
APersistableBundle_putString(mPBundle, key.c_str(), val.c_str());
}
}
void putBooleanVector(const std::string& key, const std::vector<bool>& vec) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
// std::vector<bool> has no ::data().
int32_t num = vec.size();
if (num > 0) {
@@ -222,7 +219,7 @@
}
void putIntVector(const std::string& key, const std::vector<int32_t>& vec) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
int32_t num = vec.size();
if (num > 0) {
APersistableBundle_putIntVector(mPBundle, key.c_str(), vec.data(), num);
@@ -230,7 +227,7 @@
}
}
void putLongVector(const std::string& key, const std::vector<int64_t>& vec) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
int32_t num = vec.size();
if (num > 0) {
APersistableBundle_putLongVector(mPBundle, key.c_str(), vec.data(), num);
@@ -238,7 +235,7 @@
}
}
void putDoubleVector(const std::string& key, const std::vector<double>& vec) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
int32_t num = vec.size();
if (num > 0) {
APersistableBundle_putDoubleVector(mPBundle, key.c_str(), vec.data(), num);
@@ -246,7 +243,7 @@
}
}
void putStringVector(const std::string& key, const std::vector<std::string>& vec) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
int32_t num = vec.size();
if (num > 0) {
char** inVec = (char**)malloc(num * sizeof(char*));
@@ -261,13 +258,13 @@
}
}
void putPersistableBundle(const std::string& key, const PersistableBundle& pBundle) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
APersistableBundle_putPersistableBundle(mPBundle, key.c_str(), pBundle.mPBundle);
}
}
bool getBoolean(const std::string& key, bool* _Nonnull val) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return APersistableBundle_getBoolean(mPBundle, key.c_str(), val);
} else {
return false;
@@ -275,7 +272,7 @@
}
bool getInt(const std::string& key, int32_t* _Nonnull val) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return APersistableBundle_getInt(mPBundle, key.c_str(), val);
} else {
return false;
@@ -283,7 +280,7 @@
}
bool getLong(const std::string& key, int64_t* _Nonnull val) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return APersistableBundle_getLong(mPBundle, key.c_str(), val);
} else {
return false;
@@ -291,7 +288,7 @@
}
bool getDouble(const std::string& key, double* _Nonnull val) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return APersistableBundle_getDouble(mPBundle, key.c_str(), val);
} else {
return false;
@@ -303,7 +300,7 @@
}
bool getString(const std::string& key, std::string* _Nonnull val) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
char* outString = nullptr;
bool ret = APersistableBundle_getString(mPBundle, key.c_str(), &outString,
&stringAllocator, nullptr);
@@ -321,7 +318,7 @@
const char* _Nonnull, T* _Nullable, int32_t),
const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key,
std::vector<T>* _Nonnull vec) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
int32_t bytes = 0;
// call first with nullptr to get required size in bytes
bytes = getVec(pBundle, key, nullptr, 0);
@@ -343,28 +340,28 @@
}
bool getBooleanVector(const std::string& key, std::vector<bool>* _Nonnull vec) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getVecInternal<bool>(&APersistableBundle_getBooleanVector, mPBundle, key.c_str(),
vec);
}
return false;
}
bool getIntVector(const std::string& key, std::vector<int32_t>* _Nonnull vec) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getVecInternal<int32_t>(&APersistableBundle_getIntVector, mPBundle, key.c_str(),
vec);
}
return false;
}
bool getLongVector(const std::string& key, std::vector<int64_t>* _Nonnull vec) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getVecInternal<int64_t>(&APersistableBundle_getLongVector, mPBundle, key.c_str(),
vec);
}
return false;
}
bool getDoubleVector(const std::string& key, std::vector<double>* _Nonnull vec) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getVecInternal<double>(&APersistableBundle_getDoubleVector, mPBundle,
key.c_str(), vec);
}
@@ -389,7 +386,7 @@
}
bool getStringVector(const std::string& key, std::vector<std::string>* _Nonnull vec) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
int32_t bytes = APersistableBundle_getStringVector(mPBundle, key.c_str(), nullptr, 0,
&stringAllocator, nullptr);
if (bytes > 0) {
@@ -406,7 +403,7 @@
}
bool getPersistableBundle(const std::string& key, PersistableBundle* _Nonnull val) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
APersistableBundle* bundle = nullptr;
bool ret = APersistableBundle_getPersistableBundle(mPBundle, key.c_str(), &bundle);
if (ret) {
@@ -438,77 +435,77 @@
}
std::set<std::string> getBooleanKeys() const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getKeys(&APersistableBundle_getBooleanKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getIntKeys() const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getKeys(&APersistableBundle_getIntKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getLongKeys() const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getKeys(&APersistableBundle_getLongKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getDoubleKeys() const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getKeys(&APersistableBundle_getDoubleKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getStringKeys() const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getKeys(&APersistableBundle_getStringKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getBooleanVectorKeys() const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getKeys(&APersistableBundle_getBooleanVectorKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getIntVectorKeys() const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getKeys(&APersistableBundle_getIntVectorKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getLongVectorKeys() const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getKeys(&APersistableBundle_getLongVectorKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getDoubleVectorKeys() const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getKeys(&APersistableBundle_getDoubleVectorKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getStringVectorKeys() const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getKeys(&APersistableBundle_getStringVectorKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getPersistableBundleKeys() const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getKeys(&APersistableBundle_getPersistableBundleKeys, mPBundle);
} else {
return {};
diff --git a/libs/binder/rust/rpcbinder/Android.bp b/libs/binder/rust/rpcbinder/Android.bp
index 174fe8a..4036551 100644
--- a/libs/binder/rust/rpcbinder/Android.bp
+++ b/libs/binder/rust/rpcbinder/Android.bp
@@ -27,7 +27,6 @@
visibility: [
"//device/google/cuttlefish/shared/minidroid/sample",
"//packages/modules/Virtualization:__subpackages__",
- "//system/software_defined_vehicle:__subpackages__",
],
apex_available: [
"//apex_available:platform",
diff --git a/libs/binder/trusty/ndk/include/android/llndk-versioning.h b/libs/binder/trusty/ndk/include/android/llndk-versioning.h
deleted file mode 100644
index e955a34..0000000
--- a/libs/binder/trusty/ndk/include/android/llndk-versioning.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2024 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
-
-// TODO(b/349936395): set to true for Trusty
-#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) (false)
diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp
index 8dabc2c..f9f304a 100644
--- a/libs/gralloc/types/Android.bp
+++ b/libs/gralloc/types/Android.bp
@@ -56,7 +56,7 @@
],
export_shared_lib_headers: [
- "android.hardware.graphics.common-V5-ndk",
+ "android.hardware.graphics.common-V6-ndk",
"android.hardware.graphics.mapper@4.0",
"libhidlbase",
],
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index d1a5663..a8d5fe7 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -92,7 +92,7 @@
"android.hardware.common-V2-ndk.so:"
"android.hardware.common.fmq-V1-ndk.so:"
"android.hardware.graphics.allocator-V2-ndk.so:"
- "android.hardware.graphics.common-V5-ndk.so:"
+ "android.hardware.graphics.common-V6-ndk.so:"
"android.hardware.graphics.common@1.0.so:"
"android.hardware.graphics.common@1.1.so:"
"android.hardware.graphics.common@1.2.so:"
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 80e148b..1e33abb 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -274,6 +274,7 @@
"LayerMetadata.cpp",
"LayerStatePermissions.cpp",
"LayerState.cpp",
+ "DisplayLuts.cpp",
"OccupancyTracker.cpp",
"StreamSplitter.cpp",
"ScreenCaptureResults.cpp",
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 495418b..7aee903 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -286,18 +286,23 @@
if (surfaceControlChanged && mSurfaceControl != nullptr) {
BQA_LOGD("Updating SurfaceControl without recreating BBQ");
}
- bool applyTransaction = false;
// Always update the native object even though they might have the same layer handle, so we can
// get the updated transform hint from WM.
mSurfaceControl = surface;
SurfaceComposerClient::Transaction t;
+ bool applyTransaction = false;
if (surfaceControlChanged) {
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
updateBufferReleaseProducer();
#endif
t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure,
layer_state_t::eEnableBackpressure);
+ // Migrate the picture profile handle to the new surface control.
+ if (com_android_graphics_libgui_flags_apply_picture_profiles() &&
+ mPictureProfileHandle.has_value()) {
+ t.setPictureProfileHandle(mSurfaceControl, *mPictureProfileHandle);
+ }
applyTransaction = true;
}
mTransformHint = mSurfaceControl->getTransformHint();
@@ -679,6 +684,17 @@
if (!bufferItem.mIsAutoTimestamp) {
t->setDesiredPresentTime(bufferItem.mTimestamp);
}
+ if (com_android_graphics_libgui_flags_apply_picture_profiles() &&
+ bufferItem.mPictureProfileHandle.has_value()) {
+ t->setPictureProfileHandle(mSurfaceControl, *bufferItem.mPictureProfileHandle);
+ // The current picture profile must be maintained in case the BBQ gets its
+ // SurfaceControl switched out.
+ mPictureProfileHandle = bufferItem.mPictureProfileHandle;
+ // Clear out the picture profile if the requestor has asked for it to be cleared
+ if (mPictureProfileHandle == PictureProfileHandle::NONE) {
+ mPictureProfileHandle = std::nullopt;
+ }
+ }
// Drop stale frame timeline infos
while (!mPendingFrameTimelines.empty() &&
diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp
index 5beba02..3b2d337 100644
--- a/libs/gui/BufferItem.cpp
+++ b/libs/gui/BufferItem.cpp
@@ -38,26 +38,25 @@
return static_cast<T>(static_cast<uint64_t>(hi)<<32 | lo);
}
-BufferItem::BufferItem() :
- mGraphicBuffer(nullptr),
- mFence(nullptr),
- mCrop(Rect::INVALID_RECT),
- mTransform(0),
- mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
- mTimestamp(0),
- mIsAutoTimestamp(false),
- mDataSpace(HAL_DATASPACE_UNKNOWN),
- mFrameNumber(0),
- mSlot(INVALID_BUFFER_SLOT),
- mIsDroppable(false),
- mAcquireCalled(false),
- mTransformToDisplayInverse(false),
- mSurfaceDamage(),
- mAutoRefresh(false),
- mQueuedBuffer(true),
- mIsStale(false),
- mApi(0) {
-}
+BufferItem::BufferItem()
+ : mGraphicBuffer(nullptr),
+ mFence(nullptr),
+ mCrop(Rect::INVALID_RECT),
+ mTransform(0),
+ mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
+ mTimestamp(0),
+ mIsAutoTimestamp(false),
+ mDataSpace(HAL_DATASPACE_UNKNOWN),
+ mFrameNumber(0),
+ mSlot(INVALID_BUFFER_SLOT),
+ mIsDroppable(false),
+ mAcquireCalled(false),
+ mTransformToDisplayInverse(false),
+ mSurfaceDamage(),
+ mAutoRefresh(false),
+ mQueuedBuffer(true),
+ mIsStale(false),
+ mApi(0) {}
BufferItem::~BufferItem() {}
@@ -76,6 +75,11 @@
addAligned(size, high32(mTimestamp));
addAligned(size, mIsAutoTimestamp);
addAligned(size, mDataSpace);
+#if COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
+ addAligned(size, mPictureProfileHandle.has_value());
+ addAligned(size, low32(PictureProfileHandle::NONE.getId()));
+ addAligned(size, high32(PictureProfileHandle::NONE.getId()));
+#endif // COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
addAligned(size, low32(mFrameNumber));
addAligned(size, high32(mFrameNumber));
addAligned(size, mSlot);
@@ -170,6 +174,16 @@
writeAligned(buffer, size, high32(mTimestamp));
writeAligned(buffer, size, mIsAutoTimestamp);
writeAligned(buffer, size, mDataSpace);
+#if COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
+ writeAligned(buffer, size, mPictureProfileHandle.has_value());
+ if (mPictureProfileHandle.has_value()) {
+ writeAligned(buffer, size, low32(mPictureProfileHandle->getId()));
+ writeAligned(buffer, size, high32(mPictureProfileHandle->getId()));
+ } else {
+ writeAligned(buffer, size, low32(PictureProfileHandle::NONE.getId()));
+ writeAligned(buffer, size, high32(PictureProfileHandle::NONE.getId()));
+ }
+#endif // COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
writeAligned(buffer, size, low32(mFrameNumber));
writeAligned(buffer, size, high32(mFrameNumber));
writeAligned(buffer, size, mSlot);
@@ -231,6 +245,7 @@
uint32_t timestampLo = 0, timestampHi = 0;
uint32_t frameNumberLo = 0, frameNumberHi = 0;
+ int32_t pictureProfileIdLo = 0, pictureProfileIdHi = 0;
readAligned(buffer, size, mCrop);
readAligned(buffer, size, mTransform);
@@ -240,6 +255,16 @@
mTimestamp = to64<int64_t>(timestampLo, timestampHi);
readAligned(buffer, size, mIsAutoTimestamp);
readAligned(buffer, size, mDataSpace);
+#if COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
+ bool hasPictureProfileHandle;
+ readAligned(buffer, size, hasPictureProfileHandle);
+ readAligned(buffer, size, pictureProfileIdLo);
+ readAligned(buffer, size, pictureProfileIdHi);
+ mPictureProfileHandle = hasPictureProfileHandle
+ ? std::optional(PictureProfileHandle(
+ to64<PictureProfileId>(pictureProfileIdLo, pictureProfileIdHi)))
+ : std::nullopt;
+#endif // COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
readAligned(buffer, size, frameNumberLo);
readAligned(buffer, size, frameNumberHi);
mFrameNumber = to64<uint64_t>(frameNumberLo, frameNumberHi);
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 473a374..39209f9 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -938,6 +938,8 @@
&getFrameTimestamps);
const Region& surfaceDamage = input.getSurfaceDamage();
const HdrMetadata& hdrMetadata = input.getHdrMetadata();
+ const std::optional<PictureProfileHandle>& pictureProfileHandle =
+ input.getPictureProfileHandle();
if (acquireFence == nullptr) {
BQ_LOGE("queueBuffer: fence is NULL");
@@ -1044,6 +1046,7 @@
item.mIsAutoTimestamp = isAutoTimestamp;
item.mDataSpace = dataSpace;
item.mHdrMetadata = hdrMetadata;
+ item.mPictureProfileHandle = pictureProfileHandle;
item.mFrameNumber = currentFrameNumber;
item.mSlot = slot;
item.mFence = acquireFence;
diff --git a/libs/gui/BufferStuffing.md b/libs/gui/BufferStuffing.md
new file mode 100644
index 0000000..6ed8ad9
--- /dev/null
+++ b/libs/gui/BufferStuffing.md
@@ -0,0 +1,7 @@
+### Buffer Stuffing and Recovery ###
+
+Buffer stuffing happens on the client side when SurfaceFlinger misses a frame, but the client continues producing buffers at the same rate. This could occur anytime when SurfaceFlinger does not meet the expected timeline’s deadline to finish composing a frame. As a result, SurfaceFlinger cannot yet release the buffer for the frame that it missed and the client has one less buffer to render into. The client may then run out of buffers or have to wait for buffer release callbacks, increasing the chances of janking when clients render multiple windows.
+
+Recovery is implemented by first detecting when buffer stuffing occurs and ensuring that the elevated buffer counts in the server are from a relevant SurfaceControl (is a ViewRootImpl). Other SurfaceControl buffer producers such as games, media, and camera have other reasons for expectedly increased buffer counts, which do not need buffer stuffing recovery.
+
+The actual recovery adjusts the animation timeline in the Choreographer so that the client deadlines for subsequent frames are moved forward in time by one frame. This approach adjusts the client buffer production timeline such that SurfaceFlinger does not fall behind when it misses a frame because the client will simply match its frame production rate with SurfaceFlinger. Ordinarily, buffer stuffing is problematic because the client continues producing buffers when SurfaceFlinger is behind. However, if the client delays producing its buffers to match SurfaceFlinger’s rate, the animation has new frame deadlines that can be reasonably met. The animation is effectively paused for one frame longer than originally intended, and continues the remainder of the animation normally.
\ No newline at end of file
diff --git a/libs/gui/DisplayLuts.cpp b/libs/gui/DisplayLuts.cpp
new file mode 100644
index 0000000..8042976
--- /dev/null
+++ b/libs/gui/DisplayLuts.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 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 "include/gui/DisplayLuts.h"
+#include <gui/DisplayLuts.h>
+#include <private/gui/ParcelUtils.h>
+
+namespace android::gui {
+
+status_t DisplayLuts::Entry::readFromParcel(const android::Parcel* parcel) {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ SAFE_PARCEL(parcel->readInt32, &dimension);
+ SAFE_PARCEL(parcel->readInt32, &size);
+ SAFE_PARCEL(parcel->readInt32, &samplingKey);
+
+ return OK;
+}
+
+status_t DisplayLuts::Entry::writeToParcel(android::Parcel* parcel) const {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ SAFE_PARCEL(parcel->writeInt32, dimension);
+ SAFE_PARCEL(parcel->writeInt32, size);
+ SAFE_PARCEL(parcel->writeInt32, samplingKey);
+
+ return OK;
+}
+
+status_t DisplayLuts::readFromParcel(const android::Parcel* parcel) {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ SAFE_PARCEL(parcel->readUniqueFileDescriptor, &fd);
+ SAFE_PARCEL(parcel->readInt32Vector, &offsets);
+ int32_t numLutProperties;
+ SAFE_PARCEL(parcel->readInt32, &numLutProperties);
+ lutProperties.reserve(numLutProperties);
+ for (int32_t i = 0; i < numLutProperties; i++) {
+ lutProperties.push_back({});
+ SAFE_PARCEL(lutProperties.back().readFromParcel, parcel);
+ }
+ return OK;
+}
+
+status_t DisplayLuts::writeToParcel(android::Parcel* parcel) const {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ SAFE_PARCEL(parcel->writeUniqueFileDescriptor, fd);
+ SAFE_PARCEL(parcel->writeInt32Vector, offsets);
+ SAFE_PARCEL(parcel->writeInt32, static_cast<int32_t>(lutProperties.size()));
+ for (auto& entry : lutProperties) {
+ SAFE_PARCEL(entry.writeToParcel, parcel);
+ }
+ return OK;
+}
+} // namespace android::gui
\ No newline at end of file
diff --git a/libs/gui/IGraphicBufferProducerFlattenables.cpp b/libs/gui/IGraphicBufferProducerFlattenables.cpp
index c8b9b67..4e92a39 100644
--- a/libs/gui/IGraphicBufferProducerFlattenables.cpp
+++ b/libs/gui/IGraphicBufferProducerFlattenables.cpp
@@ -20,21 +20,19 @@
namespace android {
constexpr size_t IGraphicBufferProducer::QueueBufferInput::minFlattenedSize() {
- return sizeof(timestamp) +
- sizeof(isAutoTimestamp) +
- sizeof(dataSpace) +
- sizeof(crop) +
- sizeof(scalingMode) +
- sizeof(transform) +
- sizeof(stickyTransform) +
- sizeof(getFrameTimestamps) +
- sizeof(slot);
+ return sizeof(timestamp) + sizeof(isAutoTimestamp) + sizeof(dataSpace) + sizeof(crop) +
+ sizeof(scalingMode) + sizeof(transform) + sizeof(stickyTransform) +
+ sizeof(getFrameTimestamps) + sizeof(slot) +
+#if COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
+ sizeof(decltype(pictureProfileHandle.has_value())) +
+ sizeof(decltype(pictureProfileHandle.getId()));
+#else
+ 0;
+#endif // COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
}
size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const {
- return minFlattenedSize() +
- fence->getFlattenedSize() +
- surfaceDamage.getFlattenedSize() +
+ return minFlattenedSize() + fence->getFlattenedSize() + surfaceDamage.getFlattenedSize() +
hdrMetadata.getFlattenedSize();
}
@@ -57,6 +55,12 @@
FlattenableUtils::write(buffer, size, transform);
FlattenableUtils::write(buffer, size, stickyTransform);
FlattenableUtils::write(buffer, size, getFrameTimestamps);
+#if COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
+ FlattenableUtils::write(buffer, size, pictureProfileHandle.has_value());
+ FlattenableUtils::write(buffer, size,
+ pictureProfileHandle.has_value() ? pictureProfileHandle->getId()
+ : PictureProfileHandle::NONE.getId());
+#endif // COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
status_t result = fence->flatten(buffer, size, fds, count);
if (result != NO_ERROR) {
@@ -91,6 +95,15 @@
FlattenableUtils::read(buffer, size, transform);
FlattenableUtils::read(buffer, size, stickyTransform);
FlattenableUtils::read(buffer, size, getFrameTimestamps);
+#if COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
+ bool hasPictureProfileHandle;
+ FlattenableUtils::read(buffer, size, hasPictureProfileHandle);
+ PictureProfileId pictureProfileId;
+ FlattenableUtils::read(buffer, size, pictureProfileId);
+ pictureProfileHandle = hasPictureProfileHandle
+ ? std::optional(PictureProfileHandle(pictureProfileId))
+ : std::nullopt;
+#endif // COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
fence = new Fence();
status_t result = fence->unflatten(buffer, size, fds, count);
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 4b53134..c1a03fc 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -21,6 +21,7 @@
#include <android/gui/ISurfaceComposerClient.h>
#include <android/native_window.h>
#include <binder/Parcel.h>
+#include <com_android_graphics_libgui_flags.h>
#include <gui/FrameRateUtils.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/LayerState.h>
@@ -91,7 +92,9 @@
trustedOverlay(gui::TrustedOverlay::UNSET),
bufferCrop(Rect::INVALID_RECT),
destinationFrame(Rect::INVALID_RECT),
- dropInputMode(gui::DropInputMode::NONE) {
+ dropInputMode(gui::DropInputMode::NONE),
+ pictureProfileHandle(PictureProfileHandle::NONE),
+ appContentPriority(0) {
matrix.dsdx = matrix.dtdy = 1.0f;
matrix.dsdy = matrix.dtdx = 0.0f;
hdrMetadata.validTypes = 0;
@@ -202,6 +205,16 @@
if (hasBufferReleaseChannel) {
SAFE_PARCEL(output.writeParcelable, *bufferReleaseChannel);
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS_APPLY_PICTURE_PROFILES
+ SAFE_PARCEL(output.writeInt64, pictureProfileHandle.getId());
+ SAFE_PARCEL(output.writeInt32, appContentPriority);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS_APPLY_PICTURE_PROFILES
+
+ const bool hasLuts = (luts != nullptr);
+ SAFE_PARCEL(output.writeBool, hasLuts);
+ if (hasLuts) {
+ SAFE_PARCEL(output.writeParcelable, *luts);
+ }
return NO_ERROR;
}
@@ -357,6 +370,21 @@
bufferReleaseChannel = std::make_shared<gui::BufferReleaseChannel::ProducerEndpoint>();
SAFE_PARCEL(input.readParcelable, bufferReleaseChannel.get());
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS_APPLY_PICTURE_PROFILES
+ int64_t pictureProfileId;
+ SAFE_PARCEL(input.readInt64, &pictureProfileId);
+ pictureProfileHandle = PictureProfileHandle(pictureProfileId);
+ SAFE_PARCEL(input.readInt32, &appContentPriority);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS_APPLY_PICTURE_PROFILES
+
+ bool hasLuts;
+ SAFE_PARCEL(input.readBool, &hasLuts);
+ if (hasLuts) {
+ luts = std::make_shared<gui::DisplayLuts>();
+ SAFE_PARCEL(input.readParcelable, luts.get());
+ } else {
+ luts = nullptr;
+ }
return NO_ERROR;
}
@@ -745,6 +773,16 @@
what |= eBufferReleaseChannelChanged;
bufferReleaseChannel = other.bufferReleaseChannel;
}
+ if (com_android_graphics_libgui_flags_apply_picture_profiles()) {
+ if (other.what & ePictureProfileHandleChanged) {
+ what |= ePictureProfileHandleChanged;
+ pictureProfileHandle = other.pictureProfileHandle;
+ }
+ if (other.what & eAppContentPriorityChanged) {
+ what |= eAppContentPriorityChanged;
+ appContentPriority = other.appContentPriority;
+ }
+ }
if ((other.what & what) != other.what) {
ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
"other.what=0x%" PRIX64 " what=0x%" PRIX64 " unmerged flags=0x%" PRIX64,
@@ -826,6 +864,8 @@
CHECK_DIFF(diff, eDimmingEnabledChanged, other, dimmingEnabled);
if (other.what & eBufferReleaseChannelChanged) diff |= eBufferReleaseChannelChanged;
if (other.what & eLutsChanged) diff |= eLutsChanged;
+ CHECK_DIFF(diff, ePictureProfileHandleChanged, other, pictureProfileHandle);
+ CHECK_DIFF(diff, eAppContentPriorityChanged, other, appContentPriority);
return diff;
}
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index c97dfd4..61aabaa 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -20,8 +20,6 @@
#include <stdint.h>
#include <sys/types.h>
-#include <com_android_graphics_libgui_flags.h>
-
#include <android/gui/BnWindowInfosReportedListener.h>
#include <android/gui/DisplayState.h>
#include <android/gui/EdgeExtensionParameters.h>
@@ -29,6 +27,7 @@
#include <android/gui/IWindowInfosListener.h>
#include <android/gui/TrustedPresentationThresholds.h>
#include <android/os/IInputConstants.h>
+#include <com_android_graphics_libgui_flags.h>
#include <gui/DisplayLuts.h>
#include <gui/FrameRateUtils.h>
#include <gui/TraceUtils.h>
@@ -1971,9 +1970,13 @@
return *this;
}
- s->luts = std::make_shared<gui::DisplayLuts>(base::unique_fd(dup(lutFd.get())), offsets,
- dimensions, sizes, samplingKeys);
s->what |= layer_state_t::eLutsChanged;
+ if (lutFd.ok()) {
+ s->luts = std::make_shared<gui::DisplayLuts>(base::unique_fd(dup(lutFd.get())), offsets,
+ dimensions, sizes, samplingKeys);
+ } else {
+ s->luts = nullptr;
+ }
registerSurfaceControlForCallback(sc);
return *this;
@@ -2447,6 +2450,40 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setPictureProfileHandle(
+ const sp<SurfaceControl>& sc, const PictureProfileHandle& pictureProfileHandle) {
+ if (com_android_graphics_libgui_flags_apply_picture_profiles()) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+
+ s->what |= layer_state_t::ePictureProfileHandleChanged;
+ s->pictureProfileHandle = pictureProfileHandle;
+
+ registerSurfaceControlForCallback(sc);
+ }
+ return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setContentPriority(
+ const sp<SurfaceControl>& sc, int32_t priority) {
+ if (com_android_graphics_libgui_flags_apply_picture_profiles()) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+
+ s->what |= layer_state_t::eAppContentPriorityChanged;
+ s->appContentPriority = priority;
+
+ registerSurfaceControlForCallback(sc);
+ }
+ return *this;
+}
+
// ---------------------------------------------------------------------------
DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 8894b66..07558aa 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -17,7 +17,9 @@
#ifndef ANDROID_GUI_BLAST_BUFFER_QUEUE_H
#define ANDROID_GUI_BLAST_BUFFER_QUEUE_H
-#include <com_android_graphics_libgui_flags.h>
+#include <optional>
+#include <queue>
+
#include <gui/BufferItem.h>
#include <gui/BufferItemConsumer.h>
#include <gui/IGraphicBufferConsumer.h>
@@ -29,7 +31,6 @@
#include <utils/RefBase.h>
#include <system/window.h>
-#include <queue>
#include <com_android_graphics_libgui_flags.h>
@@ -222,6 +223,10 @@
ui::Size mRequestedSize GUARDED_BY(mMutex);
int32_t mFormat GUARDED_BY(mMutex);
+ // Keep a copy of the current picture profile handle, so it can be moved to a new
+ // SurfaceControl when BBQ migrates via ::update.
+ std::optional<PictureProfileHandle> mPictureProfileHandle;
+
struct BufferInfo {
bool hasBuffer = false;
uint32_t width;
diff --git a/libs/gui/include/gui/BufferItem.h b/libs/gui/include/gui/BufferItem.h
index 218bb42..2f85c62 100644
--- a/libs/gui/include/gui/BufferItem.h
+++ b/libs/gui/include/gui/BufferItem.h
@@ -17,9 +17,12 @@
#ifndef ANDROID_GUI_BUFFERITEM_H
#define ANDROID_GUI_BUFFERITEM_H
+#include <optional>
+
#include <gui/HdrMetadata.h>
#include <ui/FenceTime.h>
+#include <ui/PictureProfileHandle.h>
#include <ui/Rect.h>
#include <ui/Region.h>
@@ -91,6 +94,10 @@
// mHdrMetadata is the HDR metadata associated with this buffer slot.
HdrMetadata mHdrMetadata;
+ // mPictureProfileHandle is a handle that points to a set of parameters that configure picture
+ // processing hardware to enhance the quality of buffer contents.
+ std::optional<PictureProfileHandle> mPictureProfileHandle;
+
// mFrameNumber is the number of the queued frame for this slot.
uint64_t mFrameNumber;
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index 4dbf9e1..40a6e79 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -119,7 +119,7 @@
HdcpLevelsChange hdcpLevelsChange;
};
};
- static_assert(sizeof(Event) == 216);
+ static_assert(sizeof(Event) == 224);
public:
/*
diff --git a/libs/gui/include/gui/DisplayLuts.h b/libs/gui/include/gui/DisplayLuts.h
index 16a360d..ab86ac4 100644
--- a/libs/gui/include/gui/DisplayLuts.h
+++ b/libs/gui/include/gui/DisplayLuts.h
@@ -16,16 +16,24 @@
#pragma once
#include <android-base/unique_fd.h>
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
#include <vector>
namespace android::gui {
-struct DisplayLuts {
+struct DisplayLuts : public Parcelable {
public:
- struct Entry {
+ struct Entry : public Parcelable {
+ Entry() {};
+ Entry(int32_t lutDimension, int32_t lutSize, int32_t lutSamplingKey)
+ : dimension(lutDimension), size(lutSize), samplingKey(lutSamplingKey) {}
int32_t dimension;
int32_t size;
int32_t samplingKey;
+
+ status_t writeToParcel(android::Parcel* parcel) const override;
+ status_t readFromParcel(const android::Parcel* parcel) override;
};
DisplayLuts() {}
@@ -42,7 +50,10 @@
}
}
- base::unique_fd& getLutFileDescriptor() { return fd; }
+ status_t writeToParcel(android::Parcel* parcel) const override;
+ status_t readFromParcel(const android::Parcel* parcel) override;
+
+ const base::unique_fd& getLutFileDescriptor() const { return fd; }
std::vector<Entry> lutProperties;
std::vector<int32_t> offsets;
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
index 3aac457..a42ddc4 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -19,6 +19,7 @@
#include <stdint.h>
#include <sys/types.h>
+#include <optional>
#include <utils/Errors.h>
#include <utils/RefBase.h>
@@ -28,6 +29,7 @@
#include <ui/BufferQueueDefs.h>
#include <ui/Fence.h>
#include <ui/GraphicBuffer.h>
+#include <ui/PictureProfileHandle.h>
#include <ui/Rect.h>
#include <ui/Region.h>
@@ -365,6 +367,14 @@
const HdrMetadata& getHdrMetadata() const { return hdrMetadata; }
void setHdrMetadata(const HdrMetadata& metadata) { hdrMetadata = metadata; }
+ const std::optional<PictureProfileHandle>& getPictureProfileHandle() const {
+ return pictureProfileHandle;
+ }
+ void setPictureProfileHandle(const PictureProfileHandle& profile) {
+ pictureProfileHandle = profile;
+ }
+ void clearPictureProfileHandle() { pictureProfileHandle = std::nullopt; }
+
int64_t timestamp{0};
int isAutoTimestamp{0};
android_dataspace dataSpace{HAL_DATASPACE_UNKNOWN};
@@ -377,6 +387,7 @@
bool getFrameTimestamps{false};
int slot{-1};
HdrMetadata hdrMetadata;
+ std::optional<PictureProfileHandle> pictureProfileHandle;
};
struct QueueBufferOutput : public Flattenable<QueueBufferOutput> {
@@ -403,7 +414,7 @@
uint64_t nextFrameNumber{0};
FrameEventHistoryDelta frameTimestamps;
bool bufferReplaced{false};
- int maxBufferCount{0};
+ int maxBufferCount{BufferQueueDefs::NUM_BUFFER_SLOTS};
status_t result{NO_ERROR};
};
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 6bfeaec..1c31e46 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -47,6 +47,7 @@
#include <ui/BlurRegion.h>
#include <ui/GraphicTypes.h>
#include <ui/LayerStack.h>
+#include <ui/PictureProfileHandle.h>
#include <ui/Rect.h>
#include <ui/Region.h>
#include <ui/Rotation.h>
@@ -170,6 +171,10 @@
// Sets a property on this layer indicating that its visible region should be considered
// when computing TrustedPresentation Thresholds.
eCanOccludePresentation = 0x1000,
+ // Indicates that the SurfaceControl should recover from buffer stuffing when
+ // possible. This is the case when the SurfaceControl is the root SurfaceControl
+ // owned by ViewRootImpl.
+ eRecoverableFromBufferStuffing = 0x2000,
};
enum {
@@ -224,6 +229,8 @@
eExtendedRangeBrightnessChanged = 0x10000'00000000,
eEdgeExtensionChanged = 0x20000'00000000,
eBufferReleaseChannelChanged = 0x40000'00000000,
+ ePictureProfileHandleChanged = 0x80000'00000000,
+ eAppContentPriorityChanged = 0x100000'00000000,
};
layer_state_t();
@@ -267,7 +274,8 @@
layer_state_t::eColorSpaceAgnosticChanged | layer_state_t::eColorTransformChanged |
layer_state_t::eCornerRadiusChanged | layer_state_t::eDimmingEnabledChanged |
layer_state_t::eHdrMetadataChanged | layer_state_t::eShadowRadiusChanged |
- layer_state_t::eStretchChanged;
+ layer_state_t::eStretchChanged | layer_state_t::ePictureProfileHandleChanged |
+ layer_state_t::eAppContentPriorityChanged;
// Changes which invalidates the layer's visible region in CE.
static constexpr uint64_t CONTENT_DIRTY = layer_state_t::CONTENT_CHANGES |
@@ -412,6 +420,15 @@
float currentHdrSdrRatio = 1.f;
float desiredHdrSdrRatio = 1.f;
+ // Enhance the quality of the buffer contents by configurating a picture processing pipeline
+ // with values as specified by this picture profile.
+ PictureProfileHandle pictureProfileHandle{PictureProfileHandle::NONE};
+
+ // A value indicating the significance of the layer's content to the app's desired user
+ // experience. A lower priority will result in more likelihood of getting access to limited
+ // resources, such as picture processing hardware.
+ int32_t appContentPriority = 0;
+
gui::CachingHint cachingHint = gui::CachingHint::Enabled;
TrustedPresentationThresholds trustedPresentationThresholds;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 7c6b341..0d7f8c2 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -38,6 +38,7 @@
#include <ui/EdgeExtensionEffect.h>
#include <ui/FrameStats.h>
#include <ui/GraphicTypes.h>
+#include <ui/PictureProfileHandle.h>
#include <ui/PixelFormat.h>
#include <ui/Rotation.h>
#include <ui/StaticDisplayInfo.h>
@@ -776,6 +777,20 @@
const sp<SurfaceControl>& sc,
const std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint>& channel);
+ /**
+ * Configures a surface control to use picture processing hardware, configured as specified
+ * by the picture profile, to enhance the quality of all subsequent buffer contents.
+ */
+ Transaction& setPictureProfileHandle(const sp<SurfaceControl>& sc,
+ const PictureProfileHandle& pictureProfileHandle);
+
+ /**
+ * Configures the relative importance of the contents of the layer with respect to the app's
+ * user experience. A lower priority value will give the layer preferred access to limited
+ * resources, such as picture processing, over a layer with a higher priority value.
+ */
+ Transaction& setContentPriority(const sp<SurfaceControl>& sc, int32_t contentPriority);
+
status_t setDisplaySurface(const sp<IBinder>& token,
const sp<IGraphicBufferProducer>& bufferProducer);
diff --git a/libs/gui/include/gui/VsyncEventData.h b/libs/gui/include/gui/VsyncEventData.h
index b40a840..ced5130 100644
--- a/libs/gui/include/gui/VsyncEventData.h
+++ b/libs/gui/include/gui/VsyncEventData.h
@@ -36,6 +36,9 @@
// Size of frame timelines provided by the platform; max is kFrameTimelinesCapacity.
uint32_t frameTimelinesLength;
+ // Number of queued buffers to indicate if buffer stuffing mode is detected.
+ uint32_t numberQueuedBuffers;
+
struct alignas(8) FrameTimeline {
// The Vsync Id corresponsing to this vsync event. This will be used to
// populate ISurfaceComposer::setFrameTimelineVsync and
diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig
index 22d32e9..6bf38c0 100644
--- a/libs/gui/libgui_flags.aconfig
+++ b/libs/gui/libgui_flags.aconfig
@@ -131,3 +131,11 @@
bug: "359252619"
is_fixed_read_only: true
} # bq_producer_throttles_only_async_mode
+
+flag {
+ name: "bq_gl_fence_cleanup"
+ namespace: "core_graphics"
+ description: "Remove BufferQueueProducer::dequeue's wait on this fence (or the fence entirely) to prevent deadlocks"
+ bug: "339705065"
+ is_fixed_read_only: true
+} # bq_gl_fence_cleanup
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 2e6ffcb..b026e64 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -27,6 +27,7 @@
#include <gui/Surface.h>
#include <ui/GraphicBuffer.h>
+#include <ui/PictureProfileHandle.h>
#include <android-base/properties.h>
@@ -1569,4 +1570,61 @@
EXPECT_EQ(ADATASPACE_UNKNOWN, dataSpace);
}
+TEST_F(BufferQueueTest, PassesThroughPictureProfileHandle) {
+ createBufferQueue();
+ sp<MockConsumer> mc(new MockConsumer);
+ mConsumer->consumerConnect(mc, false);
+
+ IGraphicBufferProducer::QueueBufferOutput qbo;
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &qbo);
+ mProducer->setMaxDequeuedBufferCount(2);
+ mConsumer->setMaxAcquiredBufferCount(2);
+
+ // First try to pass a valid picture profile handle
+ {
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buf;
+ IGraphicBufferProducer::QueueBufferInput qbi(0, false, HAL_DATASPACE_UNKNOWN,
+ Rect(0, 0, 1, 1),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+ Fence::NO_FENCE);
+ qbi.setPictureProfileHandle(PictureProfileHandle(1));
+
+ EXPECT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
+ mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0, GRALLOC_USAGE_SW_READ_OFTEN,
+ nullptr, nullptr));
+ EXPECT_EQ(OK, mProducer->requestBuffer(slot, &buf));
+ EXPECT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
+
+ BufferItem item;
+ EXPECT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+
+ ASSERT_TRUE(item.mPictureProfileHandle.has_value());
+ ASSERT_EQ(item.mPictureProfileHandle, PictureProfileHandle(1));
+ }
+
+ // Then validate that the picture profile handle isn't sticky and is reset for the next buffer
+ {
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buf;
+ IGraphicBufferProducer::QueueBufferInput qbi(0, false, HAL_DATASPACE_UNKNOWN,
+ Rect(0, 0, 1, 1),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+ Fence::NO_FENCE);
+
+ EXPECT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
+ mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0, GRALLOC_USAGE_SW_READ_OFTEN,
+ nullptr, nullptr));
+ EXPECT_EQ(OK, mProducer->requestBuffer(slot, &buf));
+ EXPECT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
+
+ BufferItem item;
+ EXPECT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+
+ ASSERT_FALSE(item.mPictureProfileHandle.has_value());
+ }
+}
+
} // namespace android
diff --git a/libs/gui/tests/DisplayEventStructLayout_test.cpp b/libs/gui/tests/DisplayEventStructLayout_test.cpp
index 29eeaa8..791f471 100644
--- a/libs/gui/tests/DisplayEventStructLayout_test.cpp
+++ b/libs/gui/tests/DisplayEventStructLayout_test.cpp
@@ -36,15 +36,16 @@
CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameInterval, 8);
CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.preferredFrameTimelineIndex, 16);
CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelinesLength, 20);
- CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelines, 24);
- CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelines[0].vsyncId, 24);
+ CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.numberQueuedBuffers, 24);
+ CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelines, 32);
+ CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelines[0].vsyncId, 32);
CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelines[0].deadlineTimestamp,
- 32);
+ 40);
CHECK_OFFSET(DisplayEventReceiver::Event::VSync,
- vsyncData.frameTimelines[0].expectedPresentationTime, 40);
+ vsyncData.frameTimelines[0].expectedPresentationTime, 48);
// Also test the offsets of the last frame timeline. A loop is not used because the non-const
// index cannot be used in static_assert.
- const int lastFrameTimelineOffset = /* Start of array */ 24 +
+ const int lastFrameTimelineOffset = /* Start of array */ 32 +
(VsyncEventData::kFrameTimelinesCapacity - 1) * /* Size of FrameTimeline */ 24;
CHECK_OFFSET(DisplayEventReceiver::Event::VSync,
vsyncData.frameTimelines[VsyncEventData::kFrameTimelinesCapacity - 1].vsyncId,
diff --git a/libs/input/InputConsumerNoResampling.cpp b/libs/input/InputConsumerNoResampling.cpp
index d3653cf..2c0f77a 100644
--- a/libs/input/InputConsumerNoResampling.cpp
+++ b/libs/input/InputConsumerNoResampling.cpp
@@ -193,13 +193,6 @@
InputConsumerNoResampling::~InputConsumerNoResampling() {
ensureCalledOnLooperThread(__func__);
- while (!mOutboundQueue.empty()) {
- processOutboundEvents();
- // This is our last chance to ack the events. If we don't ack them here, we will get an ANR,
- // so keep trying to send the events as long as they are present in the queue.
- }
-
- setFdEvents(0);
// If there are any remaining unread batches, send an ack for them and don't deliver
// them to callbacks.
for (auto& [_, batches] : mBatches) {
@@ -208,6 +201,12 @@
batches.pop();
}
}
+
+ while (!mOutboundQueue.empty()) {
+ processOutboundEvents();
+ // This is our last chance to ack the events. If we don't ack them here, we will get an ANR,
+ // so keep trying to send the events as long as they are present in the queue.
+ }
// However, it is still up to the app to finish any events that have already been delivered
// to the callbacks. If we wanted to change that behaviour and auto-finish all unfinished events
// that were already sent to callbacks, we could potentially loop through "mConsumeTimes"
@@ -216,6 +215,10 @@
const size_t unfinishedEvents = mConsumeTimes.size();
LOG_IF(INFO, unfinishedEvents != 0)
<< getName() << " has " << unfinishedEvents << " unfinished event(s)";
+ // Remove the fd from epoll, so that Looper does not call 'handleReceiveCallback' anymore.
+ // This must be done at the end of the destructor; otherwise, some of the other functions may
+ // call 'setFdEvents' as a side-effect, thus adding the fd back to the epoll set of the looper.
+ setFdEvents(0);
}
int InputConsumerNoResampling::handleReceiveCallback(int events) {
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 77dcaa9..6a55726 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -583,15 +583,6 @@
StringPrintf("publishMotionEvent(inputChannel=%s, action=%s)",
mChannel->getName().c_str(),
MotionEvent::actionToString(action).c_str()));
- if (verifyEvents()) {
- Result<void> result =
- mInputVerifier.processMovement(deviceId, source, action, pointerCount,
- pointerProperties, pointerCoords, flags);
- if (!result.ok()) {
- LOG(ERROR) << "Bad stream: " << result.error();
- return BAD_VALUE;
- }
- }
if (debugTransportPublisher()) {
std::string transformString;
transform.dump(transformString, "transform", " ");
@@ -657,8 +648,18 @@
msg.body.motion.pointers[i].properties = pointerProperties[i];
msg.body.motion.pointers[i].coords = pointerCoords[i];
}
+ const status_t status = mChannel->sendMessage(&msg);
- return mChannel->sendMessage(&msg);
+ if (status == OK && verifyEvents()) {
+ Result<void> result =
+ mInputVerifier.processMovement(deviceId, source, action, pointerCount,
+ pointerProperties, pointerCoords, flags);
+ if (!result.ok()) {
+ LOG(ERROR) << "Bad stream: " << result.error();
+ return BAD_VALUE;
+ }
+ }
+ return status;
}
status_t InputPublisher::publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus) {
diff --git a/libs/input/tests/InputConsumer_test.cpp b/libs/input/tests/InputConsumer_test.cpp
index 06e19bb..226b892 100644
--- a/libs/input/tests/InputConsumer_test.cpp
+++ b/libs/input/tests/InputConsumer_test.cpp
@@ -70,11 +70,20 @@
[]() { return std::make_unique<LegacyResampler>(); });
}
- void invokeLooperCallback() const {
+ bool invokeLooperCallback() const {
sp<LooperCallback> callback;
- ASSERT_TRUE(mLooper->getFdStateDebug(mClientTestChannel->getFd(), /*ident=*/nullptr,
- /*events=*/nullptr, &callback, /*data=*/nullptr));
+ const bool found =
+ mLooper->getFdStateDebug(mClientTestChannel->getFd(), /*ident=*/nullptr,
+ /*events=*/nullptr, &callback, /*data=*/nullptr);
+ if (!found) {
+ return false;
+ }
+ if (callback == nullptr) {
+ LOG(FATAL) << "Looper has the fd of interest, but the callback is null!";
+ return false;
+ }
callback->handleEvent(mClientTestChannel->getFd(), ALOOPER_EVENT_INPUT, /*data=*/nullptr);
+ return true;
}
void assertOnBatchedInputEventPendingWasCalled() {
@@ -271,6 +280,27 @@
}
/**
+ * Check what happens when looper invokes callback after consumer has been destroyed.
+ * This reproduces a crash where the LooperEventCallback was added back to the Looper during
+ * destructor, thus allowing the looper callback to be invoked onto a null consumer object.
+ */
+TEST_F(InputConsumerTest, LooperCallbackInvokedAfterConsumerDestroyed) {
+ mClientTestChannel->enqueueMessage(
+ InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0}.action(ACTION_DOWN).build());
+ mClientTestChannel->enqueueMessage(
+ InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1}.action(ACTION_MOVE).build());
+ ASSERT_TRUE(invokeLooperCallback());
+ assertOnBatchedInputEventPendingWasCalled();
+ assertReceivedMotionEvent(WithMotionAction(ACTION_DOWN));
+ mClientTestChannel->assertFinishMessage(/*seq=*/0, /*handled=*/true);
+
+ // Now, destroy the consumer and invoke the looper callback again after it's been destroyed.
+ mConsumer.reset();
+ mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/false);
+ ASSERT_FALSE(invokeLooperCallback());
+}
+
+/**
* Send an event to the InputConsumer, but do not invoke "consumeBatchedInputEvents", thus leaving
* the input event unconsumed by the callbacks. Ensure that no crash occurs when the consumer is
* destroyed.
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index ac3a832..5ce4076 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -222,6 +222,8 @@
static_cast<int>(HAL_DATASPACE_BT2020_ITU_HLG));
static_assert(static_cast<int>(ADATASPACE_DEPTH) == static_cast<int>(HAL_DATASPACE_DEPTH));
static_assert(static_cast<int>(ADATASPACE_DYNAMIC_DEPTH) == static_cast<int>(HAL_DATASPACE_DYNAMIC_DEPTH));
+ static_assert(static_cast<int>(ADATASPACE_DISPLAY_BT2020) ==
+ static_cast<int>(HAL_DATASPACE_DISPLAY_BT2020));
if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) {
return -EINVAL;
diff --git a/libs/nativewindow/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h
index 8056d9a..295a307 100644
--- a/libs/nativewindow/include/android/data_space.h
+++ b/libs/nativewindow/include/android/data_space.h
@@ -578,6 +578,13 @@
*/
ADATASPACE_BT2020_ITU_HLG = 302383104, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_HLG |
// ADATASPACE_RANGE_LIMITED
+ /**
+ * sRGB-encoded BT. 2020
+ *
+ * Uses full range, sRGB transfer and BT2020 standard.
+ */
+ ADATASPACE_DISPLAY_BT2020 = 142999552, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_SRGB
+ // | ADATASPACE_RANGE_FULL
/**
* Depth
diff --git a/libs/nativewindow/rust/src/lib.rs b/libs/nativewindow/rust/src/lib.rs
index 014c912..9876362 100644
--- a/libs/nativewindow/rust/src/lib.rs
+++ b/libs/nativewindow/rust/src/lib.rs
@@ -31,11 +31,13 @@
};
use ffi::{
AHardwareBuffer, AHardwareBuffer_Desc, AHardwareBuffer_readFromParcel,
- AHardwareBuffer_writeToParcel,
+ AHardwareBuffer_writeToParcel, ARect,
};
+use std::ffi::c_void;
use std::fmt::{self, Debug, Formatter};
-use std::mem::ManuallyDrop;
-use std::ptr::{self, null_mut, NonNull};
+use std::mem::{forget, ManuallyDrop};
+use std::os::fd::{AsRawFd, BorrowedFd, FromRawFd, OwnedFd};
+use std::ptr::{self, null, null_mut, NonNull};
/// Wrapper around a C `AHardwareBuffer_Desc`.
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -267,10 +269,141 @@
rfu0: 0,
rfu1: 0,
};
- // SAFETY: neither the buffer nor AHardwareBuffer_Desc pointers will be null.
+ // SAFETY: The `AHardwareBuffer` pointer we wrap is always valid, and the
+ // AHardwareBuffer_Desc pointer is valid because it comes from a reference.
unsafe { ffi::AHardwareBuffer_describe(self.0.as_ref(), &mut buffer_desc) };
HardwareBufferDescription(buffer_desc)
}
+
+ /// Locks the hardware buffer for direct CPU access.
+ ///
+ /// # Safety
+ ///
+ /// - If `fence` is `None`, the caller must ensure that all writes to the buffer have completed
+ /// before calling this function.
+ /// - If the buffer has `AHARDWAREBUFFER_FORMAT_BLOB`, multiple threads or process may lock the
+ /// buffer simultaneously, but the caller must ensure that they don't access it simultaneously
+ /// and break Rust's aliasing rules, like any other shared memory.
+ /// - Otherwise if `usage` includes `AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY` or
+ /// `AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN`, the caller must ensure that no other threads or
+ /// processes lock the buffer simultaneously for any usage.
+ /// - Otherwise, the caller must ensure that no other threads lock the buffer for writing
+ /// simultaneously.
+ /// - If `rect` is not `None`, the caller must not modify the buffer outside of that rectangle.
+ pub unsafe fn lock<'a>(
+ &'a self,
+ usage: AHardwareBuffer_UsageFlags,
+ fence: Option<BorrowedFd>,
+ rect: Option<&ARect>,
+ ) -> Result<HardwareBufferGuard<'a>, StatusCode> {
+ let fence = if let Some(fence) = fence { fence.as_raw_fd() } else { -1 };
+ let rect = rect.map(ptr::from_ref).unwrap_or(null());
+ let mut address = null_mut();
+ // SAFETY: The `AHardwareBuffer` pointer we wrap is always valid, and the buffer address out
+ // pointer is valid because it comes from a reference. Our caller promises that writes have
+ // completed and there will be no simultaneous read/write locks.
+ let status = unsafe {
+ ffi::AHardwareBuffer_lock(self.0.as_ptr(), usage.0, fence, rect, &mut address)
+ };
+ status_result(status)?;
+ Ok(HardwareBufferGuard {
+ buffer: self,
+ address: NonNull::new(address)
+ .expect("AHardwareBuffer_lock set a null outVirtualAddress"),
+ })
+ }
+
+ /// Locks the hardware buffer for direct CPU access, returning information about the bytes per
+ /// pixel and stride as well.
+ ///
+ /// # Safety
+ ///
+ /// - If `fence` is `None`, the caller must ensure that all writes to the buffer have completed
+ /// before calling this function.
+ /// - If the buffer has `AHARDWAREBUFFER_FORMAT_BLOB`, multiple threads or process may lock the
+ /// buffer simultaneously, but the caller must ensure that they don't access it simultaneously
+ /// and break Rust's aliasing rules, like any other shared memory.
+ /// - Otherwise if `usage` includes `AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY` or
+ /// `AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN`, the caller must ensure that no other threads or
+ /// processes lock the buffer simultaneously for any usage.
+ /// - Otherwise, the caller must ensure that no other threads lock the buffer for writing
+ /// simultaneously.
+ pub unsafe fn lock_and_get_info<'a>(
+ &'a self,
+ usage: AHardwareBuffer_UsageFlags,
+ fence: Option<BorrowedFd>,
+ rect: Option<&ARect>,
+ ) -> Result<LockedBufferInfo<'a>, StatusCode> {
+ let fence = if let Some(fence) = fence { fence.as_raw_fd() } else { -1 };
+ let rect = rect.map(ptr::from_ref).unwrap_or(null());
+ let mut address = null_mut();
+ let mut bytes_per_pixel = 0;
+ let mut stride = 0;
+ // SAFETY: The `AHardwareBuffer` pointer we wrap is always valid, and the various out
+ // pointers are valid because they come from references. Our caller promises that writes have
+ // completed and there will be no simultaneous read/write locks.
+ let status = unsafe {
+ ffi::AHardwareBuffer_lockAndGetInfo(
+ self.0.as_ptr(),
+ usage.0,
+ fence,
+ rect,
+ &mut address,
+ &mut bytes_per_pixel,
+ &mut stride,
+ )
+ };
+ status_result(status)?;
+ Ok(LockedBufferInfo {
+ guard: HardwareBufferGuard {
+ buffer: self,
+ address: NonNull::new(address)
+ .expect("AHardwareBuffer_lockAndGetInfo set a null outVirtualAddress"),
+ },
+ bytes_per_pixel: bytes_per_pixel as u32,
+ stride: stride as u32,
+ })
+ }
+
+ /// Unlocks the hardware buffer from direct CPU access.
+ ///
+ /// Must be called after all changes to the buffer are completed by the caller. This will block
+ /// until the unlocking is complete and the buffer contents are updated.
+ fn unlock(&self) -> Result<(), StatusCode> {
+ // SAFETY: The `AHardwareBuffer` pointer we wrap is always valid.
+ let status = unsafe { ffi::AHardwareBuffer_unlock(self.0.as_ptr(), null_mut()) };
+ status_result(status)?;
+ Ok(())
+ }
+
+ /// Unlocks the hardware buffer from direct CPU access.
+ ///
+ /// Must be called after all changes to the buffer are completed by the caller.
+ ///
+ /// This may not block until all work is completed, but rather will return a file descriptor
+ /// which will be signalled once the unlocking is complete and the buffer contents is updated.
+ /// If `Ok(None)` is returned then unlocking has already completed and no further waiting is
+ /// necessary. The file descriptor may be passed to a subsequent call to [`Self::lock`].
+ pub fn unlock_with_fence(
+ &self,
+ guard: HardwareBufferGuard,
+ ) -> Result<Option<OwnedFd>, StatusCode> {
+ // Forget the guard so that its `Drop` implementation doesn't try to unlock the
+ // HardwareBuffer again.
+ forget(guard);
+
+ let mut fence = -2;
+ // SAFETY: The `AHardwareBuffer` pointer we wrap is always valid.
+ let status = unsafe { ffi::AHardwareBuffer_unlock(self.0.as_ptr(), &mut fence) };
+ let fence = if fence < 0 {
+ None
+ } else {
+ // SAFETY: `AHardwareBuffer_unlock` gives us ownership of the fence file descriptor.
+ Some(unsafe { OwnedFd::from_raw_fd(fence) })
+ };
+ status_result(status)?;
+ Ok(fence)
+ }
}
impl Drop for HardwareBuffer {
@@ -346,6 +479,37 @@
// according to the docs on the underlying gralloc calls)
unsafe impl Sync for HardwareBuffer {}
+/// A guard for when a `HardwareBuffer` is locked.
+///
+/// The `HardwareBuffer` will be unlocked when this is dropped, or may be unlocked via
+/// [`HardwareBuffer::unlock_with_fence`].
+#[derive(Debug)]
+pub struct HardwareBufferGuard<'a> {
+ buffer: &'a HardwareBuffer,
+ /// The address of the buffer in memory.
+ pub address: NonNull<c_void>,
+}
+
+impl<'a> Drop for HardwareBufferGuard<'a> {
+ fn drop(&mut self) {
+ self.buffer
+ .unlock()
+ .expect("Failed to unlock HardwareBuffer when dropping HardwareBufferGuard");
+ }
+}
+
+/// A guard for when a `HardwareBuffer` is locked, with additional information about the number of
+/// bytes per pixel and stride.
+#[derive(Debug)]
+pub struct LockedBufferInfo<'a> {
+ /// The locked buffer guard.
+ pub guard: HardwareBufferGuard<'a>,
+ /// The number of bytes used for each pixel in the buffer.
+ pub bytes_per_pixel: u32,
+ /// The stride in bytes between rows in the buffer.
+ pub stride: u32,
+}
+
#[cfg(test)]
mod test {
use super::*;
@@ -499,4 +663,108 @@
assert_eq!(buffer.description(), buffer_description);
assert_eq!(buffer2.description(), buffer_description);
}
+
+ #[test]
+ fn lock() {
+ let buffer = HardwareBuffer::new(&HardwareBufferDescription::new(
+ 1024,
+ 512,
+ 1,
+ AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+ 0,
+ ))
+ .expect("Failed to create buffer");
+
+ // SAFETY: No other threads or processes have access to the buffer.
+ let guard = unsafe {
+ buffer.lock(
+ AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+ None,
+ None,
+ )
+ }
+ .unwrap();
+
+ drop(guard);
+ }
+
+ #[test]
+ fn lock_with_rect() {
+ let buffer = HardwareBuffer::new(&HardwareBufferDescription::new(
+ 1024,
+ 512,
+ 1,
+ AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+ 0,
+ ))
+ .expect("Failed to create buffer");
+ let rect = ARect { left: 10, right: 20, top: 35, bottom: 45 };
+
+ // SAFETY: No other threads or processes have access to the buffer.
+ let guard = unsafe {
+ buffer.lock(
+ AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+ None,
+ Some(&rect),
+ )
+ }
+ .unwrap();
+
+ drop(guard);
+ }
+
+ #[test]
+ fn unlock_with_fence() {
+ let buffer = HardwareBuffer::new(&HardwareBufferDescription::new(
+ 1024,
+ 512,
+ 1,
+ AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+ 0,
+ ))
+ .expect("Failed to create buffer");
+
+ // SAFETY: No other threads or processes have access to the buffer.
+ let guard = unsafe {
+ buffer.lock(
+ AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+ None,
+ None,
+ )
+ }
+ .unwrap();
+
+ buffer.unlock_with_fence(guard).unwrap();
+ }
+
+ #[test]
+ fn lock_with_info() {
+ const WIDTH: u32 = 1024;
+ let buffer = HardwareBuffer::new(&HardwareBufferDescription::new(
+ WIDTH,
+ 512,
+ 1,
+ AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+ 0,
+ ))
+ .expect("Failed to create buffer");
+
+ // SAFETY: No other threads or processes have access to the buffer.
+ let info = unsafe {
+ buffer.lock_and_get_info(
+ AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+ None,
+ None,
+ )
+ }
+ .unwrap();
+
+ assert_eq!(info.bytes_per_pixel, 4);
+ assert_eq!(info.stride, WIDTH * 4);
+ drop(info);
+ }
}
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index d248ea0..7f207f0 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -105,6 +105,7 @@
"skia/filters/KawaseBlurDualFilter.cpp",
"skia/filters/KawaseBlurFilter.cpp",
"skia/filters/LinearEffect.cpp",
+ "skia/filters/LutShader.cpp",
"skia/filters/MouriMap.cpp",
"skia/filters/StretchShaderFactory.cpp",
"skia/filters/EdgeExtensionShaderFactory.cpp",
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index ec9d3ef..a93f6c3 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -543,6 +543,11 @@
}
}
+ if (graphicBuffer && parameters.layer.luts) {
+ shader = mLutShader.lutShader(shader, parameters.layer.luts,
+ toSkColorSpace(parameters.outputDataSpace));
+ }
+
if (parameters.requiresLinearEffect) {
const auto format = targetBuffer != nullptr
? std::optional<ui::PixelFormat>(
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index b5f8898..7be4c25 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -39,6 +39,7 @@
#include "filters/BlurFilter.h"
#include "filters/EdgeExtensionShaderFactory.h"
#include "filters/LinearEffect.h"
+#include "filters/LutShader.h"
#include "filters/StretchShaderFactory.h"
class SkData;
@@ -184,6 +185,7 @@
StretchShaderFactory mStretchShaderFactory;
EdgeExtensionShaderFactory mEdgeExtensionShaderFactory;
+ LutShader mLutShader;
sp<Fence> mLastDrawFence;
BlurFilter* mBlurFilter = nullptr;
diff --git a/libs/renderengine/skia/filters/LutShader.cpp b/libs/renderengine/skia/filters/LutShader.cpp
new file mode 100644
index 0000000..1e43ff3
--- /dev/null
+++ b/libs/renderengine/skia/filters/LutShader.cpp
@@ -0,0 +1,233 @@
+/*
+ * Copyright 2024 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 "LutShader.h"
+
+#include <SkTileMode.h>
+#include <common/trace.h>
+#include <cutils/ashmem.h>
+#include <math/half.h>
+#include <sys/mman.h>
+
+#include "include/core/SkColorSpace.h"
+#include "src/core/SkColorFilterPriv.h"
+
+using aidl::android::hardware::graphics::composer3::LutProperties;
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+static const SkString kShader = SkString(R"(
+ uniform shader image;
+ uniform shader lut;
+ uniform int size;
+ uniform int key;
+ uniform int dimension;
+ vec4 main(vec2 xy) {
+ float4 rgba = image.eval(xy);
+ float3 linear = toLinearSrgb(rgba.rgb);
+ if (dimension == 1) {
+ // RGB
+ if (key == 0) {
+ float indexR = linear.r * float(size - 1);
+ float indexG = linear.g * float(size - 1);
+ float indexB = linear.b * float(size - 1);
+ float gainR = lut.eval(vec2(indexR, 0.0) + 0.5).r;
+ float gainG = lut.eval(vec2(indexG, 0.0) + 0.5).r;
+ float gainB = lut.eval(vec2(indexB, 0.0) + 0.5).r;
+ return float4(linear.r * gainR, linear.g * gainG, linear.b * gainB, rgba.a);
+ // MAX_RGB
+ } else if (key == 1) {
+ float4 rgba = image.eval(xy);
+ float3 linear = toLinearSrgb(rgba.rgb);
+ float maxRGB = max(linear.r, max(linear.g, linear.b));
+ float index = maxRGB * float(size - 1);
+ float gain = lut.eval(vec2(index, 0.0) + 0.5).r;
+ return float4(linear * gain, rgba.a);
+ }
+ } else if (dimension == 3) {
+ if (key == 0) {
+ float tx = linear.r * float(size - 1);
+ float ty = linear.g * float(size - 1);
+ float tz = linear.b * float(size - 1);
+
+ // calculate lower and upper bounds for each dimension
+ int x = int(tx);
+ int y = int(ty);
+ int z = int(tz);
+
+ int i000 = x + y * size + z * size * size;
+ int i100 = i000 + 1;
+ int i010 = i000 + size;
+ int i110 = i000 + size + 1;
+ int i001 = i000 + size * size;
+ int i101 = i000 + size * size + 1;
+ int i011 = i000 + size * size + size;
+ int i111 = i000 + size * size + size + 1;
+
+ // get 1d normalized indices
+ float c000 = float(i000) / float(size * size * size);
+ float c100 = float(i100) / float(size * size * size);
+ float c010 = float(i010) / float(size * size * size);
+ float c110 = float(i110) / float(size * size * size);
+ float c001 = float(i001) / float(size * size * size);
+ float c101 = float(i101) / float(size * size * size);
+ float c011 = float(i011) / float(size * size * size);
+ float c111 = float(i111) / float(size * size * size);
+
+ //TODO(b/377984618): support Tetrahedral interpolation
+ // perform trilinear interpolation
+ float3 c00 = mix(lut.eval(vec2(c000, 0.0) + 0.5).rgb,
+ lut.eval(vec2(c100, 0.0) + 0.5).rgb, linear.r);
+ float3 c01 = mix(lut.eval(vec2(c001, 0.0) + 0.5).rgb,
+ lut.eval(vec2(c101, 0.0) + 0.5).rgb, linear.r);
+ float3 c10 = mix(lut.eval(vec2(c010, 0.0) + 0.5).rgb,
+ lut.eval(vec2(c110, 0.0) + 0.5).rgb, linear.r);
+ float3 c11 = mix(lut.eval(vec2(c011, 0.0) + 0.5).rgb,
+ lut.eval(vec2(c111, 0.0) + 0.5).rgb, linear.r);
+
+ float3 c0 = mix(c00, c10, linear.g);
+ float3 c1 = mix(c01, c11, linear.g);
+
+ float3 val = mix(c0, c1, linear.b);
+
+ return float4(val, rgba.a);
+ }
+ }
+ return rgba;
+ })");
+
+sk_sp<SkShader> LutShader::generateLutShader(sk_sp<SkShader> input,
+ const std::vector<float>& buffers,
+ const int32_t offset, const int32_t length,
+ const int32_t dimension, const int32_t size,
+ const int32_t samplingKey) {
+ SFTRACE_NAME("lut shader");
+ std::vector<half> buffer(length * 4); // 4 is for RGBA
+ auto d = static_cast<LutProperties::Dimension>(dimension);
+ if (d == LutProperties::Dimension::ONE_D) {
+ auto it = buffers.begin() + offset;
+ std::generate(buffer.begin(), buffer.end(), [it, i = 0]() mutable {
+ float val = (i++ % 4 == 0) ? *it++ : 0.0f;
+ return half(val);
+ });
+ } else {
+ for (int i = 0; i < length; i++) {
+ buffer[i * 4] = half(buffers[offset + i]);
+ buffer[i * 4 + 1] = half(buffers[offset + length + i]);
+ buffer[i * 4 + 2] = half(buffers[offset + length * 2 + i]);
+ buffer[i * 4 + 3] = half(0);
+ }
+ }
+ /**
+ * 1D Lut(rgba)
+ * (R0, 0, 0, 0)
+ * (R1, 0, 0, 0)
+ * ...
+ *
+ * 3D Lut
+ * (R0, G0, B0, 0)
+ * (R1, G1, B1, 0)
+ * ...
+ */
+ SkImageInfo info = SkImageInfo::Make(length /* the number of rgba */ * 4, 1,
+ kRGBA_F16_SkColorType, kPremul_SkAlphaType);
+ SkBitmap bitmap;
+ bitmap.allocPixels(info);
+ if (!bitmap.installPixels(info, buffer.data(), info.minRowBytes())) {
+ LOG_ALWAYS_FATAL("unable to install pixels");
+ }
+
+ sk_sp<SkImage> lutImage = SkImages::RasterFromBitmap(bitmap);
+ mBuilder->child("image") = input;
+ mBuilder->child("lut") =
+ lutImage->makeRawShader(SkTileMode::kClamp, SkTileMode::kClamp,
+ d == LutProperties::Dimension::ONE_D
+ ? SkSamplingOptions(SkFilterMode::kLinear)
+ : SkSamplingOptions());
+
+ const int uSize = static_cast<int>(size);
+ const int uKey = static_cast<int>(samplingKey);
+ const int uDimension = static_cast<int>(dimension);
+ mBuilder->uniform("size") = uSize;
+ mBuilder->uniform("key") = uKey;
+ mBuilder->uniform("dimension") = uDimension;
+ return mBuilder->makeShader();
+}
+
+sk_sp<SkShader> LutShader::lutShader(sk_sp<SkShader>& input,
+ std::shared_ptr<gui::DisplayLuts> displayLuts,
+ sk_sp<SkColorSpace> outColorSpace) {
+ if (mBuilder == nullptr) {
+ const static SkRuntimeEffect::Result instance = SkRuntimeEffect::MakeForShader(kShader);
+ mBuilder = std::make_unique<SkRuntimeShaderBuilder>(instance.effect);
+ }
+
+ auto& fd = displayLuts->getLutFileDescriptor();
+ if (fd.ok()) {
+ // de-gamma the image without changing the primaries
+ SkImage* baseImage = input->isAImage((SkMatrix*)nullptr, (SkTileMode*)nullptr);
+ sk_sp<SkColorSpace> baseColorSpace = baseImage && baseImage->colorSpace()
+ ? baseImage->refColorSpace()
+ : SkColorSpace::MakeSRGB();
+ sk_sp<SkColorSpace> lutMathColorSpace = baseColorSpace->makeLinearGamma();
+ input = input->makeWithWorkingColorSpace(lutMathColorSpace);
+
+ auto& offsets = displayLuts->offsets;
+ auto& lutProperties = displayLuts->lutProperties;
+ std::vector<float> buffers;
+ int fullLength = offsets[lutProperties.size() - 1];
+ if (lutProperties[lutProperties.size() - 1].dimension == 1) {
+ fullLength += lutProperties[lutProperties.size() - 1].size;
+ } else {
+ fullLength += (lutProperties[lutProperties.size() - 1].size *
+ lutProperties[lutProperties.size() - 1].size *
+ lutProperties[lutProperties.size() - 1].size * 3);
+ }
+ size_t bufferSize = fullLength * sizeof(float);
+
+ // decode the shared memory of luts
+ float* ptr =
+ (float*)mmap(NULL, bufferSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0);
+ if (ptr == MAP_FAILED) {
+ LOG_ALWAYS_FATAL("mmap failed");
+ }
+ buffers = std::vector<float>(ptr, ptr + fullLength);
+ munmap(ptr, bufferSize);
+
+ for (size_t i = 0; i < offsets.size(); i++) {
+ int bufferSizePerLut = (i == offsets.size() - 1) ? buffers.size() - offsets[i]
+ : offsets[i + 1] - offsets[i];
+ // divide by 3 for 3d Lut because of 3 (RGB) channels
+ if (static_cast<LutProperties::Dimension>(lutProperties[i].dimension) ==
+ LutProperties::Dimension::THREE_D) {
+ bufferSizePerLut /= 3;
+ }
+ input = generateLutShader(input, buffers, offsets[i], bufferSizePerLut,
+ lutProperties[i].dimension, lutProperties[i].size,
+ lutProperties[i].samplingKey);
+ }
+
+ auto colorXformLutToDst =
+ SkColorFilterPriv::MakeColorSpaceXform(lutMathColorSpace, outColorSpace);
+ input = input->makeWithColorFilter(colorXformLutToDst);
+ }
+ return input;
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/skia/filters/LutShader.h b/libs/renderengine/skia/filters/LutShader.h
new file mode 100644
index 0000000..ce3e059
--- /dev/null
+++ b/libs/renderengine/skia/filters/LutShader.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2024 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 <SkBitmap.h>
+#include <SkImage.h>
+#include <SkRuntimeEffect.h>
+
+#include <aidl/android/hardware/graphics/composer3/LutProperties.h>
+#include <gui/DisplayLuts.h>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+class LutShader {
+public:
+ sk_sp<SkShader> lutShader(sk_sp<SkShader>& input, std::shared_ptr<gui::DisplayLuts> displayLuts,
+ sk_sp<SkColorSpace> outColorSpace);
+
+private:
+ sk_sp<SkShader> generateLutShader(sk_sp<SkShader> input, const std::vector<float>& buffers,
+ const int32_t offset, const int32_t length,
+ const int32_t dimension, const int32_t size,
+ const int32_t samplingKey);
+ std::unique_ptr<SkRuntimeShaderBuilder> mBuilder;
+};
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/ui/Gralloc5.cpp b/libs/ui/Gralloc5.cpp
index c9ec036..2143f79 100644
--- a/libs/ui/Gralloc5.cpp
+++ b/libs/ui/Gralloc5.cpp
@@ -23,7 +23,6 @@
#include <aidlcommonsupport/NativeHandle.h>
#include <android/binder_manager.h>
#include <android/hardware/graphics/mapper/utils/IMapperMetadataTypes.h>
-#include <android/llndk-versioning.h>
#include <binder/IPCThreadState.h>
#include <dlfcn.h>
#include <ui/FatVector.h>
@@ -91,7 +90,7 @@
}
void* so = nullptr;
- if API_LEVEL_AT_LEAST (__ANDROID_API_V__, 202404) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
so = AServiceManager_openDeclaredPassthroughHal("mapper", mapperSuffix.c_str(),
RTLD_LOCAL | RTLD_NOW);
} else {
diff --git a/libs/ui/include/ui/PictureProfileHandle.h b/libs/ui/include/ui/PictureProfileHandle.h
index 9b709b6..f840650 100644
--- a/libs/ui/include/ui/PictureProfileHandle.h
+++ b/libs/ui/include/ui/PictureProfileHandle.h
@@ -39,7 +39,7 @@
static const PictureProfileHandle NONE;
PictureProfileHandle() { *this = NONE; }
- PictureProfileHandle(PictureProfileId id) : mId(id) {}
+ explicit PictureProfileHandle(PictureProfileId id) : mId(id) {}
PictureProfileId const& getId() const { return mId; }
diff --git a/services/audiomanager/IAudioManager.cpp b/services/audiomanager/IAudioManager.cpp
index da1aae2..f8a38d1 100644
--- a/services/audiomanager/IAudioManager.cpp
+++ b/services/audiomanager/IAudioManager.cpp
@@ -87,12 +87,15 @@
}
virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event,
- audio_port_handle_t eventId) {
+ const std::vector<audio_port_handle_t>& eventIds) {
Parcel data, reply;
data.writeInterfaceToken(IAudioManager::getInterfaceDescriptor());
data.writeInt32((int32_t) piid);
data.writeInt32((int32_t) event);
- data.writeInt32((int32_t) eventId);
+ data.writeInt32((int32_t) eventIds.size());
+ for (auto eventId: eventIds) {
+ data.writeInt32((int32_t) eventId);
+ }
return remote()->transact(PLAYER_EVENT, data, &reply, IBinder::FLAG_ONEWAY);
}
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 35ba48f..013ef86 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -129,7 +129,8 @@
{"multi_intensity", InputLightClass::MULTI_INTENSITY},
{"max_brightness", InputLightClass::MAX_BRIGHTNESS},
{"kbd_backlight", InputLightClass::KEYBOARD_BACKLIGHT},
- {"mic_mute", InputLightClass::KEYBOARD_MIC_MUTE}};
+ {"mic_mute", InputLightClass::KEYBOARD_MIC_MUTE},
+ {"mute", InputLightClass::KEYBOARD_VOLUME_MUTE}};
// Mapping for input multicolor led class node names.
// https://www.kernel.org/doc/html/latest/leds/leds-class-multicolor.html
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index a2b7e82..ada6653 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -19,6 +19,7 @@
#include "InputReader.h"
#include <android-base/stringprintf.h>
+#include <com_android_input_flags.h>
#include <errno.h>
#include <input/Keyboard.h>
#include <input/VirtualKeyMap.h>
@@ -903,7 +904,9 @@
bool InputReader::setKernelWakeEnabled(int32_t deviceId, bool enabled) {
std::scoped_lock _l(mLock);
-
+ if (!com::android::input::flags::set_input_device_kernel_wake()){
+ return false;
+ }
InputDevice* device = findInputDeviceLocked(deviceId);
if (device) {
return device->setKernelWakeEnabled(enabled);
diff --git a/services/inputflinger/reader/controller/PeripheralController.cpp b/services/inputflinger/reader/controller/PeripheralController.cpp
index 49ad8b5..9eeb2b2 100644
--- a/services/inputflinger/reader/controller/PeripheralController.cpp
+++ b/services/inputflinger/reader/controller/PeripheralController.cpp
@@ -514,6 +514,8 @@
type = InputDeviceLightType::KEYBOARD_BACKLIGHT;
} else if (rawInfo.flags.test(InputLightClass::KEYBOARD_MIC_MUTE)) {
type = InputDeviceLightType::KEYBOARD_MIC_MUTE;
+ } else if (rawInfo.flags.test(InputLightClass::KEYBOARD_VOLUME_MUTE)) {
+ type = InputDeviceLightType::KEYBOARD_VOLUME_MUTE;
} else {
type = InputDeviceLightType::INPUT;
}
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 4336945..5839b4c 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -179,6 +179,8 @@
KEYBOARD_BACKLIGHT = 0x00000100,
/* The input light has mic_mute name */
KEYBOARD_MIC_MUTE = 0x00000200,
+ /* The input light has mute name */
+ KEYBOARD_VOLUME_MUTE = 0x00000400,
};
enum class InputBatteryClass : uint32_t {
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index abe7a5f..4744dd0 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -48,7 +48,7 @@
inline InputReaderContext* getContext() { return mContext; }
inline int32_t getId() const { return mId; }
inline int32_t getControllerNumber() const { return mControllerNumber; }
- inline int32_t getGeneration() const { return mGeneration; }
+ inline virtual int32_t getGeneration() const { return mGeneration; }
inline const std::string getName() const { return mIdentifier.name; }
inline const std::string getDescriptor() { return mIdentifier.descriptor; }
inline std::optional<std::string> getBluetoothAddress() const {
@@ -59,7 +59,7 @@
inline virtual uint32_t getSources() const { return mSources; }
inline bool hasEventHubDevices() const { return !mDevices.empty(); }
- inline bool isExternal() { return mIsExternal; }
+ inline virtual bool isExternal() { return mIsExternal; }
inline std::optional<uint8_t> getAssociatedDisplayPort() const {
return mAssociatedDisplayPort;
}
@@ -79,7 +79,7 @@
inline bool isIgnored() { return !getMapperCount() && !mController; }
- inline KeyboardType getKeyboardType() const { return mKeyboardType; }
+ inline virtual KeyboardType getKeyboardType() const { return mKeyboardType; }
bool isEnabled();
@@ -124,7 +124,7 @@
int32_t getMetaState();
void setKeyboardType(KeyboardType keyboardType);
- void bumpGeneration();
+ virtual void bumpGeneration();
[[nodiscard]] NotifyDeviceResetArgs notifyReset(nsecs_t when);
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index 1959423..54270eb 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -99,6 +99,8 @@
out << "Current classification: " << ftl::enum_string(mCurrentClassification) << "\n";
out << "Is hovering: " << mIsHovering << "\n";
out << "Enable Tap Timestamp: " << mWhenToEnableTapToClick << "\n";
+ out << "Three finger tap shortcut enabled: "
+ << (mThreeFingerTapShortcutEnabled ? "enabled" : "disabled") << "\n";
return out.str();
}
diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp
index ca797dc..8235c90 100644
--- a/services/inputflinger/tests/InputMapperTest.cpp
+++ b/services/inputflinger/tests/InputMapperTest.cpp
@@ -100,9 +100,14 @@
std::list<NotifyArgs> InputMapperUnitTest::process(nsecs_t when, int32_t type, int32_t code,
int32_t value) {
+ return process(when, when, type, code, value);
+}
+
+std::list<NotifyArgs> InputMapperUnitTest::process(nsecs_t when, nsecs_t readTime, int32_t type,
+ int32_t code, int32_t value) {
RawEvent event;
event.when = when;
- event.readTime = when;
+ event.readTime = readTime;
event.deviceId = mMapper->getDeviceContext().getEventHubId();
event.type = type;
event.code = code;
diff --git a/services/inputflinger/tests/InputMapperTest.h b/services/inputflinger/tests/InputMapperTest.h
index b6c5812..10ef6f1 100644
--- a/services/inputflinger/tests/InputMapperTest.h
+++ b/services/inputflinger/tests/InputMapperTest.h
@@ -56,6 +56,8 @@
std::list<NotifyArgs> process(int32_t type, int32_t code, int32_t value);
std::list<NotifyArgs> process(nsecs_t when, int32_t type, int32_t code, int32_t value);
+ std::list<NotifyArgs> process(nsecs_t when, nsecs_t readTime, int32_t type, int32_t code,
+ int32_t value);
InputDeviceIdentifier mIdentifier;
MockEventHubInterface mMockEventHub;
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index ee3b2a2..9d2256f 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -3031,1048 +3031,6 @@
mapper.assertProcessWasCalled();
}
-// --- KeyboardInputMapperTest ---
-
-class KeyboardInputMapperTest : public InputMapperTest {
-protected:
- void SetUp() override {
- InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::KEYBOARD |
- InputDeviceClass::ALPHAKEY);
- }
- const std::string UNIQUE_ID = "local:0";
- const KeyboardLayoutInfo DEVICE_KEYBOARD_LAYOUT_INFO = KeyboardLayoutInfo("en-US", "qwerty");
- void prepareDisplay(ui::Rotation orientation);
-
- void testDPadKeyRotation(KeyboardInputMapper& mapper, int32_t originalScanCode,
- int32_t originalKeyCode, int32_t rotatedKeyCode,
- ui::LogicalDisplayId displayId = ui::LogicalDisplayId::INVALID);
-};
-
-/* Similar to setDisplayInfoAndReconfigure, but pre-populates all parameters except for the
- * orientation.
- */
-void KeyboardInputMapperTest::prepareDisplay(ui::Rotation orientation) {
- setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation, UNIQUE_ID,
- NO_PORT, ViewportType::INTERNAL);
-}
-
-void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper& mapper,
- int32_t originalScanCode, int32_t originalKeyCode,
- int32_t rotatedKeyCode,
- ui::LogicalDisplayId displayId) {
- NotifyKeyArgs args;
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, originalScanCode, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
- ASSERT_EQ(originalScanCode, args.scanCode);
- ASSERT_EQ(rotatedKeyCode, args.keyCode);
- ASSERT_EQ(displayId, args.displayId);
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, originalScanCode, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
- ASSERT_EQ(originalScanCode, args.scanCode);
- ASSERT_EQ(rotatedKeyCode, args.keyCode);
- ASSERT_EQ(displayId, args.displayId);
-}
-
-TEST_F(KeyboardInputMapperTest, GetSources) {
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
-
- ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, mapper.getSources());
-}
-
-TEST_F(KeyboardInputMapperTest, Process_SimpleKeyPress) {
- const int32_t USAGE_A = 0x070004;
- const int32_t USAGE_UNKNOWN = 0x07ffff;
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
- mFakeEventHub->addKey(EVENTHUB_ID, 0, USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE);
- mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_NUMLOCK, AKEYCODE_NUM_LOCK, POLICY_FLAG_WAKE);
- mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_CAPSLOCK, AKEYCODE_CAPS_LOCK, POLICY_FLAG_WAKE);
- mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_SCROLLLOCK, AKEYCODE_SCROLL_LOCK, POLICY_FLAG_WAKE);
-
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
- // Initial metastate is AMETA_NONE.
- ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
-
- // Key down by scan code.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_HOME, 1);
- NotifyKeyArgs args;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(DEVICE_ID, args.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
- ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
- ASSERT_EQ(AKEYCODE_HOME, args.keyCode);
- ASSERT_EQ(KEY_HOME, args.scanCode);
- ASSERT_EQ(AMETA_NONE, args.metaState);
- ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
- ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
- ASSERT_EQ(ARBITRARY_TIME, args.downTime);
-
- // Key up by scan code.
- process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_HOME, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(DEVICE_ID, args.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
- ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime);
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
- ASSERT_EQ(AKEYCODE_HOME, args.keyCode);
- ASSERT_EQ(KEY_HOME, args.scanCode);
- ASSERT_EQ(AMETA_NONE, args.metaState);
- ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
- ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
- ASSERT_EQ(ARBITRARY_TIME, args.downTime);
-
- // Key down by usage code.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_SCAN, USAGE_A);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, 0, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(DEVICE_ID, args.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
- ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
- ASSERT_EQ(AKEYCODE_A, args.keyCode);
- ASSERT_EQ(0, args.scanCode);
- ASSERT_EQ(AMETA_NONE, args.metaState);
- ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
- ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
- ASSERT_EQ(ARBITRARY_TIME, args.downTime);
-
- // Key up by usage code.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_SCAN, USAGE_A);
- process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, 0, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(DEVICE_ID, args.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
- ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime);
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
- ASSERT_EQ(AKEYCODE_A, args.keyCode);
- ASSERT_EQ(0, args.scanCode);
- ASSERT_EQ(AMETA_NONE, args.metaState);
- ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
- ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
- ASSERT_EQ(ARBITRARY_TIME, args.downTime);
-
- // Key down with unknown scan code or usage code.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UNKNOWN, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(DEVICE_ID, args.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
- ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
- ASSERT_EQ(0, args.keyCode);
- ASSERT_EQ(KEY_UNKNOWN, args.scanCode);
- ASSERT_EQ(AMETA_NONE, args.metaState);
- ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
- ASSERT_EQ(0U, args.policyFlags);
- ASSERT_EQ(ARBITRARY_TIME, args.downTime);
-
- // Key up with unknown scan code or usage code.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
- process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_UNKNOWN, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(DEVICE_ID, args.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
- ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime);
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
- ASSERT_EQ(0, args.keyCode);
- ASSERT_EQ(KEY_UNKNOWN, args.scanCode);
- ASSERT_EQ(AMETA_NONE, args.metaState);
- ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
- ASSERT_EQ(0U, args.policyFlags);
- ASSERT_EQ(ARBITRARY_TIME, args.downTime);
-}
-
-TEST_F(KeyboardInputMapperTest, Process_KeyRemapping) {
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_B, 0, AKEYCODE_B, 0);
-
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
-
- mFakeEventHub->setKeyRemapping(EVENTHUB_ID, {{AKEYCODE_A, AKEYCODE_B}});
- // Key down by scan code.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_A, 1);
- NotifyKeyArgs args;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(AKEYCODE_B, args.keyCode);
-
- // Key up by scan code.
- process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_A, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(AKEYCODE_B, args.keyCode);
-}
-
-/**
- * Ensure that the readTime is set to the time when the EV_KEY is received.
- */
-TEST_F(KeyboardInputMapperTest, Process_SendsReadTime) {
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
-
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
- NotifyKeyArgs args;
-
- // Key down
- process(mapper, ARBITRARY_TIME, /*readTime=*/12, EV_KEY, KEY_HOME, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(12, args.readTime);
-
- // Key up
- process(mapper, ARBITRARY_TIME, /*readTime=*/15, EV_KEY, KEY_HOME, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(15, args.readTime);
-}
-
-TEST_F(KeyboardInputMapperTest, Process_ShouldUpdateMetaState) {
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFTSHIFT, 0, AKEYCODE_SHIFT_LEFT, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_NUMLOCK, AKEYCODE_NUM_LOCK, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_CAPSLOCK, AKEYCODE_CAPS_LOCK, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_SCROLLLOCK, AKEYCODE_SCROLL_LOCK, 0);
-
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
-
- // Initial metastate is AMETA_NONE.
- ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
-
- // Metakey down.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_LEFTSHIFT, 1);
- NotifyKeyArgs args;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper.getMetaState());
- ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertUpdateGlobalMetaStateWasCalled());
-
- // Key down.
- process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_A, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper.getMetaState());
-
- // Key up.
- process(mapper, ARBITRARY_TIME + 2, READ_TIME, EV_KEY, KEY_A, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper.getMetaState());
-
- // Metakey up.
- process(mapper, ARBITRARY_TIME + 3, READ_TIME, EV_KEY, KEY_LEFTSHIFT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(AMETA_NONE, args.metaState);
- ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
- ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertUpdateGlobalMetaStateWasCalled());
-}
-
-TEST_F(KeyboardInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateDPad) {
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
-
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
-
- prepareDisplay(ui::ROTATION_90);
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
- KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
- KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_RIGHT));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
- KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_DOWN));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
- KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT));
-}
-
-TEST_F(KeyboardInputMapperTest, Process_WhenOrientationAware_ShouldRotateDPad) {
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
-
- addConfigurationProperty("keyboard.orientationAware", "1");
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
-
- prepareDisplay(ui::ROTATION_0);
- ASSERT_NO_FATAL_FAILURE(
- testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, DISPLAY_ID));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
- AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN,
- AKEYCODE_DPAD_DOWN, DISPLAY_ID));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT,
- AKEYCODE_DPAD_LEFT, DISPLAY_ID));
-
- clearViewports();
- prepareDisplay(ui::ROTATION_90);
- ASSERT_NO_FATAL_FAILURE(
- testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, DISPLAY_ID));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
- AKEYCODE_DPAD_UP, DISPLAY_ID));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN,
- AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT,
- AKEYCODE_DPAD_DOWN, DISPLAY_ID));
-
- clearViewports();
- prepareDisplay(ui::ROTATION_180);
- ASSERT_NO_FATAL_FAILURE(
- testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN, DISPLAY_ID));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
- AKEYCODE_DPAD_LEFT, DISPLAY_ID));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN,
- AKEYCODE_DPAD_UP, DISPLAY_ID));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT,
- AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
-
- clearViewports();
- prepareDisplay(ui::ROTATION_270);
- ASSERT_NO_FATAL_FAILURE(
- testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
- AKEYCODE_DPAD_DOWN, DISPLAY_ID));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN,
- AKEYCODE_DPAD_LEFT, DISPLAY_ID));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT,
- AKEYCODE_DPAD_UP, DISPLAY_ID));
-
- // Special case: if orientation changes while key is down, we still emit the same keycode
- // in the key up as we did in the key down.
- NotifyKeyArgs args;
- clearViewports();
- prepareDisplay(ui::ROTATION_270);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
- ASSERT_EQ(KEY_UP, args.scanCode);
- ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode);
-
- clearViewports();
- prepareDisplay(ui::ROTATION_180);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
- ASSERT_EQ(KEY_UP, args.scanCode);
- ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode);
-}
-
-TEST_F(KeyboardInputMapperTest, DisplayIdConfigurationChange_NotOrientationAware) {
- // If the keyboard is not orientation aware,
- // key events should not be associated with a specific display id
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
-
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
- NotifyKeyArgs args;
-
- // Display id should be LogicalDisplayId::INVALID without any display configuration.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(ui::LogicalDisplayId::INVALID, args.displayId);
-
- prepareDisplay(ui::ROTATION_0);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(ui::LogicalDisplayId::INVALID, args.displayId);
-}
-
-TEST_F(KeyboardInputMapperTest, DisplayIdConfigurationChange_OrientationAware) {
- // If the keyboard is orientation aware,
- // key events should be associated with the internal viewport
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
-
- addConfigurationProperty("keyboard.orientationAware", "1");
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
- NotifyKeyArgs args;
-
- // Display id should be LogicalDisplayId::INVALID without any display configuration.
- // ^--- already checked by the previous test
-
- setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
- UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(DISPLAY_ID, args.displayId);
-
- constexpr ui::LogicalDisplayId newDisplayId = ui::LogicalDisplayId{2};
- clearViewports();
- setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
- UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(newDisplayId, args.displayId);
-}
-
-TEST_F(KeyboardInputMapperTest, GetKeyCodeState) {
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
-
- mFakeEventHub->setKeyCodeState(EVENTHUB_ID, AKEYCODE_A, 1);
- ASSERT_EQ(1, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
-
- mFakeEventHub->setKeyCodeState(EVENTHUB_ID, AKEYCODE_A, 0);
- ASSERT_EQ(0, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
-}
-
-TEST_F(KeyboardInputMapperTest, GetKeyCodeForKeyLocation) {
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
-
- mFakeEventHub->addKeyCodeMapping(EVENTHUB_ID, AKEYCODE_Y, AKEYCODE_Z);
- ASSERT_EQ(AKEYCODE_Z, mapper.getKeyCodeForKeyLocation(AKEYCODE_Y))
- << "If a mapping is available, the result is equal to the mapping";
-
- ASSERT_EQ(AKEYCODE_A, mapper.getKeyCodeForKeyLocation(AKEYCODE_A))
- << "If no mapping is available, the result is the key location";
-}
-
-TEST_F(KeyboardInputMapperTest, GetScanCodeState) {
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
-
- mFakeEventHub->setScanCodeState(EVENTHUB_ID, KEY_A, 1);
- ASSERT_EQ(1, mapper.getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
-
- mFakeEventHub->setScanCodeState(EVENTHUB_ID, KEY_A, 0);
- ASSERT_EQ(0, mapper.getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
-}
-
-TEST_F(KeyboardInputMapperTest, MarkSupportedKeyCodes) {
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
-
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0);
-
- uint8_t flags[2] = { 0, 0 };
- ASSERT_TRUE(mapper.markSupportedKeyCodes(AINPUT_SOURCE_ANY, {AKEYCODE_A, AKEYCODE_B}, flags));
- ASSERT_TRUE(flags[0]);
- ASSERT_FALSE(flags[1]);
-}
-
-TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleMetaStateAndLeds) {
- mFakeEventHub->addLed(EVENTHUB_ID, LED_CAPSL, true /*initially on*/);
- mFakeEventHub->addLed(EVENTHUB_ID, LED_NUML, false /*initially off*/);
- mFakeEventHub->addLed(EVENTHUB_ID, LED_SCROLLL, false /*initially off*/);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
-
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
- // Initial metastate is AMETA_NONE.
- ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
-
- // Initialization should have turned all of the lights off.
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
-
- // Toggle caps lock on.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
- ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
- ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState());
-
- // Toggle num lock on.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
- ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
- ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
- ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper.getMetaState());
-
- // Toggle caps lock off.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
- ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
- ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper.getMetaState());
-
- // Toggle scroll lock on.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
- ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
- ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
- ASSERT_EQ(AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState());
-
- // Toggle num lock off.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
- ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
- ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper.getMetaState());
-
- // Toggle scroll lock off.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
- ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
-}
-
-TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) {
- // keyboard 1.
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
-
- // keyboard 2.
- const std::string USB2 = "USB2";
- const std::string DEVICE_NAME2 = "KEYBOARD2";
- constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
- constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1;
- std::shared_ptr<InputDevice> device2 =
- newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID,
- ftl::Flags<InputDeviceClass>(0));
-
- mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
- mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0);
- mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
- mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
-
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
-
- device2->addEmptyEventHubDevice(SECOND_EVENTHUB_ID);
- KeyboardInputMapper& mapper2 =
- device2->constructAndAddMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID,
- mFakePolicy
- ->getReaderConfiguration(),
- AINPUT_SOURCE_KEYBOARD);
- std::list<NotifyArgs> unused =
- device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- /*changes=*/{});
- unused += device2->reset(ARBITRARY_TIME);
-
- // Prepared displays and associated info.
- constexpr uint8_t hdmi1 = 0;
- constexpr uint8_t hdmi2 = 1;
- const std::string SECONDARY_UNIQUE_ID = "local:1";
-
- mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1);
- mFakePolicy->addInputPortAssociation(USB2, hdmi2);
-
- // No associated display viewport found, should disable the device.
- unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::Change::DISPLAY_INFO);
- ASSERT_FALSE(device2->isEnabled());
-
- // Prepare second display.
- constexpr ui::LogicalDisplayId newDisplayId = ui::LogicalDisplayId{2};
- setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
- UNIQUE_ID, hdmi1, ViewportType::INTERNAL);
- setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
- SECONDARY_UNIQUE_ID, hdmi2, ViewportType::EXTERNAL);
- // Default device will reconfigure above, need additional reconfiguration for another device.
- unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::Change::DISPLAY_INFO);
-
- // Device should be enabled after the associated display is found.
- ASSERT_TRUE(mDevice->isEnabled());
- ASSERT_TRUE(device2->isEnabled());
-
- // Test pad key events
- ASSERT_NO_FATAL_FAILURE(
- testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, DISPLAY_ID));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
- AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN,
- AKEYCODE_DPAD_DOWN, DISPLAY_ID));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT,
- AKEYCODE_DPAD_LEFT, DISPLAY_ID));
-
- ASSERT_NO_FATAL_FAILURE(
- testDPadKeyRotation(mapper2, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, newDisplayId));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
- AKEYCODE_DPAD_RIGHT, newDisplayId));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_DOWN, AKEYCODE_DPAD_DOWN,
- AKEYCODE_DPAD_DOWN, newDisplayId));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_LEFT, AKEYCODE_DPAD_LEFT,
- AKEYCODE_DPAD_LEFT, newDisplayId));
-}
-
-TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleAfterReattach) {
- mFakeEventHub->addLed(EVENTHUB_ID, LED_CAPSL, true /*initially on*/);
- mFakeEventHub->addLed(EVENTHUB_ID, LED_NUML, false /*initially off*/);
- mFakeEventHub->addLed(EVENTHUB_ID, LED_SCROLLL, false /*initially off*/);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
-
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
- // Initial metastate is AMETA_NONE.
- ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
-
- // Initialization should have turned all of the lights off.
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
-
- // Toggle caps lock on.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
- ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
- ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState());
-
- // Toggle num lock on.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
- ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
- ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper.getMetaState());
-
- // Toggle scroll lock on.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
- ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
- ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState());
-
- mFakeEventHub->removeDevice(EVENTHUB_ID);
- mReader->loopOnce();
-
- // keyboard 2 should default toggle keys.
- const std::string USB2 = "USB2";
- const std::string DEVICE_NAME2 = "KEYBOARD2";
- constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
- constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1;
- std::shared_ptr<InputDevice> device2 =
- newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID,
- ftl::Flags<InputDeviceClass>(0));
- mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_CAPSL, true /*initially on*/);
- mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_NUML, false /*initially off*/);
- mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_SCROLLL, false /*initially off*/);
- mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
- mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
- mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
-
- device2->addEmptyEventHubDevice(SECOND_EVENTHUB_ID);
- KeyboardInputMapper& mapper2 =
- device2->constructAndAddMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID,
- mFakePolicy
- ->getReaderConfiguration(),
- AINPUT_SOURCE_KEYBOARD);
- std::list<NotifyArgs> unused =
- device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- /*changes=*/{});
- unused += device2->reset(ARBITRARY_TIME);
-
- ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_CAPSL));
- ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_NUML));
- ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_SCROLLL));
- ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON,
- mapper2.getMetaState());
-}
-
-TEST_F(KeyboardInputMapperTest, Process_toggleCapsLockState) {
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
-
- // Suppose we have two mappers. (DPAD + KEYBOARD)
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_DPAD);
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
- // Initial metastate is AMETA_NONE.
- ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
-
- mReader->toggleCapsLockState(DEVICE_ID);
- ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState());
-}
-
-TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleInMultiDevices) {
- // keyboard 1.
- mFakeEventHub->addLed(EVENTHUB_ID, LED_CAPSL, true /*initially on*/);
- mFakeEventHub->addLed(EVENTHUB_ID, LED_NUML, false /*initially off*/);
- mFakeEventHub->addLed(EVENTHUB_ID, LED_SCROLLL, false /*initially off*/);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
-
- KeyboardInputMapper& mapper1 =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
-
- // keyboard 2.
- const std::string USB2 = "USB2";
- const std::string DEVICE_NAME2 = "KEYBOARD2";
- constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
- constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1;
- std::shared_ptr<InputDevice> device2 =
- newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID,
- ftl::Flags<InputDeviceClass>(0));
- mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_CAPSL, true /*initially on*/);
- mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_NUML, false /*initially off*/);
- mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_SCROLLL, false /*initially off*/);
- mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
- mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
- mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
-
- device2->addEmptyEventHubDevice(SECOND_EVENTHUB_ID);
- KeyboardInputMapper& mapper2 =
- device2->constructAndAddMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID,
- mFakePolicy
- ->getReaderConfiguration(),
- AINPUT_SOURCE_KEYBOARD);
- std::list<NotifyArgs> unused =
- device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- /*changes=*/{});
- unused += device2->reset(ARBITRARY_TIME);
-
- // Initial metastate is AMETA_NONE.
- ASSERT_EQ(AMETA_NONE, mapper1.getMetaState());
- ASSERT_EQ(AMETA_NONE, mapper2.getMetaState());
-
- // Toggle num lock on and off.
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
- ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
- ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper1.getMetaState());
- ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper2.getMetaState());
-
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
- ASSERT_EQ(AMETA_NONE, mapper1.getMetaState());
- ASSERT_EQ(AMETA_NONE, mapper2.getMetaState());
-
- // Toggle caps lock on and off.
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
- ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
- ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper1.getMetaState());
- ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper2.getMetaState());
-
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
- ASSERT_EQ(AMETA_NONE, mapper1.getMetaState());
- ASSERT_EQ(AMETA_NONE, mapper2.getMetaState());
-
- // Toggle scroll lock on and off.
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
- ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
- ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper1.getMetaState());
- ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper2.getMetaState());
-
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
- ASSERT_EQ(AMETA_NONE, mapper1.getMetaState());
- ASSERT_EQ(AMETA_NONE, mapper2.getMetaState());
-}
-
-TEST_F(KeyboardInputMapperTest, Process_DisabledDevice) {
- const int32_t USAGE_A = 0x070004;
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
- mFakeEventHub->addKey(EVENTHUB_ID, 0, USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE);
-
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
- // Key down by scan code.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_HOME, 1);
- NotifyKeyArgs args;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(DEVICE_ID, args.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
- ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
- ASSERT_EQ(AKEYCODE_HOME, args.keyCode);
- ASSERT_EQ(KEY_HOME, args.scanCode);
- ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
-
- // Disable device, it should synthesize cancellation events for down events.
- mFakePolicy->addDisabledDevice(DEVICE_ID);
- configureDevice(InputReaderConfiguration::Change::ENABLED_STATE);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
- ASSERT_EQ(AKEYCODE_HOME, args.keyCode);
- ASSERT_EQ(KEY_HOME, args.scanCode);
- ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_CANCELED, args.flags);
-}
-
-TEST_F(KeyboardInputMapperTest, Configure_AssignKeyboardLayoutInfo) {
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
- std::list<NotifyArgs> unused =
- mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- /*changes=*/{});
-
- uint32_t generation = mReader->getContext()->getGeneration();
- mFakePolicy->addKeyboardLayoutAssociation(DEVICE_LOCATION, DEVICE_KEYBOARD_LAYOUT_INFO);
-
- unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::Change::KEYBOARD_LAYOUT_ASSOCIATION);
-
- InputDeviceInfo deviceInfo = mDevice->getDeviceInfo();
- ASSERT_EQ(DEVICE_KEYBOARD_LAYOUT_INFO.languageTag,
- deviceInfo.getKeyboardLayoutInfo()->languageTag);
- ASSERT_EQ(DEVICE_KEYBOARD_LAYOUT_INFO.layoutType,
- deviceInfo.getKeyboardLayoutInfo()->layoutType);
- ASSERT_TRUE(mReader->getContext()->getGeneration() != generation);
-
- // Call change layout association with the same values: Generation shouldn't change
- generation = mReader->getContext()->getGeneration();
- mFakePolicy->addKeyboardLayoutAssociation(DEVICE_LOCATION, DEVICE_KEYBOARD_LAYOUT_INFO);
- unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::Change::KEYBOARD_LAYOUT_ASSOCIATION);
- ASSERT_TRUE(mReader->getContext()->getGeneration() == generation);
-}
-
-TEST_F(KeyboardInputMapperTest, LayoutInfoCorrectlyMapped) {
- mFakeEventHub->setRawLayoutInfo(EVENTHUB_ID,
- RawLayoutInfo{.languageTag = "en", .layoutType = "extended"});
-
- // Configuration
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
- InputReaderConfiguration config;
- std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, config, /*changes=*/{});
-
- ASSERT_EQ("en", mDevice->getDeviceInfo().getKeyboardLayoutInfo()->languageTag);
- ASSERT_EQ("extended", mDevice->getDeviceInfo().getKeyboardLayoutInfo()->layoutType);
-}
-
-TEST_F(KeyboardInputMapperTest, Process_GesureEventToSetFlagKeepTouchMode) {
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, POLICY_FLAG_GESTURE);
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
- NotifyKeyArgs args;
-
- // Key down
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_LEFT, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_KEEP_TOUCH_MODE, args.flags);
-}
-
-TEST_F_WITH_FLAGS(KeyboardInputMapperTest, WakeBehavior_AlphabeticKeyboard,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
- enable_alphabetic_keyboard_wake))) {
- // For internal alphabetic devices, keys will trigger wake on key down.
-
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAYPAUSE, 0, AKEYCODE_MEDIA_PLAY_PAUSE, 0);
-
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_A, 1);
- NotifyKeyArgs args;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
-
- process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_A, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(uint32_t(0), args.policyFlags);
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_HOME, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
-
- process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_HOME, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(uint32_t(0), args.policyFlags);
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAYPAUSE, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
-
- process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_PLAYPAUSE, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(uint32_t(0), args.policyFlags);
-}
-
-/**
- * When there is more than one KeyboardInputMapper for an InputDevice, each mapper should produce
- * events that use the shared keyboard source across all mappers. This is to ensure that each
- * input device generates key events in a consistent manner, regardless of which mapper produces
- * the event.
- */
-TEST_F(KeyboardInputMapperTest, UsesSharedKeyboardSource) {
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
-
- // Add a mapper with SOURCE_KEYBOARD
- KeyboardInputMapper& keyboardMapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
-
- process(keyboardMapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1);
- ASSERT_NO_FATAL_FAILURE(
- mFakeListener->assertNotifyKeyWasCalled(WithSource(AINPUT_SOURCE_KEYBOARD)));
- process(keyboardMapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0);
- ASSERT_NO_FATAL_FAILURE(
- mFakeListener->assertNotifyKeyWasCalled(WithSource(AINPUT_SOURCE_KEYBOARD)));
-
- // Add a mapper with SOURCE_DPAD
- KeyboardInputMapper& dpadMapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_DPAD);
- for (auto* mapper : {&keyboardMapper, &dpadMapper}) {
- process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(
- WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD)));
- process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(
- WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD)));
- }
-
- // Add a mapper with SOURCE_GAMEPAD
- KeyboardInputMapper& gamepadMapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_GAMEPAD);
- for (auto* mapper : {&keyboardMapper, &dpadMapper, &gamepadMapper}) {
- process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(
- WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD)));
- process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(
- WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD)));
- }
-}
-
-// --- KeyboardInputMapperTest_ExternalAlphabeticDevice ---
-
-class KeyboardInputMapperTest_ExternalAlphabeticDevice : public InputMapperTest {
-protected:
- void SetUp() override {
- InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::KEYBOARD |
- InputDeviceClass::ALPHAKEY | InputDeviceClass::EXTERNAL);
- }
-};
-
-// --- KeyboardInputMapperTest_ExternalNonAlphabeticDevice ---
-
-class KeyboardInputMapperTest_ExternalNonAlphabeticDevice : public InputMapperTest {
-protected:
- void SetUp() override {
- InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::KEYBOARD |
- InputDeviceClass::EXTERNAL);
- }
-};
-
-TEST_F(KeyboardInputMapperTest_ExternalAlphabeticDevice, WakeBehavior_AlphabeticKeyboard) {
- // For external devices, keys will trigger wake on key down. Media keys should also trigger
- // wake if triggered from external devices.
-
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAYPAUSE, 0, AKEYCODE_MEDIA_PLAY_PAUSE,
- POLICY_FLAG_WAKE);
-
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_HOME, 1);
- NotifyKeyArgs args;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
-
- process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_HOME, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(uint32_t(0), args.policyFlags);
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAY, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
-
- process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_PLAY, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(uint32_t(0), args.policyFlags);
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAYPAUSE, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
-
- process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_PLAYPAUSE, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
-}
-
-TEST_F(KeyboardInputMapperTest_ExternalNonAlphabeticDevice, WakeBehavior_NonAlphabeticKeyboard) {
- // For external devices, keys will trigger wake on key down. Media keys should not trigger
- // wake if triggered from external non-alphaebtic keyboard (e.g. headsets).
-
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAYPAUSE, 0, AKEYCODE_MEDIA_PLAY_PAUSE,
- POLICY_FLAG_WAKE);
-
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAY, 1);
- NotifyKeyArgs args;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(uint32_t(0), args.policyFlags);
-
- process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_PLAY, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(uint32_t(0), args.policyFlags);
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAYPAUSE, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
-
- process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_PLAYPAUSE, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
-}
-
-TEST_F(KeyboardInputMapperTest_ExternalAlphabeticDevice, DoNotWakeByDefaultBehavior) {
- // Tv Remote key's wake behavior is prescribed by the keylayout file.
-
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, POLICY_FLAG_WAKE);
-
- addConfigurationProperty("keyboard.doNotWakeByDefault", "1");
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_HOME, 1);
- NotifyKeyArgs args;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
-
- process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_HOME, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_DOWN, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(uint32_t(0), args.policyFlags);
-
- process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_DOWN, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(uint32_t(0), args.policyFlags);
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAY, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
-
- process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_PLAY, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
-}
-
// --- TouchInputMapperTest ---
class TouchInputMapperTest : public InputMapperTest {
diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h
index 25e2b45..6f7c2e5 100644
--- a/services/inputflinger/tests/InterfaceMocks.h
+++ b/services/inputflinger/tests/InterfaceMocks.h
@@ -201,7 +201,9 @@
MOCK_METHOD(uint32_t, getSources, (), (const, override));
MOCK_METHOD(std::optional<DisplayViewport>, getAssociatedViewport, (), (const));
+ MOCK_METHOD(KeyboardType, getKeyboardType, (), (const, override));
MOCK_METHOD(bool, isEnabled, (), ());
+ MOCK_METHOD(bool, isExternal, (), (override));
MOCK_METHOD(void, dump, (std::string& dump, const std::string& eventHubDevStr), ());
MOCK_METHOD(void, addEmptyEventHubDevice, (int32_t eventHubId), ());
@@ -249,8 +251,6 @@
MOCK_METHOD(int32_t, getMetaState, (), ());
MOCK_METHOD(void, setKeyboardType, (KeyboardType keyboardType), ());
- MOCK_METHOD(void, bumpGeneration, (), ());
-
MOCK_METHOD(const PropertyMap&, getConfiguration, (), (const, override));
MOCK_METHOD(NotifyDeviceResetArgs, notifyReset, (nsecs_t when), ());
@@ -260,5 +260,11 @@
MOCK_METHOD(void, updateLedState, (bool reset), ());
MOCK_METHOD(size_t, getMapperCount, (), ());
+
+ virtual int32_t getGeneration() const override { return mGeneration; }
+ virtual void bumpGeneration() override { mGeneration++; }
+
+private:
+ int32_t mGeneration = 0;
};
} // namespace android
diff --git a/services/inputflinger/tests/KeyboardInputMapper_test.cpp b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
index bcc6062..39b583c 100644
--- a/services/inputflinger/tests/KeyboardInputMapper_test.cpp
+++ b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
@@ -16,29 +16,82 @@
#include "KeyboardInputMapper.h"
-#include <gtest/gtest.h>
+#include <cstdint>
+#include <list>
+#include <memory>
+#include <optional>
+#include <string>
+#include <android/input.h>
+#include <android/keycodes.h>
+#include <com_android_input_flags.h>
+#include <flag_macros.h>
+#include <ftl/flags.h>
+#include <gtest/gtest.h>
+#include <input/DisplayViewport.h>
+#include <input/Input.h>
+#include <input/InputDevice.h>
+#include <ui/LogicalDisplayId.h>
+#include <ui/Rotation.h>
+#include <utils/Errors.h>
+
+#include "EventHub.h"
#include "InputMapperTest.h"
#include "InterfaceMocks.h"
+#include "NotifyArgs.h"
+#include "TestConstants.h"
#include "TestEventMatchers.h"
#define TAG "KeyboardInputMapper_test"
namespace android {
+using namespace ftl::flag_operators;
using testing::_;
using testing::AllOf;
+using testing::AnyOf;
using testing::Args;
using testing::DoAll;
+using testing::IsEmpty;
using testing::Return;
+using testing::ReturnArg;
+using testing::SaveArg;
using testing::SetArgPointee;
using testing::VariantWith;
+namespace {
+
+// Arbitrary display properties.
+constexpr ui::LogicalDisplayId DISPLAY_ID = ui::LogicalDisplayId::DEFAULT;
+constexpr int32_t DISPLAY_WIDTH = 480;
+constexpr int32_t DISPLAY_HEIGHT = 800;
+
+DisplayViewport createPrimaryViewport(ui::Rotation orientation) {
+ const bool isRotated =
+ orientation == ui::Rotation::Rotation90 || orientation == ui::Rotation::Rotation270;
+ DisplayViewport v;
+ v.displayId = DISPLAY_ID;
+ v.orientation = orientation;
+ v.logicalRight = isRotated ? DISPLAY_HEIGHT : DISPLAY_WIDTH;
+ v.logicalBottom = isRotated ? DISPLAY_WIDTH : DISPLAY_HEIGHT;
+ v.physicalRight = isRotated ? DISPLAY_HEIGHT : DISPLAY_WIDTH;
+ v.physicalBottom = isRotated ? DISPLAY_WIDTH : DISPLAY_HEIGHT;
+ v.deviceWidth = isRotated ? DISPLAY_HEIGHT : DISPLAY_WIDTH;
+ v.deviceHeight = isRotated ? DISPLAY_WIDTH : DISPLAY_HEIGHT;
+ v.isActive = true;
+ v.uniqueId = "local:1";
+ return v;
+}
+
+} // namespace
+
/**
* Unit tests for KeyboardInputMapper.
*/
class KeyboardInputMapperUnitTest : public InputMapperUnitTest {
protected:
+ const KeyboardLayoutInfo DEVICE_KEYBOARD_LAYOUT_INFO = KeyboardLayoutInfo("en-US", "qwerty");
+
sp<FakeInputReaderPolicy> mFakePolicy;
const std::unordered_map<int32_t, int32_t> mKeyCodeMap{{KEY_0, AKEYCODE_0},
{KEY_A, AKEYCODE_A},
@@ -60,9 +113,8 @@
InputMapperUnitTest::SetUp();
// set key-codes expected in tests
- for (const auto& [scanCode, outKeycode] : mKeyCodeMap) {
- EXPECT_CALL(mMockEventHub, mapKey(EVENTHUB_ID, scanCode, _, _, _, _, _))
- .WillRepeatedly(DoAll(SetArgPointee<4>(outKeycode), Return(NO_ERROR)));
+ for (const auto& [evdevCode, outKeycode] : mKeyCodeMap) {
+ addKeyByEvdevCode(evdevCode, outKeycode);
}
mFakePolicy = sp<FakeInputReaderPolicy>::make();
@@ -73,8 +125,79 @@
mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration,
AINPUT_SOURCE_KEYBOARD);
}
+
+ void addKeyByEvdevCode(int32_t evdevCode, int32_t keyCode, int32_t flags = 0) {
+ EXPECT_CALL(mMockEventHub, mapKey(EVENTHUB_ID, evdevCode, _, _, _, _, _))
+ .WillRepeatedly([=](int32_t, int32_t, int32_t, int32_t metaState,
+ int32_t* outKeycode, int32_t* outMetaState,
+ uint32_t* outFlags) {
+ if (outKeycode != nullptr) {
+ *outKeycode = keyCode;
+ }
+ if (outMetaState != nullptr) {
+ *outMetaState = metaState;
+ }
+ if (outFlags != nullptr) {
+ *outFlags = flags;
+ }
+ return NO_ERROR;
+ });
+ }
+
+ void addKeyByUsageCode(int32_t usageCode, int32_t keyCode, int32_t flags = 0) {
+ EXPECT_CALL(mMockEventHub, mapKey(EVENTHUB_ID, _, usageCode, _, _, _, _))
+ .WillRepeatedly([=](int32_t, int32_t, int32_t, int32_t metaState,
+ int32_t* outKeycode, int32_t* outMetaState,
+ uint32_t* outFlags) {
+ if (outKeycode != nullptr) {
+ *outKeycode = keyCode;
+ }
+ if (outMetaState != nullptr) {
+ *outMetaState = metaState;
+ }
+ if (outFlags != nullptr) {
+ *outFlags = flags;
+ }
+ return NO_ERROR;
+ });
+ }
+
+ void setDisplayOrientation(ui::Rotation orientation) {
+ EXPECT_CALL((*mDevice), getAssociatedViewport)
+ .WillRepeatedly(Return(createPrimaryViewport(orientation)));
+ std::list<NotifyArgs> args =
+ mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
+ InputReaderConfiguration::Change::DISPLAY_INFO);
+ ASSERT_EQ(0u, args.size());
+ }
+
+ NotifyKeyArgs expectSingleKeyArg(const std::list<NotifyArgs>& args) {
+ EXPECT_EQ(1u, args.size());
+ return std::get<NotifyKeyArgs>(args.front());
+ }
+
+ void testDPadKeyRotation(int32_t originalEvdevCode, int32_t originalKeyCode,
+ int32_t rotatedKeyCode, ui::LogicalDisplayId displayId = DISPLAY_ID) {
+ std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, originalEvdevCode, 1);
+ NotifyKeyArgs args = expectSingleKeyArg(argsList);
+ ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
+ ASSERT_EQ(originalEvdevCode, args.scanCode);
+ ASSERT_EQ(rotatedKeyCode, args.keyCode);
+ ASSERT_EQ(displayId, args.displayId);
+
+ argsList = process(ARBITRARY_TIME, EV_KEY, originalEvdevCode, 0);
+ args = expectSingleKeyArg(argsList);
+ ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
+ ASSERT_EQ(originalEvdevCode, args.scanCode);
+ ASSERT_EQ(rotatedKeyCode, args.keyCode);
+ ASSERT_EQ(displayId, args.displayId);
+ }
};
+TEST_F(KeyboardInputMapperUnitTest, GetSources) {
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, mMapper->getSources());
+}
+
TEST_F(KeyboardInputMapperUnitTest, KeyPressTimestampRecorded) {
nsecs_t when = ARBITRARY_TIME;
std::vector<int32_t> keyCodes{KEY_0, KEY_A, KEY_LEFTCTRL, KEY_RIGHTALT, KEY_LEFTSHIFT};
@@ -109,4 +232,935 @@
WithScanCode(KEY_0)))));
}
+TEST_F(KeyboardInputMapperUnitTest, Process_SimpleKeyPress) {
+ const int32_t USAGE_A = 0x070004;
+ addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME, POLICY_FLAG_WAKE);
+ addKeyByUsageCode(USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE);
+
+ // Initial metastate is AMETA_NONE.
+ ASSERT_EQ(AMETA_NONE, mMapper->getMetaState());
+
+ // Key down by evdev code.
+ std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+ NotifyKeyArgs args = expectSingleKeyArg(argsList);
+ ASSERT_EQ(DEVICE_ID, args.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
+ ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
+ ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
+ ASSERT_EQ(AKEYCODE_HOME, args.keyCode);
+ ASSERT_EQ(KEY_HOME, args.scanCode);
+ ASSERT_EQ(AMETA_NONE, args.metaState);
+ ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
+ ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+ ASSERT_EQ(ARBITRARY_TIME, args.downTime);
+
+ // Key up by evdev code.
+ argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
+ args = expectSingleKeyArg(argsList);
+ ASSERT_EQ(DEVICE_ID, args.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
+ ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime);
+ ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
+ ASSERT_EQ(AKEYCODE_HOME, args.keyCode);
+ ASSERT_EQ(KEY_HOME, args.scanCode);
+ ASSERT_EQ(AMETA_NONE, args.metaState);
+ ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
+ ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+ ASSERT_EQ(ARBITRARY_TIME, args.downTime);
+
+ // Key down by usage code.
+ argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_A);
+ argsList += process(ARBITRARY_TIME, EV_KEY, 0, 1);
+ args = expectSingleKeyArg(argsList);
+ ASSERT_EQ(DEVICE_ID, args.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
+ ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
+ ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
+ ASSERT_EQ(AKEYCODE_A, args.keyCode);
+ ASSERT_EQ(0, args.scanCode);
+ ASSERT_EQ(AMETA_NONE, args.metaState);
+ ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
+ ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+ ASSERT_EQ(ARBITRARY_TIME, args.downTime);
+
+ // Key up by usage code.
+ argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_A);
+ argsList += process(ARBITRARY_TIME + 1, EV_KEY, 0, 0);
+ args = expectSingleKeyArg(argsList);
+ ASSERT_EQ(DEVICE_ID, args.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
+ ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime);
+ ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
+ ASSERT_EQ(AKEYCODE_A, args.keyCode);
+ ASSERT_EQ(0, args.scanCode);
+ ASSERT_EQ(AMETA_NONE, args.metaState);
+ ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
+ ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+ ASSERT_EQ(ARBITRARY_TIME, args.downTime);
+}
+
+TEST_F(KeyboardInputMapperUnitTest, Process_UnknownKey) {
+ const int32_t USAGE_UNKNOWN = 0x07ffff;
+ EXPECT_CALL(mMockEventHub, mapKey(EVENTHUB_ID, KEY_UNKNOWN, USAGE_UNKNOWN, _, _, _, _))
+ .WillRepeatedly(Return(NAME_NOT_FOUND));
+
+ // Key down with unknown scan code or usage code.
+ std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
+ argsList += process(ARBITRARY_TIME, EV_KEY, KEY_UNKNOWN, 1);
+ NotifyKeyArgs args = expectSingleKeyArg(argsList);
+ ASSERT_EQ(DEVICE_ID, args.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
+ ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
+ ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
+ ASSERT_EQ(0, args.keyCode);
+ ASSERT_EQ(KEY_UNKNOWN, args.scanCode);
+ ASSERT_EQ(AMETA_NONE, args.metaState);
+ ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
+ ASSERT_EQ(0U, args.policyFlags);
+ ASSERT_EQ(ARBITRARY_TIME, args.downTime);
+
+ // Key up with unknown scan code or usage code.
+ argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
+ argsList += process(ARBITRARY_TIME + 1, EV_KEY, KEY_UNKNOWN, 0);
+ args = expectSingleKeyArg(argsList);
+ ASSERT_EQ(DEVICE_ID, args.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
+ ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime);
+ ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
+ ASSERT_EQ(0, args.keyCode);
+ ASSERT_EQ(KEY_UNKNOWN, args.scanCode);
+ ASSERT_EQ(AMETA_NONE, args.metaState);
+ ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
+ ASSERT_EQ(0U, args.policyFlags);
+ ASSERT_EQ(ARBITRARY_TIME, args.downTime);
+}
+
+/**
+ * Ensure that the readTime is set to the time when the EV_KEY is received.
+ */
+TEST_F(KeyboardInputMapperUnitTest, Process_SendsReadTime) {
+ addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME);
+
+ // Key down
+ std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, /*readTime=*/12, EV_KEY, KEY_HOME, 1);
+ ASSERT_EQ(12, expectSingleKeyArg(argsList).readTime);
+
+ // Key up
+ argsList = process(ARBITRARY_TIME, /*readTime=*/15, EV_KEY, KEY_HOME, 1);
+ ASSERT_EQ(15, expectSingleKeyArg(argsList).readTime);
+}
+
+TEST_F(KeyboardInputMapperUnitTest, Process_ShouldUpdateMetaState) {
+ addKeyByEvdevCode(KEY_LEFTSHIFT, AKEYCODE_SHIFT_LEFT);
+ addKeyByEvdevCode(KEY_A, AKEYCODE_A);
+
+ EXPECT_CALL(mMockInputReaderContext, updateGlobalMetaState()).Times(2);
+
+ // Initial metastate is AMETA_NONE.
+ ASSERT_EQ(AMETA_NONE, mMapper->getMetaState());
+
+ // Metakey down.
+ std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_LEFTSHIFT, 1);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, expectSingleKeyArg(argsList).metaState);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mMapper->getMetaState());
+
+ // Key down.
+ argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_A, 1);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, expectSingleKeyArg(argsList).metaState);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mMapper->getMetaState());
+
+ // Key up.
+ argsList = process(ARBITRARY_TIME + 2, EV_KEY, KEY_A, 0);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, expectSingleKeyArg(argsList).metaState);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mMapper->getMetaState());
+
+ // Metakey up.
+ argsList = process(ARBITRARY_TIME + 3, EV_KEY, KEY_LEFTSHIFT, 0);
+ ASSERT_EQ(AMETA_NONE, expectSingleKeyArg(argsList).metaState);
+ ASSERT_EQ(AMETA_NONE, mMapper->getMetaState());
+}
+
+TEST_F(KeyboardInputMapperUnitTest, Process_WhenNotOrientationAware_ShouldNotRotateDPad) {
+ addKeyByEvdevCode(KEY_UP, AKEYCODE_DPAD_UP);
+ addKeyByEvdevCode(KEY_RIGHT, AKEYCODE_DPAD_RIGHT);
+ addKeyByEvdevCode(KEY_DOWN, AKEYCODE_DPAD_DOWN);
+ addKeyByEvdevCode(KEY_LEFT, AKEYCODE_DPAD_LEFT);
+
+ setDisplayOrientation(ui::Rotation::Rotation90);
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP));
+ ASSERT_NO_FATAL_FAILURE(
+ testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_RIGHT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_DOWN));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT));
+}
+
+TEST_F(KeyboardInputMapperUnitTest, Process_WhenOrientationAware_ShouldRotateDPad) {
+ addKeyByEvdevCode(KEY_UP, AKEYCODE_DPAD_UP);
+ addKeyByEvdevCode(KEY_RIGHT, AKEYCODE_DPAD_RIGHT);
+ addKeyByEvdevCode(KEY_DOWN, AKEYCODE_DPAD_DOWN);
+ addKeyByEvdevCode(KEY_LEFT, AKEYCODE_DPAD_LEFT);
+
+ mPropertyMap.addProperty("keyboard.orientationAware", "1");
+ mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration,
+ AINPUT_SOURCE_KEYBOARD);
+ setDisplayOrientation(ui::ROTATION_0);
+
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP));
+ ASSERT_NO_FATAL_FAILURE(
+ testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_RIGHT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_DOWN));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT));
+
+ setDisplayOrientation(ui::ROTATION_90);
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN));
+
+ setDisplayOrientation(ui::ROTATION_180);
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN));
+ ASSERT_NO_FATAL_FAILURE(
+ testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_LEFT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_UP));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_RIGHT));
+
+ setDisplayOrientation(ui::ROTATION_270);
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_RIGHT));
+ ASSERT_NO_FATAL_FAILURE(
+ testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_DOWN));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_LEFT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_UP));
+
+ // Special case: if orientation changes while key is down, we still emit the same keycode
+ // in the key up as we did in the key down.
+ setDisplayOrientation(ui::ROTATION_270);
+ std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+ NotifyKeyArgs args = expectSingleKeyArg(argsList);
+ ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
+ ASSERT_EQ(KEY_UP, args.scanCode);
+ ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode);
+
+ setDisplayOrientation(ui::ROTATION_180);
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+ args = expectSingleKeyArg(argsList);
+ ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
+ ASSERT_EQ(KEY_UP, args.scanCode);
+ ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode);
+}
+
+TEST_F(KeyboardInputMapperUnitTest, DisplayIdConfigurationChange_NotOrientationAware) {
+ // If the keyboard is not orientation aware,
+ // key events should not be associated with a specific display id
+ addKeyByEvdevCode(KEY_UP, AKEYCODE_DPAD_UP);
+
+ // Display id should be LogicalDisplayId::INVALID without any display configuration.
+ std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+ ASSERT_GT(argsList.size(), 0u);
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+ ASSERT_GT(argsList.size(), 0u);
+ ASSERT_EQ(ui::LogicalDisplayId::INVALID, std::get<NotifyKeyArgs>(argsList.front()).displayId);
+}
+
+TEST_F(KeyboardInputMapperUnitTest, DisplayIdConfigurationChange_OrientationAware) {
+ // If the keyboard is orientation aware,
+ // key events should be associated with the internal viewport
+ addKeyByEvdevCode(KEY_UP, AKEYCODE_DPAD_UP);
+
+ mPropertyMap.addProperty("keyboard.orientationAware", "1");
+ mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration,
+ AINPUT_SOURCE_KEYBOARD);
+
+ // Display id should be LogicalDisplayId::INVALID without any display configuration.
+ // ^--- already checked by the previous test
+
+ setDisplayOrientation(ui::ROTATION_0);
+ std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+ ASSERT_GT(argsList.size(), 0u);
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+ ASSERT_GT(argsList.size(), 0u);
+ ASSERT_EQ(DISPLAY_ID, std::get<NotifyKeyArgs>(argsList.front()).displayId);
+
+ constexpr ui::LogicalDisplayId newDisplayId = ui::LogicalDisplayId{2};
+ DisplayViewport newViewport = createPrimaryViewport(ui::ROTATION_0);
+ newViewport.displayId = newDisplayId;
+ EXPECT_CALL((*mDevice), getAssociatedViewport).WillRepeatedly(Return(newViewport));
+ argsList = mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
+ InputReaderConfiguration::Change::DISPLAY_INFO);
+ ASSERT_EQ(0u, argsList.size());
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+ ASSERT_GT(argsList.size(), 0u);
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+ ASSERT_GT(argsList.size(), 0u);
+ ASSERT_EQ(newDisplayId, std::get<NotifyKeyArgs>(argsList.front()).displayId);
+}
+
+TEST_F(KeyboardInputMapperUnitTest, GetKeyCodeState) {
+ EXPECT_CALL(mMockEventHub, getKeyCodeState(EVENTHUB_ID, AKEYCODE_A))
+ .WillRepeatedly(Return(AKEY_STATE_DOWN));
+ ASSERT_EQ(AKEY_STATE_DOWN, mMapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
+
+ EXPECT_CALL(mMockEventHub, getKeyCodeState(EVENTHUB_ID, AKEYCODE_A))
+ .WillRepeatedly(Return(AKEY_STATE_UP));
+ ASSERT_EQ(AKEY_STATE_UP, mMapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
+}
+
+TEST_F(KeyboardInputMapperUnitTest, GetKeyCodeForKeyLocation) {
+ EXPECT_CALL(mMockEventHub, getKeyCodeForKeyLocation(EVENTHUB_ID, _))
+ .WillRepeatedly(ReturnArg<1>());
+ EXPECT_CALL(mMockEventHub, getKeyCodeForKeyLocation(EVENTHUB_ID, AKEYCODE_Y))
+ .WillRepeatedly(Return(AKEYCODE_Z));
+ ASSERT_EQ(AKEYCODE_Z, mMapper->getKeyCodeForKeyLocation(AKEYCODE_Y))
+ << "If a mapping is available, the result is equal to the mapping";
+
+ ASSERT_EQ(AKEYCODE_A, mMapper->getKeyCodeForKeyLocation(AKEYCODE_A))
+ << "If no mapping is available, the result is the key location";
+}
+
+TEST_F(KeyboardInputMapperUnitTest, GetScanCodeState) {
+ EXPECT_CALL(mMockEventHub, getScanCodeState(EVENTHUB_ID, KEY_A))
+ .WillRepeatedly(Return(AKEY_STATE_DOWN));
+ ASSERT_EQ(AKEY_STATE_DOWN, mMapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
+
+ EXPECT_CALL(mMockEventHub, getScanCodeState(EVENTHUB_ID, KEY_A))
+ .WillRepeatedly(Return(AKEY_STATE_UP));
+ ASSERT_EQ(AKEY_STATE_UP, mMapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
+}
+
+TEST_F(KeyboardInputMapperUnitTest, Process_LockedKeysShouldToggleMetaStateAndLeds) {
+ EXPECT_CALL(mMockEventHub,
+ hasLed(EVENTHUB_ID, AnyOf(LED_CAPSL, LED_NUML, LED_SCROLLL /*NOTYPO*/)))
+ .WillRepeatedly(Return(true));
+ bool capsLockLed = true; // Initially on
+ bool numLockLed = false; // Initially off
+ bool scrollLockLed = false; // Initially off
+ EXPECT_CALL(mMockEventHub, setLedState(EVENTHUB_ID, LED_CAPSL, _))
+ .WillRepeatedly(SaveArg<2>(&capsLockLed));
+ EXPECT_CALL(mMockEventHub, setLedState(EVENTHUB_ID, LED_NUML, _))
+ .WillRepeatedly(SaveArg<2>(&numLockLed));
+ EXPECT_CALL(mMockEventHub, setLedState(EVENTHUB_ID, LED_SCROLLL /*NOTYPO*/, _))
+ .WillRepeatedly(SaveArg<2>(&scrollLockLed));
+ addKeyByEvdevCode(KEY_CAPSLOCK, AKEYCODE_CAPS_LOCK);
+ addKeyByEvdevCode(KEY_NUMLOCK, AKEYCODE_NUM_LOCK);
+ addKeyByEvdevCode(KEY_SCROLLLOCK, AKEYCODE_SCROLL_LOCK);
+
+ // In real operation, mappers pass new LED states to InputReader (via the context), which then
+ // calls back to the mappers to apply that state. Mimic the same thing here with mocks.
+ int32_t ledMetaState;
+ EXPECT_CALL(mMockInputReaderContext, updateLedMetaState(_))
+ .WillRepeatedly([&](int32_t newState) {
+ ledMetaState = newState;
+ mMapper->updateLedState(false);
+ });
+ EXPECT_CALL(mMockInputReaderContext, getLedMetaState())
+ .WillRepeatedly(testing::ReturnPointee(&ledMetaState));
+
+ ASSERT_THAT(mMapper->reset(ARBITRARY_TIME), IsEmpty());
+
+ // Initial metastate is AMETA_NONE.
+ ASSERT_EQ(AMETA_NONE, mMapper->getMetaState());
+
+ // Initialization should have turned all of the lights off.
+ ASSERT_FALSE(capsLockLed);
+ ASSERT_FALSE(numLockLed);
+ ASSERT_FALSE(scrollLockLed);
+
+ // Toggle caps lock on.
+ std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1);
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+ ASSERT_TRUE(capsLockLed);
+ ASSERT_FALSE(numLockLed);
+ ASSERT_FALSE(scrollLockLed);
+ ASSERT_EQ(AMETA_CAPS_LOCK_ON, mMapper->getMetaState());
+
+ // Toggle num lock on.
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1);
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0);
+ ASSERT_TRUE(capsLockLed);
+ ASSERT_TRUE(numLockLed);
+ ASSERT_FALSE(scrollLockLed);
+ ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mMapper->getMetaState());
+
+ // Toggle caps lock off.
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1);
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+ ASSERT_FALSE(capsLockLed);
+ ASSERT_TRUE(numLockLed);
+ ASSERT_FALSE(scrollLockLed);
+ ASSERT_EQ(AMETA_NUM_LOCK_ON, mMapper->getMetaState());
+
+ // Toggle scroll lock on.
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+ ASSERT_FALSE(capsLockLed);
+ ASSERT_TRUE(numLockLed);
+ ASSERT_TRUE(scrollLockLed);
+ ASSERT_EQ(AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mMapper->getMetaState());
+
+ // Toggle num lock off.
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1);
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0);
+ ASSERT_FALSE(capsLockLed);
+ ASSERT_FALSE(numLockLed);
+ ASSERT_TRUE(scrollLockLed);
+ ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mMapper->getMetaState());
+
+ // Toggle scroll lock off.
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+ ASSERT_FALSE(capsLockLed);
+ ASSERT_FALSE(numLockLed);
+ ASSERT_FALSE(scrollLockLed);
+ ASSERT_EQ(AMETA_NONE, mMapper->getMetaState());
+}
+
+TEST_F(KeyboardInputMapperUnitTest, DisablingDeviceResetsPressedKeys) {
+ const int32_t USAGE_A = 0x070004;
+ addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME, POLICY_FLAG_WAKE);
+ addKeyByUsageCode(USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE);
+
+ // Key down by scan code.
+ std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+ NotifyKeyArgs args = expectSingleKeyArg(argsList);
+ ASSERT_EQ(DEVICE_ID, args.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
+ ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
+ ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
+ ASSERT_EQ(AKEYCODE_HOME, args.keyCode);
+ ASSERT_EQ(KEY_HOME, args.scanCode);
+ ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
+
+ // Disable device, it should synthesize cancellation events for down events.
+ mReaderConfiguration.disabledDevices.insert(DEVICE_ID);
+ argsList = mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
+ InputReaderConfiguration::Change::ENABLED_STATE);
+ argsList += mMapper->reset(ARBITRARY_TIME);
+ args = expectSingleKeyArg(argsList);
+ ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
+ ASSERT_EQ(AKEYCODE_HOME, args.keyCode);
+ ASSERT_EQ(KEY_HOME, args.scanCode);
+ ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_CANCELED, args.flags);
+}
+
+TEST_F(KeyboardInputMapperUnitTest, Configure_AssignKeyboardLayoutInfo) {
+ std::list<NotifyArgs> unused =
+ mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, /*changes=*/{});
+
+ int32_t generation = mDevice->getGeneration();
+ mReaderConfiguration.keyboardLayoutAssociations.insert(
+ {mIdentifier.location, DEVICE_KEYBOARD_LAYOUT_INFO});
+
+ unused += mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
+ InputReaderConfiguration::Change::KEYBOARD_LAYOUT_ASSOCIATION);
+
+ InputDeviceInfo deviceInfo;
+ mMapper->populateDeviceInfo(deviceInfo);
+ ASSERT_EQ(DEVICE_KEYBOARD_LAYOUT_INFO.languageTag,
+ deviceInfo.getKeyboardLayoutInfo()->languageTag);
+ ASSERT_EQ(DEVICE_KEYBOARD_LAYOUT_INFO.layoutType,
+ deviceInfo.getKeyboardLayoutInfo()->layoutType);
+ ASSERT_GT(mDevice->getGeneration(), generation);
+
+ // Call change layout association with the same values: Generation shouldn't change
+ generation = mDevice->getGeneration();
+ unused += mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
+ InputReaderConfiguration::Change::KEYBOARD_LAYOUT_ASSOCIATION);
+ ASSERT_EQ(mDevice->getGeneration(), generation);
+}
+
+TEST_F(KeyboardInputMapperUnitTest, LayoutInfoCorrectlyMapped) {
+ EXPECT_CALL(mMockEventHub, getRawLayoutInfo(EVENTHUB_ID))
+ .WillRepeatedly(Return(RawLayoutInfo{.languageTag = "en", .layoutType = "extended"}));
+
+ // Configuration
+ std::list<NotifyArgs> unused =
+ mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, /*changes=*/{});
+
+ InputDeviceInfo deviceInfo;
+ mMapper->populateDeviceInfo(deviceInfo);
+ ASSERT_EQ("en", deviceInfo.getKeyboardLayoutInfo()->languageTag);
+ ASSERT_EQ("extended", deviceInfo.getKeyboardLayoutInfo()->layoutType);
+}
+
+TEST_F(KeyboardInputMapperUnitTest, Process_GestureEventToSetFlagKeepTouchMode) {
+ addKeyByEvdevCode(KEY_LEFT, AKEYCODE_DPAD_LEFT, POLICY_FLAG_GESTURE);
+
+ // Key down
+ std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_LEFT, 1);
+ ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_KEEP_TOUCH_MODE,
+ expectSingleKeyArg(argsList).flags);
+}
+
+TEST_F_WITH_FLAGS(KeyboardInputMapperUnitTest, WakeBehavior_AlphabeticKeyboard,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
+ enable_alphabetic_keyboard_wake))) {
+ // For internal alphabetic devices, keys will trigger wake on key down.
+
+ addKeyByEvdevCode(KEY_A, AKEYCODE_A);
+ addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME);
+ addKeyByEvdevCode(KEY_PLAYPAUSE, AKEYCODE_MEDIA_PLAY_PAUSE);
+
+ std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_A, 1);
+ ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
+
+ argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_A, 0);
+ ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
+
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+ ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
+
+ argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
+ ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
+
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAYPAUSE, 1);
+ ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
+
+ argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAYPAUSE, 0);
+ ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
+}
+
+// --- KeyboardInputMapperTest ---
+
+// TODO(b/283812079): convert the tests for this class, which use multiple mappers each, to use
+// InputMapperUnitTest.
+class KeyboardInputMapperTest : public InputMapperTest {
+protected:
+ void SetUp() override {
+ InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::KEYBOARD |
+ InputDeviceClass::ALPHAKEY);
+ }
+ const std::string UNIQUE_ID = "local:0";
+
+ void testDPadKeyRotation(KeyboardInputMapper& mapper, int32_t originalScanCode,
+ int32_t originalKeyCode, int32_t rotatedKeyCode,
+ ui::LogicalDisplayId displayId = ui::LogicalDisplayId::INVALID);
+};
+
+void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper& mapper,
+ int32_t originalScanCode, int32_t originalKeyCode,
+ int32_t rotatedKeyCode,
+ ui::LogicalDisplayId displayId) {
+ NotifyKeyArgs args;
+
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, originalScanCode, 1);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
+ ASSERT_EQ(originalScanCode, args.scanCode);
+ ASSERT_EQ(rotatedKeyCode, args.keyCode);
+ ASSERT_EQ(displayId, args.displayId);
+
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, originalScanCode, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
+ ASSERT_EQ(originalScanCode, args.scanCode);
+ ASSERT_EQ(rotatedKeyCode, args.keyCode);
+ ASSERT_EQ(displayId, args.displayId);
+}
+
+TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) {
+ // keyboard 1.
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
+
+ // keyboard 2.
+ const std::string USB2 = "USB2";
+ const std::string DEVICE_NAME2 = "KEYBOARD2";
+ constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
+ constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1;
+ std::shared_ptr<InputDevice> device2 =
+ newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID,
+ ftl::Flags<InputDeviceClass>(0));
+
+ mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
+ mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0);
+ mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
+ mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
+
+ KeyboardInputMapper& mapper =
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
+
+ device2->addEmptyEventHubDevice(SECOND_EVENTHUB_ID);
+ KeyboardInputMapper& mapper2 =
+ device2->constructAndAddMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID,
+ mFakePolicy
+ ->getReaderConfiguration(),
+ AINPUT_SOURCE_KEYBOARD);
+ std::list<NotifyArgs> unused =
+ device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ /*changes=*/{});
+ unused += device2->reset(ARBITRARY_TIME);
+
+ // Prepared displays and associated info.
+ constexpr uint8_t hdmi1 = 0;
+ constexpr uint8_t hdmi2 = 1;
+ const std::string SECONDARY_UNIQUE_ID = "local:1";
+
+ mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1);
+ mFakePolicy->addInputPortAssociation(USB2, hdmi2);
+
+ // No associated display viewport found, should disable the device.
+ unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ InputReaderConfiguration::Change::DISPLAY_INFO);
+ ASSERT_FALSE(device2->isEnabled());
+
+ // Prepare second display.
+ constexpr ui::LogicalDisplayId newDisplayId = ui::LogicalDisplayId{2};
+ setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ UNIQUE_ID, hdmi1, ViewportType::INTERNAL);
+ setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ SECONDARY_UNIQUE_ID, hdmi2, ViewportType::EXTERNAL);
+ // Default device will reconfigure above, need additional reconfiguration for another device.
+ unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ InputReaderConfiguration::Change::DISPLAY_INFO);
+
+ // Device should be enabled after the associated display is found.
+ ASSERT_TRUE(mDevice->isEnabled());
+ ASSERT_TRUE(device2->isEnabled());
+
+ // Test pad key events
+ ASSERT_NO_FATAL_FAILURE(
+ testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, DISPLAY_ID));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
+ AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN,
+ AKEYCODE_DPAD_DOWN, DISPLAY_ID));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT,
+ AKEYCODE_DPAD_LEFT, DISPLAY_ID));
+
+ ASSERT_NO_FATAL_FAILURE(
+ testDPadKeyRotation(mapper2, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, newDisplayId));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
+ AKEYCODE_DPAD_RIGHT, newDisplayId));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_DOWN, AKEYCODE_DPAD_DOWN,
+ AKEYCODE_DPAD_DOWN, newDisplayId));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_LEFT, AKEYCODE_DPAD_LEFT,
+ AKEYCODE_DPAD_LEFT, newDisplayId));
+}
+
+TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleAfterReattach) {
+ mFakeEventHub->addLed(EVENTHUB_ID, LED_CAPSL, true /*initially on*/);
+ mFakeEventHub->addLed(EVENTHUB_ID, LED_NUML, false /*initially off*/);
+ mFakeEventHub->addLed(EVENTHUB_ID, LED_SCROLLL, false /*initially off*/);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
+
+ KeyboardInputMapper& mapper =
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
+ // Initial metastate is AMETA_NONE.
+ ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
+
+ // Initialization should have turned all of the lights off.
+ ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
+ ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
+ ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
+
+ // Toggle caps lock on.
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+ ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
+ ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState());
+
+ // Toggle num lock on.
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
+ ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
+ ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper.getMetaState());
+
+ // Toggle scroll lock on.
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+ ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
+ ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState());
+
+ mFakeEventHub->removeDevice(EVENTHUB_ID);
+ mReader->loopOnce();
+
+ // keyboard 2 should default toggle keys.
+ const std::string USB2 = "USB2";
+ const std::string DEVICE_NAME2 = "KEYBOARD2";
+ constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
+ constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1;
+ std::shared_ptr<InputDevice> device2 =
+ newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID,
+ ftl::Flags<InputDeviceClass>(0));
+ mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_CAPSL, true /*initially on*/);
+ mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_NUML, false /*initially off*/);
+ mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_SCROLLL, false /*initially off*/);
+ mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
+ mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
+ mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
+
+ device2->addEmptyEventHubDevice(SECOND_EVENTHUB_ID);
+ KeyboardInputMapper& mapper2 =
+ device2->constructAndAddMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID,
+ mFakePolicy
+ ->getReaderConfiguration(),
+ AINPUT_SOURCE_KEYBOARD);
+ std::list<NotifyArgs> unused =
+ device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ /*changes=*/{});
+ unused += device2->reset(ARBITRARY_TIME);
+
+ ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_CAPSL));
+ ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_NUML));
+ ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_SCROLLL));
+ ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON,
+ mapper2.getMetaState());
+}
+
+TEST_F(KeyboardInputMapperTest, Process_toggleCapsLockState) {
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
+
+ // Suppose we have two mappers. (DPAD + KEYBOARD)
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_DPAD);
+ KeyboardInputMapper& mapper =
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
+ // Initial metastate is AMETA_NONE.
+ ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
+
+ mReader->toggleCapsLockState(DEVICE_ID);
+ ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState());
+}
+
+TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleInMultiDevices) {
+ // keyboard 1.
+ mFakeEventHub->addLed(EVENTHUB_ID, LED_CAPSL, true /*initially on*/);
+ mFakeEventHub->addLed(EVENTHUB_ID, LED_NUML, false /*initially off*/);
+ mFakeEventHub->addLed(EVENTHUB_ID, LED_SCROLLL, false /*initially off*/);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
+
+ KeyboardInputMapper& mapper1 =
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
+
+ // keyboard 2.
+ const std::string USB2 = "USB2";
+ const std::string DEVICE_NAME2 = "KEYBOARD2";
+ constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
+ constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1;
+ std::shared_ptr<InputDevice> device2 =
+ newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID,
+ ftl::Flags<InputDeviceClass>(0));
+ mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_CAPSL, true /*initially on*/);
+ mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_NUML, false /*initially off*/);
+ mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_SCROLLL, false /*initially off*/);
+ mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
+ mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
+ mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
+
+ device2->addEmptyEventHubDevice(SECOND_EVENTHUB_ID);
+ KeyboardInputMapper& mapper2 =
+ device2->constructAndAddMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID,
+ mFakePolicy
+ ->getReaderConfiguration(),
+ AINPUT_SOURCE_KEYBOARD);
+ std::list<NotifyArgs> unused =
+ device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ /*changes=*/{});
+ unused += device2->reset(ARBITRARY_TIME);
+
+ // Initial metastate is AMETA_NONE.
+ ASSERT_EQ(AMETA_NONE, mapper1.getMetaState());
+ ASSERT_EQ(AMETA_NONE, mapper2.getMetaState());
+
+ // Toggle num lock on and off.
+ process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
+ process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
+ ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
+ ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper1.getMetaState());
+ ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper2.getMetaState());
+
+ process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
+ process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
+ ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
+ ASSERT_EQ(AMETA_NONE, mapper1.getMetaState());
+ ASSERT_EQ(AMETA_NONE, mapper2.getMetaState());
+
+ // Toggle caps lock on and off.
+ process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
+ process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+ ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
+ ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper1.getMetaState());
+ ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper2.getMetaState());
+
+ process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
+ process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+ ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
+ ASSERT_EQ(AMETA_NONE, mapper1.getMetaState());
+ ASSERT_EQ(AMETA_NONE, mapper2.getMetaState());
+
+ // Toggle scroll lock on and off.
+ process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
+ process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+ ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
+ ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper1.getMetaState());
+ ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper2.getMetaState());
+
+ process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
+ process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+ ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
+ ASSERT_EQ(AMETA_NONE, mapper1.getMetaState());
+ ASSERT_EQ(AMETA_NONE, mapper2.getMetaState());
+}
+
+/**
+ * When there is more than one KeyboardInputMapper for an InputDevice, each mapper should produce
+ * events that use the shared keyboard source across all mappers. This is to ensure that each
+ * input device generates key events in a consistent manner, regardless of which mapper produces
+ * the event.
+ */
+TEST_F(KeyboardInputMapperTest, UsesSharedKeyboardSource) {
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
+
+ // Add a mapper with SOURCE_KEYBOARD
+ KeyboardInputMapper& keyboardMapper =
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
+
+ process(keyboardMapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1);
+ ASSERT_NO_FATAL_FAILURE(
+ mFakeListener->assertNotifyKeyWasCalled(WithSource(AINPUT_SOURCE_KEYBOARD)));
+ process(keyboardMapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0);
+ ASSERT_NO_FATAL_FAILURE(
+ mFakeListener->assertNotifyKeyWasCalled(WithSource(AINPUT_SOURCE_KEYBOARD)));
+
+ // Add a mapper with SOURCE_DPAD
+ KeyboardInputMapper& dpadMapper =
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_DPAD);
+ for (auto* mapper : {&keyboardMapper, &dpadMapper}) {
+ process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(
+ WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD)));
+ process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(
+ WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD)));
+ }
+
+ // Add a mapper with SOURCE_GAMEPAD
+ KeyboardInputMapper& gamepadMapper =
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_GAMEPAD);
+ for (auto* mapper : {&keyboardMapper, &dpadMapper, &gamepadMapper}) {
+ process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(
+ WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD)));
+ process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(
+ WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD)));
+ }
+}
+
+// --- KeyboardInputMapperTest_ExternalAlphabeticDevice ---
+
+class KeyboardInputMapperTest_ExternalAlphabeticDevice : public KeyboardInputMapperUnitTest {
+protected:
+ void SetUp() override {
+ InputMapperUnitTest::SetUp();
+ ON_CALL((*mDevice), getSources).WillByDefault(Return(AINPUT_SOURCE_KEYBOARD));
+ ON_CALL((*mDevice), getKeyboardType).WillByDefault(Return(KeyboardType::ALPHABETIC));
+ ON_CALL((*mDevice), isExternal).WillByDefault(Return(true));
+ EXPECT_CALL(mMockEventHub, getDeviceClasses(EVENTHUB_ID))
+ .WillRepeatedly(Return(InputDeviceClass::KEYBOARD | InputDeviceClass::ALPHAKEY |
+ InputDeviceClass::EXTERNAL));
+ mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration,
+ AINPUT_SOURCE_KEYBOARD);
+ }
+};
+
+// --- KeyboardInputMapperTest_ExternalNonAlphabeticDevice ---
+
+class KeyboardInputMapperTest_ExternalNonAlphabeticDevice : public KeyboardInputMapperUnitTest {
+protected:
+ void SetUp() override {
+ InputMapperUnitTest::SetUp();
+ ON_CALL((*mDevice), getSources).WillByDefault(Return(AINPUT_SOURCE_KEYBOARD));
+ ON_CALL((*mDevice), getKeyboardType).WillByDefault(Return(KeyboardType::NON_ALPHABETIC));
+ ON_CALL((*mDevice), isExternal).WillByDefault(Return(true));
+ EXPECT_CALL(mMockEventHub, getDeviceClasses(EVENTHUB_ID))
+ .WillRepeatedly(Return(InputDeviceClass::KEYBOARD | InputDeviceClass::EXTERNAL));
+ mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration,
+ AINPUT_SOURCE_KEYBOARD);
+ }
+};
+
+TEST_F(KeyboardInputMapperTest_ExternalAlphabeticDevice, WakeBehavior_AlphabeticKeyboard) {
+ // For external devices, keys will trigger wake on key down. Media keys should also trigger
+ // wake if triggered from external devices.
+
+ addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME);
+ addKeyByEvdevCode(KEY_PLAY, AKEYCODE_MEDIA_PLAY);
+ addKeyByEvdevCode(KEY_PLAYPAUSE, AKEYCODE_MEDIA_PLAY_PAUSE, POLICY_FLAG_WAKE);
+
+ std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+ ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
+
+ argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
+ ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
+
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1);
+ ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
+
+ argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0);
+ ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
+
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAYPAUSE, 1);
+ ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
+
+ argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAYPAUSE, 0);
+ ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
+}
+
+TEST_F(KeyboardInputMapperTest_ExternalNonAlphabeticDevice, WakeBehavior_NonAlphabeticKeyboard) {
+ // For external devices, keys will trigger wake on key down. Media keys should not trigger
+ // wake if triggered from external non-alphaebtic keyboard (e.g. headsets).
+
+ addKeyByEvdevCode(KEY_PLAY, AKEYCODE_MEDIA_PLAY);
+ addKeyByEvdevCode(KEY_PLAYPAUSE, AKEYCODE_MEDIA_PLAY_PAUSE, POLICY_FLAG_WAKE);
+
+ std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1);
+ ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
+
+ argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0);
+ ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
+
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAYPAUSE, 1);
+ ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
+
+ argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAYPAUSE, 0);
+ ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
+}
+
+TEST_F(KeyboardInputMapperTest_ExternalAlphabeticDevice, DoNotWakeByDefaultBehavior) {
+ // Tv Remote key's wake behavior is prescribed by the keylayout file.
+
+ addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME, POLICY_FLAG_WAKE);
+ addKeyByEvdevCode(KEY_DOWN, AKEYCODE_DPAD_DOWN);
+ addKeyByEvdevCode(KEY_PLAY, AKEYCODE_MEDIA_PLAY, POLICY_FLAG_WAKE);
+
+ mPropertyMap.addProperty("keyboard.doNotWakeByDefault", "1");
+ mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration,
+ AINPUT_SOURCE_KEYBOARD);
+
+ std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+ ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
+
+ argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
+ ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
+
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_DOWN, 1);
+ ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
+
+ argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_DOWN, 0);
+ ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
+
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1);
+ ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
+
+ argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0);
+ ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
+}
+
} // namespace android
diff --git a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
index f5c4fc5..ae84d7b 100644
--- a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
+++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
@@ -30,6 +30,8 @@
using aidl::android::hardware::power::Boost;
using aidl::android::hardware::power::ChannelConfig;
+using aidl::android::hardware::power::CpuHeadroomParams;
+using aidl::android::hardware::power::GpuHeadroomParams;
using aidl::android::hardware::power::IPower;
using aidl::android::hardware::power::IPowerHintSession;
using aidl::android::hardware::power::Mode;
@@ -71,6 +73,14 @@
MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string * hash), (override));
MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override));
MOCK_METHOD(bool, isRemote, (), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getCpuHeadroom,
+ (const CpuHeadroomParams& params, std::vector<float>* headroom), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getGpuHeadroom,
+ (const GpuHeadroomParams& params, float* headroom), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getCpuHeadroomMinIntervalMillis, (int64_t* interval),
+ (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getGpuHeadroomMinIntervalMillis, (int64_t* interval),
+ (override));
};
// -------------------------------------------------------------------------------------------------
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index 4e080b3..a5e9dde 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -148,9 +148,6 @@
virtual std::optional<LayerSettings> prepareClientComposition(
ClientCompositionTargetSettings&) const = 0;
- // Called after the layer is displayed to update the presentation fence
- virtual void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack layerStack) = 0;
-
// Initializes a promise for a buffer release fence and provides the future for that
// fence. This should only be called when a promise has not yet been created, or
// after the previous promise has already been fulfilled. Attempting to call this
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index cc491c2..712b551 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -105,7 +105,7 @@
aidl::android::hardware::graphics::composer3::Composition from,
aidl::android::hardware::graphics::composer3::Composition to) const;
bool isClientCompositionForced(bool isPeekingThrough) const;
- void updateLuts(std::shared_ptr<gui::DisplayLuts> luts,
+ void updateLuts(const LayerFECompositionState&,
const std::optional<std::vector<std::optional<LutProperties>>>& properties);
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index 05a5d38..c7ff704 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -50,9 +50,6 @@
std::optional<compositionengine::LayerFE::LayerSettings>(
compositionengine::LayerFE::ClientCompositionTargetSettings&));
- MOCK_METHOD(void, onLayerDisplayed, (ftl::SharedFuture<FenceResult>, ui::LayerStack),
- (override));
-
MOCK_METHOD0(createReleaseFenceFuture, ftl::Future<FenceResult>());
MOCK_METHOD1(setReleaseFence, void(const FenceResult&));
MOCK_METHOD0(getReleaseFencePromiseStatus, LayerFE::ReleaseFencePromiseStatus());
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 98b6666..ee813bf 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -895,6 +895,12 @@
})) {
editState().earliestPresentTime = frameTargetPtrOpt->get()->earliestPresentTime();
editState().expectedPresentTime = frameTargetPtrOpt->get()->expectedPresentTime().ns();
+ const auto debugPresentDelay = frameTargetPtrOpt->get()->debugPresentDelay();
+ if (debugPresentDelay) {
+ SFTRACE_FORMAT_INSTANT("DEBUG delaying presentation by %.2fms",
+ debugPresentDelay->ns() / 1e6f);
+ editState().expectedPresentTime += debugPresentDelay->ns();
+ }
}
editState().frameInterval = refreshArgs.frameInterval;
editState().powerCallback = refreshArgs.powerCallback;
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index e31d684..f6d9a1a 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -288,8 +288,13 @@
}
void OutputLayer::updateLuts(
- std::shared_ptr<gui::DisplayLuts> layerFEStateLut,
+ const LayerFECompositionState& layerFEState,
const std::optional<std::vector<std::optional<LutProperties>>>& properties) {
+ auto& luts = layerFEState.luts;
+ if (!luts) {
+ return;
+ }
+
auto& state = editState();
if (!properties) {
@@ -305,7 +310,7 @@
}
}
- for (const auto& inputLut : layerFEStateLut->lutProperties) {
+ for (const auto& inputLut : luts->lutProperties) {
bool foundInHwcLuts = false;
for (const auto& hwcLut : hwcLutProperties) {
if (static_cast<int32_t>(hwcLut.dimension) ==
@@ -318,7 +323,7 @@
break;
}
}
- // if any lut properties of layerFEStateLut can not be found in hwcLutProperties,
+ // if any lut properties of luts can not be found in hwcLutProperties,
// GPU composition instead
if (!foundInHwcLuts) {
state.forceClientComposition = true;
@@ -411,10 +416,7 @@
state.whitePointNits = layerBrightnessNits;
}
- const auto& layerFEStateLut = layerFEState->luts;
- if (layerFEStateLut) {
- updateLuts(layerFEStateLut, properties);
- }
+ updateLuts(*layerFEState, properties);
// These are evaluated every frame as they can potentially change at any
// time.
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index c13e444..86d7388 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -29,6 +29,7 @@
#include <cinttypes>
#include <numeric>
#include <unordered_set>
+#include <vector>
#include "../Jank/JankTracker.h"
@@ -1002,6 +1003,11 @@
finalizeCurrentDisplayFrame();
}
+const std::vector<std::shared_ptr<frametimeline::SurfaceFrame>>& FrameTimeline::getPresentFrames()
+ const {
+ return mPresentFrames;
+}
+
void FrameTimeline::onCommitNotComposited() {
SFTRACE_CALL();
std::scoped_lock lock(mMutex);
@@ -1521,6 +1527,7 @@
mPendingPresentFences.erase(mPendingPresentFences.begin());
}
+ mPresentFrames.clear();
for (size_t i = 0; i < mPendingPresentFences.size(); i++) {
const auto& pendingPresentFence = mPendingPresentFences[i];
nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID;
@@ -1533,6 +1540,13 @@
auto& displayFrame = pendingPresentFence.second;
displayFrame->onPresent(signalTime, mPreviousActualPresentTime);
+
+ // Surface frames have been jank classified and can be provided to caller
+ // to detect if buffer stuffing is occurring.
+ for (const auto& frame : displayFrame->getSurfaceFrames()) {
+ mPresentFrames.push_back(frame);
+ }
+
mPreviousPredictionPresentTime =
displayFrame->trace(mSurfaceFlingerPid, monoBootOffset,
mPreviousPredictionPresentTime, mFilterFramesBeforeTraceStarts);
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index 6cda309..a47bd57 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -328,6 +328,11 @@
virtual void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence,
const std::shared_ptr<FenceTime>& gpuFence) = 0;
+ // Provides surface frames that have already been jank classified in the most recent
+ // flush of pending present fences. This allows buffer stuffing detection from SF.
+ virtual const std::vector<std::shared_ptr<frametimeline::SurfaceFrame>>& getPresentFrames()
+ const = 0;
+
// Tells FrameTimeline that a frame was committed but not composited. This is used to flush
// all the associated surface frames.
virtual void onCommitNotComposited() = 0;
@@ -505,6 +510,8 @@
void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate, Fps renderRate) override;
void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence,
const std::shared_ptr<FenceTime>& gpuFence = FenceTime::NO_FENCE) override;
+ const std::vector<std::shared_ptr<frametimeline::SurfaceFrame>>& getPresentFrames()
+ const override;
void onCommitNotComposited() override;
void parseArgs(const Vector<String16>& args, std::string& result) override;
void setMaxDisplayFrames(uint32_t size) override;
@@ -552,6 +559,9 @@
// display frame, this is a good starting size for the vector so that we can avoid the
// internal vector resizing that happens with push_back.
static constexpr uint32_t kNumSurfaceFramesInitial = 10;
+ // Presented surface frames that have been jank classified and can
+ // indicate of potential buffer stuffing.
+ std::vector<std::shared_ptr<frametimeline::SurfaceFrame>> mPresentFrames;
};
} // namespace impl
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index a8be50a..58f6b96 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -413,6 +413,13 @@
if (forceUpdate || requested.what & layer_state_t::eCropChanged) {
geomCrop = requested.crop;
}
+ if (forceUpdate || requested.what & layer_state_t::ePictureProfileHandleChanged) {
+ pictureProfileHandle = requested.pictureProfileHandle;
+ }
+ if (forceUpdate || requested.what & layer_state_t::eAppContentPriorityChanged) {
+ // TODO(b/337330263): Also consider the system-determined priority of the app
+ pictureProfilePriority = requested.appContentPriority;
+ }
if (forceUpdate || requested.what & layer_state_t::eDefaultFrameRateCompatibilityChanged) {
const auto compatibility =
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index 713a5c5..ee9302b 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -163,7 +163,7 @@
uint64_t clientChanges = what | layer_state_t::diff(clientState);
layer_state_t::merge(clientState);
what = clientChanges;
- LLOGV(layerId, "requested=%" PRIu64 "flags=%" PRIu64, clientState.what, clientChanges);
+ LLOGV(layerId, "requested=%" PRIu64 " flags=%" PRIu64 " ", clientState.what, clientChanges);
if (clientState.what & layer_state_t::eFlagsChanged) {
if ((oldFlags ^ flags) &
@@ -633,7 +633,7 @@
layer_state_t::eEdgeExtensionChanged | layer_state_t::eBufferCropChanged |
layer_state_t::eDestinationFrameChanged | layer_state_t::eDimmingEnabledChanged |
layer_state_t::eExtendedRangeBrightnessChanged |
- layer_state_t::eDesiredHdrHeadroomChanged |
+ layer_state_t::eDesiredHdrHeadroomChanged | layer_state_t::eLutsChanged |
(FlagManager::getInstance().latch_unsignaled_with_auto_refresh_changed()
? layer_state_t::eFlagsChanged
: 0);
diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp
index a346981..231b40b 100644
--- a/services/surfaceflinger/LayerFE.cpp
+++ b/services/surfaceflinger/LayerFE.cpp
@@ -27,7 +27,6 @@
#include "LayerFE.h"
#include "SurfaceFlinger.h"
#include "ui/FenceResult.h"
-#include "ui/LayerStack.h"
namespace android {
@@ -343,11 +342,6 @@
caster.shadow = state;
}
-void LayerFE::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult,
- ui::LayerStack layerStack) {
- mCompositionResult.releaseFences.emplace_back(std::move(futureFenceResult), layerStack);
-}
-
CompositionResult&& LayerFE::stealCompositionResult() {
return std::move(mCompositionResult);
}
diff --git a/services/surfaceflinger/LayerFE.h b/services/surfaceflinger/LayerFE.h
index 658f949..5081e10 100644
--- a/services/surfaceflinger/LayerFE.h
+++ b/services/surfaceflinger/LayerFE.h
@@ -22,14 +22,12 @@
#include "compositionengine/LayerFE.h"
#include "compositionengine/LayerFECompositionState.h"
#include "renderengine/LayerSettings.h"
-#include "ui/LayerStack.h"
#include <ftl/future.h>
namespace android {
struct CompositionResult {
- std::vector<std::pair<ftl::SharedFuture<FenceResult>, ui::LayerStack>> releaseFences;
sp<Fence> lastClientCompositionFence = nullptr;
};
@@ -41,7 +39,6 @@
// compositionengine::LayerFE overrides
const compositionengine::LayerFECompositionState* getCompositionState() const override;
bool onPreComposition(bool updatingOutputGeometryThisFrame) override;
- void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack) override;
const char* getDebugName() const override;
int32_t getSequence() const override;
bool hasRoundedCorners() const override;
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 7729671..fff4284 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -44,10 +44,8 @@
#include <common/FlagManager.h>
#include <scheduler/VsyncConfig.h>
-#include "DisplayHardware/DisplayMode.h"
#include "FrameTimeline.h"
#include "VSyncDispatch.h"
-#include "VSyncTracker.h"
#include "EventThread.h"
@@ -482,6 +480,14 @@
mCondition.notify_all();
}
+// Merge lists of buffer stuffed Uids
+void EventThread::addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ for (auto& [uid, count] : bufferStuffedUids) {
+ mBufferStuffedUids.emplace_or_replace(uid, count);
+ }
+}
+
void EventThread::threadMain(std::unique_lock<std::mutex>& lock) {
DisplayEventConsumers consumers;
@@ -721,6 +727,10 @@
void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event,
const DisplayEventConsumers& consumers) {
+ // List of Uids that have been sent vsync data with queued buffer count.
+ // Used to keep track of which Uids can be removed from the map of
+ // buffer stuffed clients.
+ ftl::SmallVector<uid_t, 10> uidsPostedQueuedBuffers;
for (const auto& consumer : consumers) {
DisplayEventReceiver::Event copy = event;
if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
@@ -730,6 +740,13 @@
event.vsync.vsyncData.preferredExpectedPresentationTime(),
event.vsync.vsyncData.preferredDeadlineTimestamp());
}
+ auto it = mBufferStuffedUids.find(consumer->mOwnerUid);
+ if (it != mBufferStuffedUids.end()) {
+ copy.vsync.vsyncData.numberQueuedBuffers = it->second;
+ uidsPostedQueuedBuffers.emplace_back(consumer->mOwnerUid);
+ } else {
+ copy.vsync.vsyncData.numberQueuedBuffers = 0;
+ }
switch (consumer->postEvent(copy)) {
case NO_ERROR:
break;
@@ -745,6 +762,12 @@
removeDisplayEventConnectionLocked(consumer);
}
}
+ // The clients that have already received the queued buffer count
+ // can be removed from the buffer stuffed Uid list to avoid
+ // being sent duplicate messages.
+ for (auto uid : uidsPostedQueuedBuffers) {
+ mBufferStuffedUids.erase(uid);
+ }
if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC &&
FlagManager::getInstance().vrr_config()) {
mLastCommittedVsyncTime =
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 2daf126..95632c7 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -32,7 +32,6 @@
#include <thread>
#include <vector>
-#include "DisplayHardware/DisplayMode.h"
#include "TracedOrdinal.h"
#include "VSyncDispatch.h"
#include "VsyncSchedule.h"
@@ -55,6 +54,7 @@
// ---------------------------------------------------------------------------
using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
+using BufferStuffingMap = ftl::SmallMap<uid_t, uint32_t, 10>;
enum class VSyncRequest {
None = -2,
@@ -136,6 +136,10 @@
virtual void onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel,
int32_t maxLevel) = 0;
+
+ // An elevated number of queued buffers in the server is detected. This propagates a
+ // flag to Choreographer indicating that buffer stuffing recovery should begin.
+ virtual void addBufferStuffedUids(BufferStuffingMap bufferStuffedUids);
};
struct IEventThreadCallback {
@@ -188,6 +192,8 @@
void onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel,
int32_t maxLevel) override;
+ void addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) override;
+
private:
friend EventThreadTest;
@@ -228,6 +234,10 @@
scheduler::VSyncCallbackRegistration mVsyncRegistration GUARDED_BY(mMutex);
frametimeline::TokenManager* const mTokenManager;
+ // All consumers that need to recover from buffer stuffing and the number
+ // of their queued buffers.
+ BufferStuffingMap mBufferStuffedUids GUARDED_BY(mMutex);
+
IEventThreadCallback& mCallback;
std::thread mThread;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index 84fa139..e4069dd 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -770,17 +770,13 @@
const bool inPrimaryPhysicalRange =
policy->primaryRanges.physical.includes(modePtr->getPeakFps());
const bool inPrimaryRenderRange = policy->primaryRanges.render.includes(fps);
- if (!mIsVrrDevice.load() &&
- ((policy->primaryRangeIsSingleRate() && !inPrimaryPhysicalRange) ||
+ if (((policy->primaryRangeIsSingleRate() && !inPrimaryPhysicalRange) ||
!inPrimaryRenderRange) &&
!(layer.focused &&
(layer.vote == LayerVoteType::ExplicitDefault ||
layer.vote == LayerVoteType::ExplicitExact))) {
// Only focused layers with ExplicitDefault frame rate settings are allowed to score
// refresh rates outside the primary range.
- ALOGV("%s ignores %s (primaryRangeIsSingleRate). Current mode = %s",
- formatLayerInfo(layer, weight).c_str(), to_string(*modePtr).c_str(),
- to_string(activeMode).c_str());
continue;
}
@@ -904,8 +900,7 @@
to_string(descending.front().frameRateMode.fps).c_str());
return {descending, kNoSignals};
} else {
- ALOGV("%s (primaryRangeIsSingleRate)",
- to_string(ranking.front().frameRateMode.fps).c_str());
+ ALOGV("primaryRangeIsSingleRate");
SFTRACE_FORMAT_INSTANT("%s (primaryRangeIsSingleRate)",
to_string(ranking.front().frameRateMode.fps).c_str());
return {ranking, kNoSignals};
@@ -1041,14 +1036,12 @@
// Layers with ExplicitExactOrMultiple expect touch boost
if (globalSignals.touch && hasExplicitExactOrMultiple) {
- ALOGV("%s: Skipping for touch (input signal): uid=%d", __func__, uid);
continue;
}
// Mirrors getRankedFrameRates. If there is no ExplicitDefault, expect touch boost and
// skip frame rate override.
if (hasHighHint && !hasExplicitDefault) {
- ALOGV("%s: Skipping for touch (HighHint): uid=%d", __func__, uid);
continue;
}
@@ -1072,9 +1065,6 @@
constexpr bool isSeamlessSwitch = true;
const auto layerScore = calculateLayerScoreLocked(*layer, fps, isSeamlessSwitch);
score += layer->weight * layerScore;
- ALOGV("%s: %s gives %s fps score of %.4f", __func__,
- formatLayerInfo(*layer, layer->weight).c_str(), to_string(fps).c_str(),
- layerScore);
}
}
@@ -1329,8 +1319,6 @@
LOG_ALWAYS_FATAL_IF(!activeModeOpt);
mActiveModeOpt = FrameRateMode{activeModeOpt->get()->getPeakFps(),
ftl::as_non_null(activeModeOpt->get())};
- mIsVrrDevice = FlagManager::getInstance().vrr_config() &&
- activeModeOpt->get()->getVrrConfig().has_value();
const auto sortedModes = sortByRefreshRate(mDisplayModes);
mMinRefreshRateModeIt = sortedModes.front();
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 274e121..2875650 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -203,12 +203,16 @@
void Scheduler::onFrameSignal(ICompositor& compositor, VsyncId vsyncId,
TimePoint expectedVsyncTime) {
+ const auto debugPresentDelay = mDebugPresentDelay.load();
+ mDebugPresentDelay.store(std::nullopt);
+
const FrameTargeter::BeginFrameArgs beginFrameArgs =
{.frameBeginTime = SchedulerClock::now(),
.vsyncId = vsyncId,
.expectedVsyncTime = expectedVsyncTime,
.sfWorkDuration = mVsyncModulator->getVsyncConfig().sfWorkDuration,
- .hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration};
+ .hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration,
+ .debugPresentTimeDelay = debugPresentDelay};
ftl::NonNull<const Display*> pacesetterPtr = pacesetterPtrLocked();
pacesetterPtr->targeterPtr->beginFrame(beginFrameArgs, *pacesetterPtr->schedulePtr);
@@ -951,6 +955,11 @@
return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides);
}
+void Scheduler::addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) {
+ if (!mRenderEventThread) return;
+ mRenderEventThread->addBufferStuffedUids(std::move(bufferStuffedUids));
+}
+
void Scheduler::promotePacesetterDisplay(PhysicalDisplayId pacesetterId, PromotionParams params) {
std::shared_ptr<VsyncSchedule> pacesetterVsyncSchedule;
{
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index e77af60..61c68a9 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -42,7 +42,6 @@
#include <ui/DisplayId.h>
#include <ui/DisplayMap.h>
-#include "Display/DisplayModeRequest.h"
#include "EventThread.h"
#include "FrameRateOverrideMappings.h"
#include "ISchedulerCallback.h"
@@ -334,6 +333,12 @@
mPacesetterFrameDurationFractionToSkip = frameDurationFraction;
}
+ // Propagates a flag to the EventThread indicating that buffer stuffing
+ // recovery should begin.
+ void addBufferStuffedUids(BufferStuffingMap bufferStuffedUids);
+
+ void setDebugPresentDelay(TimePoint delay) { mDebugPresentDelay = delay; }
+
private:
friend class TestableScheduler;
@@ -599,6 +604,8 @@
FrameRateOverrideMappings mFrameRateOverrideMappings;
SmallAreaDetectionAllowMappings mSmallAreaDetectionAllowMappings;
+
+ std::atomic<std::optional<TimePoint>> mDebugPresentDelay;
};
} // namespace scheduler
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
index 2185bb0..813d4de 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
@@ -53,6 +53,8 @@
TimePoint expectedPresentTime() const { return mExpectedPresentTime; }
+ std::optional<TimePoint> debugPresentDelay() const { return mDebugPresentTimeDelay; }
+
std::optional<TimePoint> earliestPresentTime() const { return mEarliestPresentTime; }
// Equivalent to `expectedSignaledPresentFence` unless running N VSYNCs ahead.
@@ -84,6 +86,7 @@
TimePoint mFrameBeginTime;
TimePoint mExpectedPresentTime;
std::optional<TimePoint> mEarliestPresentTime;
+ std::optional<TimePoint> mDebugPresentTimeDelay;
TracedOrdinal<bool> mFramePending;
TracedOrdinal<bool> mFrameMissed;
@@ -135,6 +138,7 @@
TimePoint expectedVsyncTime;
Duration sfWorkDuration;
Duration hwcMinWorkDuration;
+ std::optional<TimePoint> debugPresentTimeDelay; // used to introduce jank for testing
};
void beginFrame(const BeginFrameArgs&, const IVsyncSource&);
diff --git a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
index 3ee1e54..4f16130 100644
--- a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
+++ b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
@@ -86,6 +86,7 @@
IsFencePendingFuncPtr isFencePendingFuncPtr) {
mVsyncId = args.vsyncId;
mFrameBeginTime = args.frameBeginTime;
+ mDebugPresentTimeDelay = args.debugPresentTimeDelay;
// The `expectedVsyncTime`, which was predicted when this frame was scheduled, is normally in
// the future relative to `frameBeginTime`, but may not be for delayed frames. Adjust
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index a7ab117..a66380b 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -64,11 +64,13 @@
#include <ftl/concat.h>
#include <ftl/fake_guard.h>
#include <ftl/future.h>
+#include <ftl/small_map.h>
#include <ftl/unit.h>
#include <gui/AidlUtil.h>
#include <gui/BufferQueue.h>
#include <gui/DebugEGLImageTracker.h>
#include <gui/IProducerListener.h>
+#include <gui/JankInfo.h>
#include <gui/LayerMetadata.h>
#include <gui/LayerState.h>
#include <gui/Surface.h>
@@ -1418,8 +1420,6 @@
return future.get();
}
-// TODO: b/241285876 - Restore thread safety analysis once mStateLock below is unconditional.
-[[clang::no_thread_safety_analysis]]
void SurfaceFlinger::finalizeDisplayModeChange(PhysicalDisplayId displayId) {
SFTRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str());
@@ -1435,8 +1435,6 @@
if (const auto oldResolution =
mDisplayModeController.getActiveMode(displayId).modePtr->getResolution();
oldResolution != activeMode.modePtr->getResolution()) {
- ConditionalLock lock(mStateLock, !FlagManager::getInstance().connected_display());
-
auto& state = mCurrentState.displays.editValueFor(getPhysicalDisplayTokenLocked(displayId));
// We need to generate new sequenceId in order to recreate the display (and this
// way the framebuffer).
@@ -2597,7 +2595,7 @@
}
{
- ConditionalLock lock(mStateLock, FlagManager::getInstance().connected_display());
+ Mutex::Autolock lock(mStateLock);
for (const auto [displayId, _] : frameTargets) {
if (mDisplayModeController.isModeSetPending(displayId)) {
@@ -2700,13 +2698,6 @@
mScheduler->chooseRefreshRateForContent(&mLayerHierarchyBuilder.getHierarchy(),
updateAttachedChoreographer);
- if (FlagManager::getInstance().connected_display()) {
- initiateDisplayModeChanges();
- }
- }
-
- if (!FlagManager::getInstance().connected_display()) {
- ftl::FakeGuard guard(mStateLock);
initiateDisplayModeChanges();
}
@@ -3072,12 +3063,40 @@
const TimePoint presentTime = TimePoint::now();
+ // The Uids of layer owners that are in buffer stuffing mode, and their elevated
+ // buffer counts. Messages to start recovery are sent exclusively to these Uids.
+ BufferStuffingMap bufferStuffedUids;
+
// Set presentation information before calling Layer::releasePendingBuffer, such that jank
// information from previous' frame classification is already available when sending jank info
// to clients, so they get jank classification as early as possible.
mFrameTimeline->setSfPresent(presentTime.ns(), pacesetterPresentFenceTime,
pacesetterGpuCompositionDoneFenceTime);
+ // Find and register any layers that are in buffer stuffing mode
+ const auto& presentFrames = mFrameTimeline->getPresentFrames();
+
+ for (const auto& frame : presentFrames) {
+ const auto& layer = mLayerLifecycleManager.getLayerFromId(frame->getLayerId());
+ if (!layer) continue;
+ uint32_t numberQueuedBuffers = layer->pendingBuffers ? layer->pendingBuffers->load() : 0;
+ int32_t jankType = frame->getJankType().value_or(JankType::None);
+ if (jankType & JankType::BufferStuffing &&
+ layer->flags & layer_state_t::eRecoverableFromBufferStuffing) {
+ auto [it, wasEmplaced] =
+ bufferStuffedUids.try_emplace(layer->ownerUid.val(), numberQueuedBuffers);
+ // Update with maximum number of queued buffers, allows clients drawing
+ // multiple windows to account for the most severely stuffed window
+ if (!wasEmplaced && it->second < numberQueuedBuffers) {
+ it->second = numberQueuedBuffers;
+ }
+ }
+ }
+
+ if (!bufferStuffedUids.empty()) {
+ mScheduler->addBufferStuffedUids(std::move(bufferStuffedUids));
+ }
+
// We use the CompositionEngine::getLastFrameRefreshTimestamp() which might
// be sampled a little later than when we started doing work for this frame,
// but that should be okay since CompositorTiming has snapping logic.
@@ -3558,7 +3577,9 @@
}
state.isProtected = true;
state.displayName = std::move(info.name);
-
+ state.maxLayerPictureProfiles = getHwComposer().getMaxLayerPictureProfiles(displayId);
+ state.hasPictureProcessing =
+ getHwComposer().hasDisplayCapability(displayId, DisplayCapability::PICTURE_PROCESSING);
mCurrentState.displays.add(token, state);
ALOGI("Connecting %s", displayString);
return activeModeId;
@@ -3719,6 +3740,8 @@
builder.setPixels(resolution);
builder.setIsSecure(state.isSecure);
builder.setIsProtected(state.isProtected);
+ builder.setHasPictureProcessing(state.hasPictureProcessing);
+ builder.setMaxLayerPictureProfiles(state.maxLayerPictureProfiles);
builder.setPowerAdvisor(mPowerAdvisor.get());
builder.setName(state.displayName);
auto compositionDisplay = getCompositionEngine().createDisplay(builder.build());
@@ -6229,7 +6252,7 @@
}
// Numbers from 1000 to 1045 are currently used for backdoors. The code
// in onTransact verifies that the user is root, and has access to use SF.
- if (code >= 1000 && code <= 1045) {
+ if (code >= 1000 && code <= 1046) {
ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code);
return OK;
}
@@ -6762,6 +6785,15 @@
}
return err;
}
+ // Introduce jank to HWC
+ case 1046: {
+ int32_t jankDelayMs = 0;
+ if (data.readInt32(&jankDelayMs) != NO_ERROR) {
+ return BAD_VALUE;
+ }
+ mScheduler->setDebugPresentDelay(TimePoint::fromNs(ms2ns(jankDelayMs)));
+ return NO_ERROR;
+ }
}
}
return err;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index d3479b7..7e9d5b8 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -1370,6 +1370,8 @@
// Flag used to set override desired display mode from backdoor
bool mDebugDisplayModeSetByBackdoor = false;
+ // Tracks the number of maximum queued buffers by layer owner Uid.
+ using BufferStuffingMap = ftl::SmallMap<uid_t, uint32_t, 10>;
BufferCountTracker mBufferCountTracker;
std::unordered_map<DisplayId, sp<HdrLayerInfoReporter>> mHdrLayerInfoListeners
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 625d2e6..268a6c4 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -23,6 +23,7 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <gui/DisplayEventReceiver.h>
#include <log/log.h>
#include <scheduler/VsyncConfig.h>
#include <utils/Errors.h>
@@ -111,6 +112,8 @@
void expectOnExpectedPresentTimePosted(nsecs_t expectedPresentTime);
void expectUidFrameRateMappingEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
std::vector<FrameRateOverride>);
+ void expectQueuedBufferCountReceivedByConnection(
+ ConnectionEventRecorder& connectionEventRecorder, uint32_t expectedBufferCount);
void onVSyncEvent(nsecs_t timestamp, nsecs_t expectedPresentationTime,
nsecs_t deadlineTimestamp) {
@@ -144,6 +147,7 @@
sp<MockEventThreadConnection> mConnection;
sp<MockEventThreadConnection> mThrottledConnection;
std::unique_ptr<frametimeline::impl::TokenManager> mTokenManager;
+ std::vector<ConnectionEventRecorder*> mBufferStuffedConnectionRecorders;
std::chrono::nanoseconds mVsyncPeriod;
@@ -376,6 +380,14 @@
EXPECT_EQ(expectedDisplayId, event.header.displayId);
}
+void EventThreadTest::expectQueuedBufferCountReceivedByConnection(
+ ConnectionEventRecorder& connectionEventRecorder, uint32_t expectedBufferCount) {
+ auto args = connectionEventRecorder.waitForCall();
+ ASSERT_TRUE(args.has_value());
+ const auto& event = std::get<0>(args.value());
+ EXPECT_EQ(expectedBufferCount, event.vsync.vsyncData.numberQueuedBuffers);
+}
+
namespace {
using namespace testing;
@@ -868,6 +880,63 @@
EXPECT_EQ(HDCP_V2, event.hdcpLevelsChange.maxLevel);
}
+TEST_F(EventThreadTest, connectionReceivesBufferStuffing) {
+ setupEventThread();
+
+ // Create a connection that will experience buffer stuffing.
+ ConnectionEventRecorder stuffedConnectionEventRecorder{0};
+ sp<MockEventThreadConnection> stuffedConnection =
+ createConnection(stuffedConnectionEventRecorder,
+ gui::ISurfaceComposer::EventRegistration::modeChanged |
+ gui::ISurfaceComposer::EventRegistration::frameRateOverride,
+ 111);
+
+ // Add a connection and buffer count to the list of stuffed Uids that will receive
+ // data in the next vsync event.
+ BufferStuffingMap bufferStuffedUids;
+ bufferStuffedUids.try_emplace(stuffedConnection->mOwnerUid, 3);
+ mThread->addBufferStuffedUids(bufferStuffedUids);
+ mBufferStuffedConnectionRecorders.emplace_back(&stuffedConnectionEventRecorder);
+
+ // Signal that we want the next vsync event to be posted to two connections.
+ mThread->requestNextVsync(mConnection);
+ mThread->requestNextVsync(stuffedConnection);
+ onVSyncEvent(123, 456, 789);
+
+ // Vsync event data contains number of queued buffers.
+ expectQueuedBufferCountReceivedByConnection(mConnectionEventCallRecorder, 0);
+ expectQueuedBufferCountReceivedByConnection(stuffedConnectionEventRecorder, 3);
+}
+
+TEST_F(EventThreadTest, connectionsWithSameUidReceiveBufferStuffing) {
+ setupEventThread();
+
+ // Create a connection with the same Uid as another connection.
+ ConnectionEventRecorder secondConnectionEventRecorder{0};
+ sp<MockEventThreadConnection> secondConnection =
+ createConnection(secondConnectionEventRecorder,
+ gui::ISurfaceComposer::EventRegistration::modeChanged |
+ gui::ISurfaceComposer::EventRegistration::frameRateOverride,
+ mConnectionUid);
+
+ // Add connection Uid and buffer count to the list of stuffed Uids that will receive
+ // data in the next vsync event.
+ BufferStuffingMap bufferStuffedUids;
+ bufferStuffedUids.try_emplace(mConnectionUid, 3);
+ mThread->addBufferStuffedUids(bufferStuffedUids);
+ mBufferStuffedConnectionRecorders.emplace_back(&mConnectionEventCallRecorder);
+ mBufferStuffedConnectionRecorders.emplace_back(&secondConnectionEventRecorder);
+
+ // Signal that we want the next vsync event to be posted to two connections.
+ mThread->requestNextVsync(mConnection);
+ mThread->requestNextVsync(secondConnection);
+ onVSyncEvent(123, 456, 789);
+
+ // Vsync event data contains number of queued buffers.
+ expectQueuedBufferCountReceivedByConnection(mConnectionEventCallRecorder, 3);
+ expectQueuedBufferCountReceivedByConnection(secondConnectionEventRecorder, 3);
+}
+
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 0dfbd61..08e4265 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -166,6 +166,7 @@
a.presentTime == b.presentTime;
}
+ NO_THREAD_SAFETY_ANALYSIS
const std::map<int64_t, TimelineItem>& getPredictions() const {
return mTokenManager->mPredictions;
}
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 06518fb..8c53eef 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -28,7 +28,6 @@
#include "ui/GraphicTypes.h"
#include <com_android_graphics_libgui_flags.h>
-#include <com_android_graphics_surfaceflinger_flags.h>
#define UPDATE_AND_VERIFY(BUILDER, ...) \
({ \
@@ -2021,4 +2020,45 @@
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
EXPECT_FALSE(getSnapshot(1)->contentDirty);
}
+TEST_F(LayerSnapshotTest, shouldUpdatePictureProfileHandle) {
+ if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
+ GTEST_SKIP() << "Flag disabled, skipping test";
+ }
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().layerId = 1;
+ transactions.back().states.front().state.layerId = 1;
+ transactions.back().states.front().state.what = layer_state_t::ePictureProfileHandleChanged;
+ transactions.back().states.front().state.pictureProfileHandle = PictureProfileHandle(3);
+
+ mLifecycleManager.applyTransactions(transactions);
+ EXPECT_EQ(mLifecycleManager.getGlobalChanges(), RequestedLayerState::Changes::Content);
+
+ update(mSnapshotBuilder);
+
+ EXPECT_EQ(getSnapshot(1)->clientChanges, layer_state_t::ePictureProfileHandleChanged);
+ EXPECT_EQ(getSnapshot(1)->pictureProfileHandle, PictureProfileHandle(3));
+}
+
+TEST_F(LayerSnapshotTest, shouldUpdatePictureProfilePriorityFromAppContentPriority) {
+ if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
+ GTEST_SKIP() << "Flag disabled, skipping test";
+ }
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().layerId = 1;
+ transactions.back().states.front().state.layerId = 1;
+ transactions.back().states.front().state.what = layer_state_t::eAppContentPriorityChanged;
+ transactions.back().states.front().state.appContentPriority = 3;
+
+ mLifecycleManager.applyTransactions(transactions);
+ EXPECT_EQ(mLifecycleManager.getGlobalChanges(), RequestedLayerState::Changes::Content);
+
+ update(mSnapshotBuilder);
+
+ EXPECT_EQ(getSnapshot(1)->pictureProfilePriority, 3);
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index b5be8db..80b2b8d 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -308,42 +308,6 @@
<< " category=" << ftl::enum_string(testCase.frameRateCategory);
}
}
-
- template <class T>
- std::vector<LayerRequirement> createLayers(const std::initializer_list<T>& surfaceVotes) {
- std::vector<LayerRequirement> layers;
- for (auto surfaceVote : surfaceVotes) {
- ALOGI("**** %s: Adding layers for %s: (desiredFrameRate=%s, voteType=%s), "
- "(frameRateCategory=%s)",
- __func__, surfaceVote.name.c_str(),
- to_string(surfaceVote.desiredFrameRate).c_str(),
- ftl::enum_string(surfaceVote.voteType).c_str(),
- ftl::enum_string(surfaceVote.frameRateCategory).c_str());
-
- if (surfaceVote.desiredFrameRate.isValid()) {
- std::stringstream ss;
- ss << surfaceVote.name << " (" << surfaceVote.weight << "): ExplicitDefault ("
- << to_string(surfaceVote.desiredFrameRate) << ")";
- LayerRequirement layer = {.name = ss.str(),
- .vote = surfaceVote.voteType,
- .desiredRefreshRate = surfaceVote.desiredFrameRate,
- .weight = surfaceVote.weight};
- layers.push_back(layer);
- }
-
- if (surfaceVote.frameRateCategory != FrameRateCategory::Default) {
- std::stringstream ss;
- ss << surfaceVote.name << " (" << surfaceVote.weight << "): ExplicitCategory ("
- << ftl::enum_string(surfaceVote.frameRateCategory) << ")";
- LayerRequirement layer = {.name = ss.str(),
- .vote = LayerVoteType::ExplicitCategory,
- .frameRateCategory = surfaceVote.frameRateCategory,
- .weight = surfaceVote.weight};
- layers.push_back(layer);
- }
- }
- return layers;
- }
};
RefreshRateSelectorTest::RefreshRateSelectorTest() {
@@ -1812,98 +1776,6 @@
selector);
}
-TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_multiSurface_arr) {
- if (GetParam() != Config::FrameRateOverride::Enabled) {
- return;
- }
-
- SET_FLAG_FOR_TEST(flags::vrr_config, true);
-
- auto selector = createSelector(kVrrMode_120, kModeId120);
-
- // Switch the policy to be more like an ARR device (primary range is a single rate).
- constexpr FpsRange k120_120Hz = {120_Hz, 120_Hz};
- constexpr FpsRange k0_120Hz = {0_Hz, 120_Hz};
- constexpr FpsRanges kPrimaryRanges = {/*physical*/ k120_120Hz,
- /*render*/ k120_120Hz};
- constexpr FpsRanges kAppRequestRanges = {/*physical*/ k120_120Hz,
- /*render*/ k0_120Hz};
- EXPECT_EQ(SetPolicyResult::Changed,
- selector.setDisplayManagerPolicy(
- {/*defaultMode*/ kModeId120, kPrimaryRanges, kAppRequestRanges}));
-
- // Surface can translate to multiple layers in SF scheduler due to category and frame rate
- // value.
- struct SurfaceVote {
- // Params
- std::string name = "";
- Fps desiredFrameRate = 0_Hz;
- LayerVoteType voteType = LayerVoteType::ExplicitDefault;
- FrameRateCategory frameRateCategory = FrameRateCategory::Default;
- float weight = 1.f;
- };
-
- auto layers = createLayers(
- std::initializer_list<SurfaceVote>{{.name = "60 fixed source",
- .desiredFrameRate = 60_Hz,
- .voteType = LayerVoteType::ExplicitExactOrMultiple,
- .weight = 0.27f},
- {.name = "1 fixed source + NoPreference",
- .desiredFrameRate = 1_Hz,
- .voteType = LayerVoteType::ExplicitExactOrMultiple,
- .frameRateCategory =
- FrameRateCategory::NoPreference}});
- auto actualRankedFrameRates = selector.getRankedFrameRates(layers);
- EXPECT_EQ(60_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps);
-
- layers = createLayers(
- std::initializer_list<SurfaceVote>{{.name = "60 fixed source",
- .desiredFrameRate = 60_Hz,
- .voteType = LayerVoteType::ExplicitExactOrMultiple,
- .weight = 0.27f},
- {.name = "1 fixed source + Normal",
- .desiredFrameRate = 1_Hz,
- .voteType = LayerVoteType::ExplicitExactOrMultiple,
- .frameRateCategory = FrameRateCategory::Normal}});
- actualRankedFrameRates = selector.getRankedFrameRates(layers);
- EXPECT_EQ(60_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps);
-
- layers = createLayers(std::initializer_list<SurfaceVote>{
- {.name = "30 fixed source + NoPreference",
- .desiredFrameRate = 30_Hz,
- .voteType = LayerVoteType::ExplicitExactOrMultiple,
- .frameRateCategory = FrameRateCategory::NoPreference}});
- actualRankedFrameRates = selector.getRankedFrameRates(layers);
- EXPECT_EQ(30_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps);
-
- layers = createLayers(std::initializer_list<SurfaceVote>{
- {.name = "1 fixed source + NoPreference",
- .desiredFrameRate = 1_Hz,
- .voteType = LayerVoteType::ExplicitExactOrMultiple,
- .frameRateCategory = FrameRateCategory::NoPreference}});
- actualRankedFrameRates = selector.getRankedFrameRates(layers);
- // Result affected by RefreshRateSelector.kMinSupportedFrameRate.
- EXPECT_EQ(20_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps);
-
- layers = createLayers(std::initializer_list<SurfaceVote>{
- {.name = "24 fixed source + NoPreference",
- .desiredFrameRate = 24_Hz,
- .voteType = LayerVoteType::ExplicitExactOrMultiple,
- .frameRateCategory = FrameRateCategory::NoPreference}});
- actualRankedFrameRates = selector.getRankedFrameRates(layers);
- EXPECT_EQ(24_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps);
-
- layers = createLayers(std::initializer_list<SurfaceVote>{
- {.name = "23.976 fixed source + NoPreference",
- .desiredFrameRate = 23.976_Hz,
- .voteType = LayerVoteType::ExplicitExactOrMultiple,
- .frameRateCategory = FrameRateCategory::NoPreference}});
- actualRankedFrameRates = selector.getRankedFrameRates(layers);
- // Chooses 120 unless certain threshold is set, see tests test23976Chooses120 and
- // test23976Chooses60IfThresholdIs120.
- EXPECT_EQ(120_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps);
-}
-
TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_60_120) {
auto selector = createSelector(makeModes(kMode60, kMode120), kModeId60);
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index 82500fe..c976697 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -53,6 +53,7 @@
MOCK_METHOD(void, onHdcpLevelsChanged,
(PhysicalDisplayId displayId, int32_t connectedLevel, int32_t maxLevel),
(override));
+ MOCK_METHOD(void, addBufferStuffedUids, (BufferStuffingMap), (override));
};
} // namespace android::mock
diff --git a/vulkan/libvulkan/libvulkan_flags.aconfig b/vulkan/libvulkan/libvulkan_flags.aconfig
index 891bc02..dae5b52 100644
--- a/vulkan/libvulkan/libvulkan_flags.aconfig
+++ b/vulkan/libvulkan/libvulkan_flags.aconfig
@@ -8,3 +8,11 @@
bug: "341978292"
is_fixed_read_only: true
}
+
+flag {
+ name: "vulkan_1_4_instance_api"
+ namespace: "core_graphics"
+ description: "Enable support for the Vulkan 1.4 instance API"
+ bug: "370568136"
+ is_fixed_read_only: true
+}