Merge "Remove obsolete BLASTBufferQueue constructor" into main
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index 372008e..a5d176d 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -83,14 +83,16 @@
"aconfig_lib_cc_static_link.defaults",
"dumpstate_cflag_defaults",
],
+ // See README.md: "Dumpstate philosophy: exec not link"
+ // Do not add things here - keep dumpstate as simple as possible and exec where possible.
shared_libs: [
"android.hardware.dumpstate@1.0",
"android.hardware.dumpstate@1.1",
"android.hardware.dumpstate-V1-ndk",
"libziparchive",
"libbase",
- "libbinder",
- "libbinder_ndk",
+ "libbinder", // BAD: dumpstate should not link code directly, should only exec binaries
+ "libbinder_ndk", // BAD: dumpstate should not link code directly, should only exec binaries
"libcrypto",
"libcutils",
"libdebuggerd_client",
@@ -98,11 +100,11 @@
"libdumpstateutil",
"libdumputils",
"libhardware_legacy",
- "libhidlbase",
+ "libhidlbase", // BAD: dumpstate should not link code directly, should only exec binaries
"liblog",
"libutils",
- "libvintf",
- "libbinderdebug",
+ "libvintf", // BAD: dumpstate should not link code directly, should only exec binaries
+ "libbinderdebug", // BAD: dumpstate should not link code directly, should only exec binaries
"packagemanager_aidl-cpp",
"server_configurable_flags",
"device_policy_aconfig_flags_c_lib",
@@ -134,6 +136,7 @@
"main.cpp",
],
required: [
+ "alloctop",
"atrace",
"bugreport_procdump",
"default_screenshot",
diff --git a/cmds/dumpstate/README.md b/cmds/dumpstate/README.md
index 26dabbb..3ab971a 100644
--- a/cmds/dumpstate/README.md
+++ b/cmds/dumpstate/README.md
@@ -21,6 +21,18 @@
mmm -j frameworks/native/cmds/dumpstate device/acme/secret_device/dumpstate/ hardware/interfaces/dumpstate
```
+## Dumpstate philosophy: exec not link
+
+Never link code directly into dumpstate. Dumpstate should execute many
+binaries and collect the results. In general, code should fail hard fail fast,
+but dumpstate is the last to solve many Android bugs. Oftentimes, failures
+in core Android infrastructure or tools are issues that cause problems in
+bugreport directly, so bugreport should not rely on these tools working.
+We want dumpstate to have as minimal of code loaded in process so that
+only that core subset needs to be bugfree for bugreport to work. Even if
+many pieces of Android break, that should not prevent dumpstate from
+working.
+
## To build, deploy, and take a bugreport
```
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 4758607..feca18d 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -128,6 +128,9 @@
using android::os::dumpstate::TaskQueue;
using android::os::dumpstate::WaitForTask;
+// BAD - See README.md: "Dumpstate philosophy: exec not link"
+// Do not add more complicated variables here, prefer to execute only. Don't link more code here.
+
// Keep in sync with
// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
static const int TRACE_DUMP_TIMEOUT_MS = 10000; // 10 seconds
@@ -1264,6 +1267,17 @@
RunCommand("IP RULES v6", {"ip", "-6", "rule", "show"});
}
+static void DumpKernelMemoryAllocations() {
+ if (!access("/proc/allocinfo", F_OK)) {
+ // Print the top 100 biggest memory allocations of at least one byte.
+ // The output is sorted by size, descending.
+ RunCommand("KERNEL MEMORY ALLOCATIONS",
+ {"alloctop", "--once", "--sort", "s", "--min", "1", "--lines", "100"});
+ }
+}
+
+// BAD - See README.md: "Dumpstate philosophy: exec not link"
+// This should all be moved into a separate binary rather than have complex logic here.
static Dumpstate::RunStatus RunDumpsysTextByPriority(const std::string& title, int priority,
std::chrono::milliseconds timeout,
std::chrono::milliseconds service_timeout) {
@@ -1344,6 +1358,8 @@
service_timeout);
}
+// BAD - See README.md: "Dumpstate philosophy: exec not link"
+// This should all be moved into a separate binary rather than have complex logic here.
static Dumpstate::RunStatus RunDumpsysProto(const std::string& title, int priority,
std::chrono::milliseconds timeout,
std::chrono::milliseconds service_timeout) {
@@ -1425,6 +1441,8 @@
* Dumpstate can pick up later and output to the bugreport. Using STDOUT_FILENO
* if it's not running in the parallel task.
*/
+// BAD - See README.md: "Dumpstate philosophy: exec not link"
+// This should all be moved into a separate binary rather than have complex logic here.
static void DumpHals(int out_fd = STDOUT_FILENO) {
RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all"},
CommandOptions::WithTimeout(10).AsRootIfAvailable().Build(),
@@ -1481,6 +1499,9 @@
}
}
+// BAD - See README.md: "Dumpstate philosophy: exec not link"
+// This should all be moved into a separate binary rather than have complex logic here.
+//
// Dump all of the files that make up the vendor interface.
// See the files listed in dumpFileList() for the latest list of files.
static void DumpVintf() {
@@ -1510,6 +1531,8 @@
printf("------ EXTERNAL FRAGMENTATION INFO ------\n");
std::ifstream ifs("/proc/buddyinfo");
auto unusable_index_regex = std::regex{"Node\\s+([0-9]+),\\s+zone\\s+(\\S+)\\s+(.*)"};
+ // BAD - See README.md: "Dumpstate philosophy: exec not link"
+ // This should all be moved into a separate binary rather than have complex logic here.
for (std::string line; std::getline(ifs, line);) {
std::smatch match_results;
if (std::regex_match(line, match_results, unusable_index_regex)) {
@@ -1773,6 +1796,8 @@
DoKmsg();
+ DumpKernelMemoryAllocations();
+
DumpShutdownCheckpoints();
DumpIpAddrAndRules();
@@ -2453,6 +2478,8 @@
return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::DEFAULT;
}
+// BAD - See README.md: "Dumpstate philosophy: exec not link"
+// This should all be moved into a separate binary rather than have complex logic here.
static void DoDumpstateBoardHidl(
const sp<dumpstate_hal_hidl_1_0::IDumpstateDevice> dumpstate_hal_1_0,
const std::vector<::ndk::ScopedFileDescriptor>& dumpstate_fds,
diff --git a/cmds/lshal/libprocpartition/include/procpartition/procpartition.h b/cmds/lshal/libprocpartition/include/procpartition/procpartition.h
index ca1e690..9c0fc18 100644
--- a/cmds/lshal/libprocpartition/include/procpartition/procpartition.h
+++ b/cmds/lshal/libprocpartition/include/procpartition/procpartition.h
@@ -27,6 +27,8 @@
enum class Partition {
UNKNOWN = 0,
SYSTEM,
+ SYSTEM_EXT,
+ PRODUCT,
VENDOR,
ODM
};
diff --git a/cmds/lshal/libprocpartition/procpartition.cpp b/cmds/lshal/libprocpartition/procpartition.cpp
index 9645f3a..35fad57 100644
--- a/cmds/lshal/libprocpartition/procpartition.cpp
+++ b/cmds/lshal/libprocpartition/procpartition.cpp
@@ -24,6 +24,8 @@
std::ostream& operator<<(std::ostream& os, Partition p) {
switch (p) {
case Partition::SYSTEM: return os << "system";
+ case Partition::SYSTEM_EXT: return os << "system_ext";
+ case Partition::PRODUCT: return os << "product";
case Partition::VENDOR: return os << "vendor";
case Partition::ODM: return os << "odm";
case Partition::UNKNOWN: // fallthrough
@@ -57,6 +59,12 @@
if (s == "system") {
return Partition::SYSTEM;
}
+ if (s == "system_ext") {
+ return Partition::SYSTEM_EXT;
+ }
+ if (s == "product") {
+ return Partition::PRODUCT;
+ }
if (s == "vendor") {
return Partition::VENDOR;
}
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 38a125b..59c4d53 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -410,7 +410,16 @@
return Status::ok();
}
-Status ServiceManager::checkService(const std::string& name, os::Service* outService) {
+Status ServiceManager::checkService(const std::string& name, sp<IBinder>* outBinder) {
+ SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_PROTO_FIELDS(
+ PERFETTO_TE_PROTO_FIELD_CSTR(kProtoServiceName, name.c_str())));
+
+ *outBinder = tryGetBinder(name, false).service;
+ // returns ok regardless of result for legacy reasons
+ return Status::ok();
+}
+
+Status ServiceManager::checkService2(const std::string& name, os::Service* outService) {
SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_PROTO_FIELDS(
PERFETTO_TE_PROTO_FIELD_CSTR(kProtoServiceName, name.c_str())));
diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h
index 964abee..5c4d891 100644
--- a/cmds/servicemanager/ServiceManager.h
+++ b/cmds/servicemanager/ServiceManager.h
@@ -46,7 +46,8 @@
// getService will try to start any services it cannot find
binder::Status getService(const std::string& name, sp<IBinder>* outBinder) override;
binder::Status getService2(const std::string& name, os::Service* outService) override;
- binder::Status checkService(const std::string& name, os::Service* outService) override;
+ binder::Status checkService(const std::string& name, sp<IBinder>* outBinder) override;
+ binder::Status checkService2(const std::string& name, os::Service* outService) override;
binder::Status addService(const std::string& name, const sp<IBinder>& binder,
bool allowIsolated, int32_t dumpPriority) override;
binder::Status listServices(int32_t dumpPriority, std::vector<std::string>* outList) override;
diff --git a/cmds/servicemanager/test_sm.cpp b/cmds/servicemanager/test_sm.cpp
index e620770..7ad84fa 100644
--- a/cmds/servicemanager/test_sm.cpp
+++ b/cmds/servicemanager/test_sm.cpp
@@ -204,6 +204,11 @@
sp<IBinder> outBinder;
EXPECT_TRUE(sm->getService("foo", &outBinder).isOk());
EXPECT_EQ(service, outBinder);
+
+ EXPECT_TRUE(sm->checkService2("foo", &out).isOk());
+ EXPECT_EQ(service, out.get<Service::Tag::serviceWithMetadata>().service);
+ EXPECT_TRUE(sm->checkService("foo", &outBinder).isOk());
+ EXPECT_EQ(service, outBinder);
}
TEST(GetService, NonExistant) {
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 64ef827..cd8ca89 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -305,6 +305,12 @@
}
prebuilt_etc {
+ name: "android.hardware.telephony.satellite.prebuilt.xml",
+ src: "android.hardware.telephony.satellite.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
name: "android.hardware.telephony.ims.singlereg.prebuilt.xml",
src: "android.hardware.telephony.ims.singlereg.xml",
defaults: ["frameworks_native_data_etc_defaults"],
diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h
index 9ea6549..52dbb61 100644
--- a/include/android/performance_hint.h
+++ b/include/android/performance_hint.h
@@ -29,7 +29,7 @@
* 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
+ * Unlike reportActualWorkDuration, the "notifyWorkload..." 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.
@@ -71,6 +71,10 @@
#include <stdint.h>
#include <unistd.h>
+#if !defined(__DEPRECATED_IN)
+#define __DEPRECATED_IN(__api_level, ...) __attribute__((__deprecated__))
+#endif
+
__BEGIN_DECLS
struct APerformanceHintManager;
@@ -116,13 +120,13 @@
* An opaque type representing a handle to a performance hint session creation configuration.
* It is consumed by {@link APerformanceHint_createSessionUsingConfig}.
*
- * A session creation config encapsulates the required information for a session.
- * Additionally, the caller can set various settings for the session,
- * to be passed during creation, streamlining the session setup process.
+ * A session creation config encapsulates the required information for creating a session. The only
+ * mandatory parameter is the set of TIDs, set using {@link ASessionCreationConfig_setTids}. Only
+ * parameters relevant to the session need to be set, and any unspecified functionality will be
+ * treated as unused on the session. Configurations without a valid set of TIDs, or which try to
+ * enable automatic timing without the graphics pipeline mode, are considered invalid.
*
- * The caller may reuse this object and modify the settings in it
- * to create additional sessions.
- *
+ * The caller may reuse this object and modify the settings in it to create additional sessions.
*/
typedef struct ASessionCreationConfig ASessionCreationConfig;
@@ -181,27 +185,43 @@
int64_t initialTargetWorkDurationNanos) __INTRODUCED_IN(__ANDROID_API_T__);
/**
- * Creates a session for the given set of threads that are graphics pipeline threads
- * and set their initial target work duration.
+ * Creates a session using arguments from a corresponding {@link ASessionCreationConfig}.
+ *
+ * Note: when using graphics pipeline mode, using too many cumulative graphics pipeline threads is
+ * not a failure and will still create a session, but it will cause all graphics pipeline sessions
+ * to have undefined behavior and the method will return EBUSY.
*
* @param manager The performance hint manager instance.
* @param config The configuration struct containing required information
* to create a session.
- * @return APerformanceHintSession pointer on success, nullptr on failure.
+ * @param sessionOut A client-provided pointer, which will be set to the new APerformanceHintSession
+ * on success or EBUSY, and to nullptr on failure.
+ *
+ * @return 0 on success.
+ * EINVAL if the creation config is in an invalid state.
+ * EPIPE if communication failed.
+ * ENOTSUP if hint sessions are not supported, or if auto timing is enabled but unsupported.
+ * EBUSY if too many graphics pipeline threads are passed.
*/
-APerformanceHintSession* _Nullable APerformanceHint_createSessionUsingConfig(
+int APerformanceHint_createSessionUsingConfig(
APerformanceHintManager* _Nonnull manager,
- ASessionCreationConfig* _Nonnull config)
- __INTRODUCED_IN(36);
+ ASessionCreationConfig* _Nonnull config,
+ APerformanceHintSession * _Nullable * _Nonnull sessionOut) __INTRODUCED_IN(36);
/**
* Get preferred update rate information for this device.
*
+ * @deprecated Client side rate limiting is not necessary, rate limiting is handled in the
+ * framework. If you were using this to check for hint session support, please use
+ * {@link APerformanceHint_isFeatureSupported} instead.
+ *
* @param manager The performance hint manager instance.
* @return the preferred update rate supported by device software.
*/
int64_t APerformanceHint_getPreferredUpdateRateNanos(
- APerformanceHintManager* _Nonnull manager) __INTRODUCED_IN(__ANDROID_API_T__);
+ APerformanceHintManager* _Nonnull manager)
+ __INTRODUCED_IN(__ANDROID_API_T__) __DEPRECATED_IN(36, "Client-side rate limiting is not"
+ " necessary, use APerformanceHint_isFeatureSupported for support checking.");
/**
* Get maximum number of graphics pipieline threads per-app for this device.
@@ -216,9 +236,11 @@
* Updates this session's target duration for each cycle of work.
*
* @param session The performance hint session instance to update.
- * @param targetDurationNanos The new desired duration in nanoseconds. This must be positive.
+ * @param targetDurationNanos The new desired duration in nanoseconds. This must be positive for the
+ * session to report work durations, and may be zero to disable this functionality.
+ *
* @return 0 on success.
- * EINVAL if targetDurationNanos is not positive.
+ * EINVAL if targetDurationNanos is less than zero.
* EPIPE if communication with the system service has failed.
*/
int APerformanceHint_updateTargetWorkDuration(
@@ -235,7 +257,7 @@
* @param actualDurationNanos The duration of time the thread group took to complete its last
* task in nanoseconds. This must be positive.
* @return 0 on success.
- * EINVAL if actualDurationNanos is not positive.
+ * EINVAL if actualDurationNanos is not positive or the target it not positive.
* EPIPE if communication with the system service has failed.
*/
int APerformanceHint_reportActualWorkDuration(
@@ -258,15 +280,20 @@
* Set a list of threads to the performance hint session. This operation will replace
* the current list of threads with the given list of threads.
*
+ * Note: when using a session with the graphics pipeline mode enabled, using too many cumulative
+ * graphics pipeline threads is not a failure, but it will cause all graphics pipeline sessions to
+ * have undefined behavior and the method will return EBUSY.
+ *
* @param session The performance hint session instance to update.
* @param threadIds The list of threads to be associated with this session. They must be part of
* this app's thread group.
* @param size The size of the list of threadIds.
* @return 0 on success.
* EINVAL if the list of thread ids is empty or if any of the thread ids are not part of
- the thread group.
+ * the thread group.
* EPIPE if communication with the system service has failed.
* EPERM if any thread id doesn't belong to the application.
+ * EBUSY if too many graphics pipeline threads were passed.
*/
int APerformanceHint_setThreads(
APerformanceHintSession* _Nonnull session,
@@ -311,89 +338,102 @@
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.
+ * Informs the framework of an upcoming increase in the workload of 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.
+ * These hints should be sent shortly before the start of the cycle where the workload is going to
+ * change, or as early as possible during that cycle for maximum effect. Hints sent towards the end
+ * of the cycle may be interpreted as applying to the next cycle. Any unsupported hints will be
+ * silently dropped, to avoid the need for excessive support checking each time they are sent, and
+ * sending a hint for both CPU and GPU will count as two separate hints for the rate limiter. These
+ * hints should not be sent repeatedly for an ongoing expensive workload, as workload time reporting
+ * is intended to handle this.
*
+ * @param session The {@link APerformanceHintSession} instance to send a hint for.
* @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.
+ * @param identifier A required string used to distinguish this specific hint, using utf-8 encoding.
+ * This string will only be held for the duration of the method, and can be 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);
+ bool cpu, bool gpu, const char* _Nonnull identifier) __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.
+ * Informs the framework that the workload associated with this session is about to start, or that
+ * it is about to completely change, and that the system should discard any assumptions about its
+ * characteristics inferred from previous activity. 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.
+ * These hints should be sent shortly before the start of the cycle where the workload is going to
+ * change, or as early as possible during that cycle for maximum effect. Hints sent towards the end
+ * of the cycle may be interpreted as applying to the next cycle. Any unsupported hints will be
+ * silently dropped, to avoid the need for excessive support checking each time they are sent, and
+ * sending a hint for both CPU and GPU will count as two separate hints for the rate limiter. These
+ * hints should not be sent repeatedly for an ongoing expensive workload, as workload time reporting
+ * is intended to handle this.
*
+ * @param session The {@link APerformanceHintSession} instance to send a hint for.
* @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.
+ * @param identifier A required string used to distinguish this specific hint, using utf-8 encoding.
+ * This string will only be held for the duration of the method, and can be 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);
+ bool cpu, bool gpu, const char* _Nonnull identifier) __INTRODUCED_IN(36);
/**
- * Informs the framework of an upcoming one-off expensive frame for a graphics pipeline
- * bound to this session. This frame will be treated as not representative of the workload as a
- * whole, and it will be discarded the purposes of load tracking. The user can specify
- * whether the workload spike is expected to be on the CPU, GPU, or both.
+ * Informs the framework of an upcoming one-off expensive workload cycle for a given session.
+ * This cycle will be treated as not representative of the workload as a whole, and it will be
+ * discarded the purposes of load tracking. The user can specify whether the workload spike 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.
+ * These hints should be sent shortly before the start of the cycle where the workload is going to
+ * change, or as early as possible during that cycle for maximum effect. Hints sent towards the end
+ * of the cycle may be interpreted as applying to the next cycle. Any unsupported hints will be
+ * silently dropped, to avoid the need for excessive support checking each time they are sent, and
+ * sending a hint for both CPU and GPU will count as two separate hints for the rate limiter. These
+ * hints should not be sent repeatedly for an ongoing expensive workload, as workload time reporting
+ * is intended to handle this.
*
+ * @param session The {@link APerformanceHintSession} instance to send a hint for.
* @param cpu Indicates if the workload spike is expected to affect the CPU.
* @param gpu Indicates if the workload spike 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.
+ * @param identifier A required string used to distinguish this specific hint, using utf-8 encoding.
+ * This string will only be held for the duration of the method, and can be 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_notifyWorkloadSpike(
APerformanceHintSession* _Nonnull session,
- bool cpu, bool gpu, const char* _Nonnull debugName) __INTRODUCED_IN(36);
+ bool cpu, bool gpu, const char* _Nonnull identifier) __INTRODUCED_IN(36);
/**
* Associates a session with any {@link ASurfaceControl} or {@link ANativeWindow}
- * instances managed by this session.
+ * instances managed by this session. Any previously associated objects that are not passed
+ * in again lose their association. Invalid or dead instances are ignored, and passing both
+ * lists as null drops all current associations.
*
* This method is primarily intended for sessions that manage the timing of an entire
- * graphics pipeline end-to-end, such as those using the
+ * graphics pipeline end-to-end for frame pacing, such as those using the
* {@link ASessionCreationConfig_setGraphicsPipeline} API. However, any session directly
* or indirectly managing a graphics pipeline should still associate themselves with
* directly relevant ASurfaceControl or ANativeWindow instances for better optimization.
+ * Additionally, if the surface associated with a session changes, this method should be called
+ * again to re-create the association.
*
- * To see any benefit from this method, the client must make sure they are updating the framerate
+ * To see any benefit from this method, the client must make sure they are updating the frame rate
* of attached surfaces using methods such as {@link ANativeWindow_setFrameRate}, or by updating
* any associated ASurfaceControls with transactions that have {ASurfaceTransaction_setFrameRate}.
*
@@ -407,16 +447,77 @@
*
* @return 0 on success.
* EPIPE if communication has failed.
- * ENOTSUP if unsupported.
- * EINVAL if invalid or empty arguments passed.
+ * ENOTSUP if this is not supported on the device.
*/
int APerformanceHint_setNativeSurfaces(APerformanceHintSession* _Nonnull session,
- ANativeWindow* _Nonnull* _Nullable nativeWindows, int nativeWindowsSize,
- ASurfaceControl* _Nonnull* _Nullable surfaceControls, int surfaceControlsSize)
+ ANativeWindow* _Nonnull* _Nullable nativeWindows, size_t nativeWindowsSize,
+ ASurfaceControl* _Nonnull* _Nullable surfaceControls, size_t surfaceControlsSize)
__INTRODUCED_IN(36);
/**
+ * This enum represents different aspects of performance hint functionality. These can be passed
+ * to {@link APerformanceHint_isFeatureSupported} to determine whether the device exposes support
+ * for that feature.
+ *
+ * Some of these features will not expose failure to the client if used when unsupported, to prevent
+ * the client from needing to worry about handling different logic for each possible support
+ * configuration. The exception to this is features with important user-facing side effects, such as
+ * {@link APERF_HINT_AUTO_CPU} and {@link APERF_HINT_AUTO_GPU} modes which expect the client not to
+ * report durations while they are active.
+ */
+typedef enum APerformanceHintFeature : int32_t {
+ /**
+ * This value represents all APerformanceHintSession functionality. Using the Performance Hint
+ * API at all if this is not enabled will likely result in either
+ * {@link APerformanceHintManager} or {@link APerformanceHintSession} failing to create, or the
+ * session having little to no benefit even if creation succeeds.
+ */
+ APERF_HINT_SESSIONS,
+
+ /**
+ * This value represents the power efficiency mode, as exposed by
+ * {@link ASessionCreationConfig_setPreferPowerEfficiency} and
+ * {@link APerformanceHint_setPreferPowerEfficiency}.
+ */
+ APERF_HINT_POWER_EFFICIENCY,
+
+ /**
+ * This value the ability for sessions to bind to surfaces using
+ * {@link APerformanceHint_setNativeSurfaces} or
+ * {@link ASessionCreationConfig_setNativeSurfaces}
+ */
+ APERF_HINT_SURFACE_BINDING,
+
+ /**
+ * This value represents the "graphics pipeline" mode, as exposed by
+ * {@link ASessionCreationConfig_setGraphicsPipeline}.
+ */
+ APERF_HINT_GRAPHICS_PIPELINE,
+
+ /**
+ * This value represents the automatic CPU timing feature, as exposed by
+ * {@link ASessionCreationConfig_setUseAutoTiming}.
+ */
+ APERF_HINT_AUTO_CPU,
+
+ /**
+ * This value represents the automatic GPU timing feature, as exposed by
+ * {@link ASessionCreationConfig_setUseAutoTiming}.
+ */
+ APERF_HINT_AUTO_GPU,
+} APerformanceHintFeature;
+
+/**
+ * Checks whether the device exposes support for a specific feature.
+ *
+ * @param feature The specific feature enum to check.
+ *
+ * @return false if unsupported, true if supported.
+ */
+bool APerformanceHint_isFeatureSupported(APerformanceHintFeature feature) __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.
@@ -507,7 +608,6 @@
ASessionCreationConfig* _Nonnull ASessionCreationConfig_create()
__INTRODUCED_IN(36);
-
/**
* Destroys a {@link ASessionCreationConfig} and frees all
* resources associated with it.
@@ -526,11 +626,8 @@
* @param tids The list of tids to be associated with this session. They must be part of
* this process' thread group.
* @param size The size of the list of tids.
- *
- * @return 0 on success.
- * EINVAL if invalid array pointer or the value of size
*/
-int ASessionCreationConfig_setTids(
+void ASessionCreationConfig_setTids(
ASessionCreationConfig* _Nonnull config,
const pid_t* _Nonnull tids, size_t size) __INTRODUCED_IN(36);
@@ -539,15 +636,11 @@
*
* @param config The {@link ASessionCreationConfig}
* created by calling {@link ASessionCreationConfig_create()}.
- * @param targetWorkDurationNanos The parameter to specify a target duration
- * in nanoseconds for the new session; this value must be positive to use
- * the work duration API.
- *
- * @return 0 on success.
- * ENOTSUP if unsupported
- * EINVAL if invalid value
+ * @param targetWorkDurationNanos The parameter to specify a target duration in nanoseconds for the
+ * new session; this value must be positive to use the work duration API, and may be ignored
+ * otherwise or set to zero. Negative values are invalid.
*/
-int ASessionCreationConfig_setTargetWorkDurationNanos(
+void ASessionCreationConfig_setTargetWorkDurationNanos(
ASessionCreationConfig* _Nonnull config,
int64_t targetWorkDurationNanos) __INTRODUCED_IN(36);
@@ -559,12 +652,8 @@
* @param config The {@link ASessionCreationConfig}
* created by calling {@link ASessionCreationConfig_create()}.
* @param enabled Whether power efficiency mode will be enabled.
- *
- * @return 0 on success.
- * ENOTSUP if unsupported
- * EINVAL if invalid pointer to creation config
*/
-int ASessionCreationConfig_setPreferPowerEfficiency(
+void ASessionCreationConfig_setPreferPowerEfficiency(
ASessionCreationConfig* _Nonnull config, bool enabled) __INTRODUCED_IN(36);
/**
@@ -574,24 +663,31 @@
* buffer is fully finished drawing.
*
* It should include any threads on the critical path of that pipeline,
- * up to a limit accessible from {@link getMaxGraphicsPipelineThreadsCount()}.
+ * up to a limit accessible from {@link APerformanceHint_getMaxGraphicsPipelineThreadsCount()}.
*
* @param config The {@link ASessionCreationConfig}
* created by calling {@link ASessionCreationConfig_create()}.
* @param enabled Whether this session manages a graphics pipeline's critical path.
- *
- * @return 0 on success.
- * ENOTSUP if unsupported
- * EINVAL if invalid pointer to creation config or maximum threads for graphics
- pipeline is reached.
*/
-int ASessionCreationConfig_setGraphicsPipeline(
+void ASessionCreationConfig_setGraphicsPipeline(
ASessionCreationConfig* _Nonnull config, bool enabled) __INTRODUCED_IN(36);
/**
- * Associates a session with any {@link ASurfaceControl} or {@link ANativeWindow}
- * instances managed by this session. See {@link APerformanceHint_setNativeSurfaces}
- * for more details.
+ * Associates the created session with any {@link ASurfaceControl} or {@link ANativeWindow}
+ * instances it will be managing. Invalid or dead instances are ignored.
+ *
+ * This method is primarily intended for sessions that manage the timing of an entire
+ * graphics pipeline end-to-end for frame pacing, such as those using the
+ * {@link ASessionCreationConfig_setGraphicsPipeline} API. However, any session directly
+ * or indirectly managing a graphics pipeline should still associate themselves with
+ * directly relevant ASurfaceControl or ANativeWindow instances for better optimization.
+ * Additionally, if the surface associated with a session changes, this method should be called
+ * again to re-create the association.
+ *
+ * To see any benefit from this method, the client must make sure they are updating the frame rate
+ * of attached surfaces using methods such as {@link ANativeWindow_setFrameRate}, or by updating
+ * any associated ASurfaceControls with transactions that have {ASurfaceTransaction_setFrameRate}.
+ *
*
* @param config The {@link ASessionCreationConfig}
* created by calling {@link ASessionCreationConfig_create()}.
@@ -601,49 +697,46 @@
* @param surfaceControls A pointer to a list of ASurfaceControls associated with this session.
* nullptr can be passed to indicate there are no associated ASurfaceControls.
* @param surfaceControlsSize The number of ASurfaceControls in the list.
- *
- * @return 0 on success.
- * ENOTSUP if unsupported.
- * EINVAL if invalid or empty arguments passed.
*/
-int ASessionCreationConfig_setNativeSurfaces(
+void ASessionCreationConfig_setNativeSurfaces(
ASessionCreationConfig* _Nonnull config,
- ANativeWindow* _Nonnull* _Nullable nativeWindows, int nativeWindowsSize,
- ASurfaceControl* _Nonnull* _Nullable surfaceControls, int surfaceControlsSize)
+ ANativeWindow* _Nonnull* _Nullable nativeWindows, size_t nativeWindowsSize,
+ ASurfaceControl* _Nonnull* _Nullable surfaceControls, size_t surfaceControlsSize)
__INTRODUCED_IN(36);
/**
* Enable automatic timing mode for sessions using the GRAPHICS_PIPELINE API with an attached
- * surface. In this mode, sessions do not need to report actual durations and only need
- * to keep their thread list up-to-date, set a native surface, call
- * {@link ASessionCreationConfig_setGraphicsPipeline()} to signal that the session is in
- * "graphics pipeline" mode, and then set whether automatic timing is desired for the
- * CPU, GPU, or both, using this method.
+ * surface. In this mode, sessions do not need to report timing data for the CPU, GPU, or both
+ * depending on the configuration. To use this mode, sessions should set a native surface
+ * using {@ASessionCreationConfig_setNativeSurfaces}, enable graphics pipeline mode with
+ * {@link ASessionCreationConfig_setGraphicsPipeline()}, and then call this method to set whether
+ * automatic timing is desired for the CPU, GPU, or both. Trying to enable this without also
+ * enabling the graphics pipeline mode will cause session creation to fail.
*
* It is still be beneficial to set an accurate target time, as this may help determine
* timing information for some workloads where there is less information available from
* the framework, such as games. Additionally, reported CPU durations will be ignored
* while automatic CPU timing is enabled, and similarly GPU durations will be ignored
* when automatic GPU timing is enabled. When both are enabled, the entire
- * reportActualWorkDuration call will be ignored, and the session will be managed
- * completely automatically.
+ * {@link APerformanceHint_reportActualWorkDuration} call will be ignored, and the session will be
+ * managed completely automatically.
*
- * This mode will not work unless the client makes sure they are updating the framerate
- * of attached surfaces with methods such as {@link ANativeWindow_setFrameRate}, or updating
- * any associated ASurfaceControls with transactions that have {ASurfaceTransaction_setFrameRate}.
+ * If the client is manually controlling their frame rate for those surfaces, then they must make
+ * sure they are updating the frame rate with {@link ANativeWindow_setFrameRate}, or updating any
+ * associated ASurfaceControls with transactions that have {ASurfaceTransaction_setFrameRate} set.
+ *
+ * The user of this API should ensure this feature is supported by checking
+ * {@link APERF_HINT_AUTO_CPU} and {@link APERF_HINT_AUTO_GPU} with
+ * {@link APerformanceHint_isFeatureSupported} and falling back to manual timing if it is not.
+ * Trying to use automatic timing when it is unsupported will cause session creation to fail.
*
* @param config The {@link ASessionCreationConfig}
* created by calling {@link ASessionCreationConfig_create()}.
* @param cpu Whether to enable automatic timing for the CPU for this session.
* @param gpu Whether to enable automatic timing for the GPU for this session.
- *
- * @return 0 on success.
- * ENOTSUP if unsupported.
*/
-int ASessionCreationConfig_setUseAutoTiming(
- ASessionCreationConfig* _Nonnull config,
- bool cpu, bool gpu)
- __INTRODUCED_IN(36);
+void ASessionCreationConfig_setUseAutoTiming(
+ ASessionCreationConfig* _Nonnull config, bool cpu, bool gpu) __INTRODUCED_IN(36);
__END_DECLS
diff --git a/include/android/surface_control_input_receiver.h b/include/android/surface_control_input_receiver.h
index f0503f6..59d3a31 100644
--- a/include/android/surface_control_input_receiver.h
+++ b/include/android/surface_control_input_receiver.h
@@ -39,11 +39,11 @@
*
* \param motionEvent The motion event. This must be released with AInputEvent_release.
*
+ * \return true if the event is handled by the client, false otherwise.
* Available since API level 35.
*/
typedef bool (*AInputReceiver_onMotionEvent)(void *_Null_unspecified context,
- AInputEvent *_Nonnull motionEvent)
- __INTRODUCED_IN(__ANDROID_API_V__);
+ AInputEvent *_Nonnull motionEvent);
/**
* The AInputReceiver_onKeyEvent callback is invoked when the registered input channel receives
* a key event.
@@ -53,11 +53,12 @@
*
* \param keyEvent The key event. This must be released with AInputEvent_release.
*
+ * \return true if the event is handled by the client, false otherwise. System may generate
+ * a fallback key event if the event is not handled.
* Available since API level 35.
*/
typedef bool (*AInputReceiver_onKeyEvent)(void *_Null_unspecified context,
- AInputEvent *_Nonnull keyEvent)
- __INTRODUCED_IN(__ANDROID_API_V__);
+ AInputEvent *_Nonnull keyEvent);
typedef struct AInputReceiverCallbacks AInputReceiverCallbacks;
diff --git a/include/android/system_health.h b/include/android/system_health.h
index 69620df..bdb1413 100644
--- a/include/android/system_health.h
+++ b/include/android/system_health.h
@@ -16,6 +16,31 @@
/**
* @defgroup SystemHealth
+*
+ * SystemHealth provides access to data about how various system resources are used by applications.
+ *
+ * CPU/GPU headroom APIs are designed to be best used by applications with consistent and intense
+ * workload such as games to query the remaining capacity headroom over a short period and perform
+ * optimization accordingly. Due to the nature of the fast job scheduling and frequency scaling of
+ * CPU and GPU, the headroom by nature will have "TOCTOU" problem which makes it less suitable for
+ * apps with inconsistent or low workload to take any useful action but simply monitoring. And to
+ * avoid oscillation it's not recommended to adjust workload too frequent (on each polling request)
+ * or too aggressively. As the headroom calculation is more based on reflecting past history usage
+ * than predicting future capacity. Take game as an example, if the API returns CPU headroom of 0 in
+ * one scenario (especially if it's constant across multiple calls), or some value significantly
+ * smaller than other scenarios, then it can reason that the recent performance result is more CPU
+ * bottlenecked. Then reducing the CPU workload intensity can help reserve some headroom to handle
+ * the load variance better, which can result in less frame drops or smooth FPS value. On the other
+ * hand, if the API returns large CPU headroom constantly, the app can be more confident to increase
+ * the workload and expect higher possibility of device meeting its performance expectation.
+ * App can also use thermal APIs to read the current thermal status and headroom first, then poll
+ * the CPU and GPU headroom if the device is (about to) getting thermal throttled. If the CPU/GPU
+ * headrooms provide enough significance such as one valued at 0 while the other at 100, then it can
+ * be used to infer that reducing CPU workload could be more efficient to cool down the device.
+ * There is a caveat that the power controller may scale down the frequency of the CPU and GPU due
+ * to thermal and other reasons, which can result in a higher than usual percentage usage of the
+ * capacity.
+ *
* @{
*/
@@ -70,55 +95,40 @@
*/
typedef struct AGpuHeadroomParams AGpuHeadroomParams;
-/**
- * Creates a new instance of ACpuHeadroomParams.
- *
- * When the client finishes using {@link ACpuHeadroomParams},
- * {@link ACpuHeadroomParams_destroy()} must be called to destroy
- * and free up the resources associated with {@link ACpuHeadroomParams}.
- *
- * Available since API level 36.
- *
- * @return A new instance of ACpuHeadroomParams.
- */
-ACpuHeadroomParams *_Nonnull ACpuHeadroomParams_create()
-__INTRODUCED_IN(36);
-
-enum ACpuHeadroomCalculationType {
+typedef enum ACpuHeadroomCalculationType : int32_t {
/**
- * Use the minimum headroom value within the calculation window.
+ * The headroom calculation type bases on minimum value over a specified window.
* Introduced in API level 36.
*/
ACPU_HEADROOM_CALCULATION_TYPE_MIN = 0,
/**
- * Use the average headroom value within the calculation window.
+ * The headroom calculation type bases on average value over a specified window.
* Introduced in API level 36.
*/
ACPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1,
-};
-typedef enum ACpuHeadroomCalculationType ACpuHeadroomCalculationType;
+} ACpuHeadroomCalculationType;
-enum AGpuHeadroomCalculationType {
+typedef enum AGpuHeadroomCalculationType : int32_t {
/**
- * Use the minimum headroom value within the calculation window.
+ * The headroom calculation type bases on minimum value over a specified window.
* Introduced in API level 36.
*/
AGPU_HEADROOM_CALCULATION_TYPE_MIN = 0,
/**
- * Use the average headroom value within the calculation window.
+ * The headroom calculation type bases on average value over a specified window.
* Introduced in API level 36.
*/
AGPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1,
-};
-typedef enum AGpuHeadroomCalculationType AGpuHeadroomCalculationType;
+} AGpuHeadroomCalculationType;
/**
- * Sets the headroom calculation window size in ACpuHeadroomParams.
+ * Sets the CPU headroom calculation window size in milliseconds.
*
* Available since API level 36.
*
* @param params The params to be set.
- * @param windowMillis The window size in milliseconds ranged from [50, 10000]. The smaller the
+ * @param windowMillis The window size in milliseconds ranges from
+ * {@link ASystemHealth_getCpuHeadroomCalculationWindowRange}. The smaller the
* window size, the larger fluctuation in the headroom value should be expected.
* The default value can be retrieved from the
* {@link #ACpuHeadroomParams_getCalculationWindowMillis} if not set. The device
@@ -129,136 +139,210 @@
__INTRODUCED_IN(36);
/**
- * Gets the headroom calculation window size in ACpuHeadroomParams.
+ * Gets the CPU headroom calculation window size in milliseconds.
+ *
+ * This will return the default value chosen by the device if not set.
*
* Available since API level 36.
*
- * @param params The params to be set.
+ * @param params The params to read from.
* @return This will return the default value chosen by the device if the params is not set.
*/
-int ACpuHeadroomParams_getCalculationWindowMillis(ACpuHeadroomParams *_Nonnull params)
+int ACpuHeadroomParams_getCalculationWindowMillis(ACpuHeadroomParams* _Nonnull params)
__INTRODUCED_IN(36);
/**
- * Sets the headroom calculation window size in AGpuHeadroomParams.
+ * Sets the GPU headroom calculation window size in milliseconds.
*
* Available since API level 36.
*
* @param params The params to be set.
- * @param windowMillis The window size in milliseconds ranged from [50, 10000]. The smaller the
+ * @param windowMillis The window size in milliseconds ranges from
+ * {@link ASystemHealth_getGpuHeadroomCalculationWindowRange}. The smaller the
* window size, the larger fluctuation in the headroom value should be expected.
* The default value can be retrieved from the
* {@link #AGpuHeadroomParams_getCalculationWindowMillis} if not set. The device
* will try to use the closest feasible window size to this param.
*/
-void AGpuHeadroomParams_setCalculationWindowMillis(AGpuHeadroomParams *_Nonnull params,
+void AGpuHeadroomParams_setCalculationWindowMillis(AGpuHeadroomParams* _Nonnull params,
int windowMillis)
__INTRODUCED_IN(36);
/**
- * Gets the headroom calculation window size in AGpuHeadroomParams.
+ * Gets the GPU headroom calculation window size in milliseconds.
+ *
+ * This will return the default value chosen by the device if not set.
*
* Available since API level 36.
*
- * @param params The params to be set.
+ * @param params The params to read from.
* @return This will return the default value chosen by the device if the params is not set.
*/
-int AGpuHeadroomParams_getCalculationWindowMillis(AGpuHeadroomParams *_Nonnull params)
+int AGpuHeadroomParams_getCalculationWindowMillis(AGpuHeadroomParams* _Nonnull params)
__INTRODUCED_IN(36);
/**
- * Sets the headroom calculation type in ACpuHeadroomParams.
+ * Sets the CPU headroom calculation type in {@link ACpuHeadroomParams}.
*
* Available since API level 36.
*
* @param params The params to be set.
* @param calculationType The headroom calculation type.
*/
-void ACpuHeadroomParams_setCalculationType(ACpuHeadroomParams *_Nonnull params,
+void ACpuHeadroomParams_setCalculationType(ACpuHeadroomParams* _Nonnull params,
ACpuHeadroomCalculationType calculationType)
__INTRODUCED_IN(36);
/**
- * Gets the headroom calculation type in ACpuHeadroomParams.
+ * Gets the CPU headroom calculation type in {@link ACpuHeadroomParams}.
+ *
+ * This will return the default value chosen by the device if not set.
*
* Available since API level 36.
*
- * @param params The params to be set.
+ * @param params The params to read from.
* @return The headroom calculation type.
*/
ACpuHeadroomCalculationType
-ACpuHeadroomParams_getCalculationType(ACpuHeadroomParams *_Nonnull params)
+ACpuHeadroomParams_getCalculationType(ACpuHeadroomParams* _Nonnull params)
__INTRODUCED_IN(36);
/**
- * Sets the headroom calculation type in AGpuHeadroomParams.
+ * Sets the GPU headroom calculation type in {@link AGpuHeadroomParams}.
*
* Available since API level 36.
*
* @param params The params to be set.
* @param calculationType The headroom calculation type.
*/
-void AGpuHeadroomParams_setCalculationType(AGpuHeadroomParams *_Nonnull params,
+void AGpuHeadroomParams_setCalculationType(AGpuHeadroomParams* _Nonnull params,
AGpuHeadroomCalculationType calculationType)
__INTRODUCED_IN(36);
/**
- * Gets the headroom calculation type in AGpuHeadroomParams.
+ * Gets the GPU headroom calculation type in {@link AGpuHeadroomParams}.
+ *
+ * This will return the default value chosen by the device if not set.
*
* Available since API level 36.
*
- * @param params The params to be set.
+ * @param params The params to read from.
* @return The headroom calculation type.
*/
AGpuHeadroomCalculationType
-AGpuHeadroomParams_getCalculationType(AGpuHeadroomParams *_Nonnull params)
+AGpuHeadroomParams_getCalculationType(AGpuHeadroomParams* _Nonnull params)
__INTRODUCED_IN(36);
/**
- * Sets the thread TIDs to track in ACpuHeadroomParams.
+ * Sets the thread TIDs to track in {@link ACpuHeadroomParams}.
+ *
+ * The TIDs should belong to the same of the process that will make the headroom call. And they
+ * should not have different core affinity.
+ *
+ * If not set or set to empty, the headroom will be based on the PID of the process making the call.
*
* Available since API level 36.
*
* @param params The params to be set.
- * @param tids Non-null array of TIDs, maximum 5.
+ * @param tids Non-null array of TIDs, where maximum size can be read from
+ * {@link ASystemHealth_getMaxCpuHeadroomTidsSize}.
* @param tidsSize The size of the tids array.
*/
-void ACpuHeadroomParams_setTids(ACpuHeadroomParams *_Nonnull params, const int *_Nonnull tids,
- int tidsSize)
+void ACpuHeadroomParams_setTids(ACpuHeadroomParams* _Nonnull params, const int* _Nonnull tids,
+ size_t tidsSize)
__INTRODUCED_IN(36);
/**
- * Creates a new instance of AGpuHeadroomParams.
+ * Creates a new instance of {@link ACpuHeadroomParams}.
+ *
+ * When the client finishes using {@link ACpuHeadroomParams},
+ * {@link ACpuHeadroomParams_destroy} must be called to destroy
+ * and free up the resources associated with {@link ACpuHeadroomParams}.
+ *
+ * Available since API level 36.
+ *
+ * @return A new instance of {@link ACpuHeadroomParams}.
+ */
+ACpuHeadroomParams* _Nonnull ACpuHeadroomParams_create(void)
+__INTRODUCED_IN(36);
+
+/**
+ * Creates a new instance of {@link AGpuHeadroomParams}.
*
* When the client finishes using {@link AGpuHeadroomParams},
- * {@link AGpuHeadroomParams_destroy()} must be called to destroy
+ * {@link AGpuHeadroomParams_destroy} must be called to destroy
* and free up the resources associated with {@link AGpuHeadroomParams}.
*
* Available since API level 36.
*
- * @return A new instance of AGpuHeadroomParams.
+ * @return A new instance of {@link AGpuHeadroomParams}.
*/
-AGpuHeadroomParams *_Nonnull AGpuHeadroomParams_create()
+AGpuHeadroomParams* _Nonnull AGpuHeadroomParams_create(void)
__INTRODUCED_IN(36);
/**
- * Deletes the ACpuHeadroomParams instance.
+ * Deletes the {@link ACpuHeadroomParams} instance.
*
* Available since API level 36.
*
* @param params The params to be deleted.
*/
-void ACpuHeadroomParams_destroy(ACpuHeadroomParams *_Nonnull params)
+void ACpuHeadroomParams_destroy(ACpuHeadroomParams* _Nullable params)
__INTRODUCED_IN(36);
/**
- * Deletes the AGpuHeadroomParams instance.
+ * Deletes the {@link AGpuHeadroomParams} instance.
*
* Available since API level 36.
*
* @param params The params to be deleted.
*/
-void AGpuHeadroomParams_destroy(AGpuHeadroomParams *_Nonnull params)
+void AGpuHeadroomParams_destroy(AGpuHeadroomParams* _Nullable params)
+__INTRODUCED_IN(36);
+
+/**
+ * Gets the maximum number of TIDs this device supports for getting CPU headroom.
+ *
+ * See {@link ACpuHeadroomParams_setTids}.
+ *
+ * Available since API level 36.
+ *
+ * @param outSize Non-null output pointer to the max size.
+ * @return 0 on success.
+ * ENOTSUP if the CPU headroom API is unsupported.
+ */
+int ASystemHealth_getMaxCpuHeadroomTidsSize(size_t* _Nonnull outSize);
+
+/**
+ * Gets the range of the calculation window size for CPU headroom.
+ *
+ * In API version 36, the range will be a superset of [50, 10000].
+ *
+ * Available since API level 36.
+ *
+ * @param outMinMillis Non-null output pointer to be set to the minimum window size in milliseconds.
+ * @param outMaxMillis Non-null output pointer to be set to the maximum window size in milliseconds.
+ * @return 0 on success.
+ * ENOTSUP if API is unsupported.
+ */
+int ASystemHealth_getCpuHeadroomCalculationWindowRange(int32_t* _Nonnull outMinMillis,
+ int32_t* _Nonnull outMaxMillis)
+__INTRODUCED_IN(36);
+
+/**
+ * Gets the range of the calculation window size for GPU headroom.
+ *
+ * In API version 36, the range will be a superset of [50, 10000].
+ *
+ * Available since API level 36.
+ *
+ * @param outMinMillis Non-null output pointer to be set to the minimum window size in milliseconds.
+ * @param outMaxMillis Non-null output pointer to be set to the maximum window size in milliseconds.
+ * @return 0 on success.
+ * ENOTSUP if API is unsupported.
+ */
+int ASystemHealth_getGpuHeadroomCalculationWindowRange(int32_t* _Nonnull outMinMillis,
+ int32_t* _Nonnull outMaxMillis)
__INTRODUCED_IN(36);
/**
@@ -269,21 +353,27 @@
* be used with the thermal status and headroom to determine if reducing the CPU bound workload can
* help reduce the device temperature to avoid thermal throttling.
*
+ * If the params are valid, each call will perform at least one synchronous binder transaction that
+ * can take more than 1ms. So it's not recommended to call or wait for this on critical threads.
+ * Some devices may implement this as an on-demand API with lazy initialization, so the caller
+ * should expect higher latency when making the first call (especially with non-default params)
+ * since app starts or after changing params, as the device may need to change its data collection.
+ *
* Available since API level 36.
*
- * @param params The params to customize the CPU headroom calculation, or nullptr to use the default
+ * @param params The params to customize the CPU headroom calculation, or nullptr to use default.
* @param outHeadroom Non-null output pointer to a single float, which will be set to the CPU
* headroom value. The value will be a single value or `Float.NaN` if it's
- * temporarily unavailable.
+ * temporarily unavailable due to server error or not enough user CPU workload.
* Each valid value ranges from [0, 100], where 0 indicates no more cpu resources
* can be granted.
- * @return 0 on success
+ * @return 0 on success.
* EPIPE if failed to get the CPU headroom.
* EPERM if the TIDs do not belong to the same process.
* ENOTSUP if API or requested params is unsupported.
*/
-int ASystemHealth_getCpuHeadroom(const ACpuHeadroomParams *_Nullable params,
- float *_Nonnull outHeadroom)
+int ASystemHealth_getCpuHeadroom(const ACpuHeadroomParams* _Nullable params,
+ float* _Nonnull outHeadroom)
__INTRODUCED_IN(36);
/**
@@ -294,52 +384,58 @@
* be used with the thermal status and headroom to determine if reducing the GPU bound workload can
* help reduce the device temperature to avoid thermal throttling.
*
+ * If the params are valid, each call will perform at least one synchronous binder transaction that
+ * can take more than 1ms. So it's not recommended to call or wait for this on critical threads.
+ * Some devices may implement this as an on-demand API with lazy initialization, so the caller
+ * should expect higher latency when making the first call (especially with non-default params)
+ * since app starts or after changing params, as the device may need to change its data collection.
+ *
* Available since API level 36
*
- * @param params The params to customize the GPU headroom calculation, or nullptr to use the default
+ * @param params The params to customize the GPU headroom calculation, or nullptr to use default
* @param outHeadroom Non-null output pointer to a single float, which will be set to the GPU
* headroom value. The value will be a single value or `Float.NaN` if it's
* temporarily unavailable.
* Each valid value ranges from [0, 100], where 0 indicates no more gpu resources
* can be granted.
- * @return 0 on success
+ * @return 0 on success.
* EPIPE if failed to get the GPU headroom.
* ENOTSUP if API or requested params is unsupported.
*/
-int ASystemHealth_getGpuHeadroom(const AGpuHeadroomParams *_Nullable params,
- float *_Nonnull outHeadroom)
+int ASystemHealth_getGpuHeadroom(const AGpuHeadroomParams* _Nullable params,
+ float* _Nonnull outHeadroom)
__INTRODUCED_IN(36);
/**
* Gets minimum polling interval for calling {@link ASystemHealth_getCpuHeadroom} in milliseconds.
*
- * The getCpuHeadroom API may return cached result if called more frequently than the interval.
+ * The {@link ASystemHealth_getCpuHeadroom} API may return cached result if called more frequently
+ * than the interval.
*
* Available since API level 36.
*
* @param outMinIntervalMillis Non-null output pointer to a int64_t, which
* will be set to the minimum polling interval in milliseconds.
- * @return 0 on success
- * EPIPE if failed to get the minimum polling interval.
+ * @return 0 on success.
* ENOTSUP if API is unsupported.
*/
-int ASystemHealth_getCpuHeadroomMinIntervalMillis(int64_t *_Nonnull outMinIntervalMillis)
+int ASystemHealth_getCpuHeadroomMinIntervalMillis(int64_t* _Nonnull outMinIntervalMillis)
__INTRODUCED_IN(36);
/**
* Gets minimum polling interval for calling {@link ASystemHealth_getGpuHeadroom} in milliseconds.
*
- * The getGpuHeadroom API may return cached result if called more frequent than the interval.
+ * The {@link ASystemHealth_getGpuHeadroom} API may return cached result if called more frequently
+ * than the interval.
*
* Available since API level 36.
*
* @param outMinIntervalMillis Non-null output pointer to a int64_t, which
* will be set to the minimum polling interval in milliseconds.
- * @return 0 on success
- * EPIPE if failed to get the minimum polling interval.
+ * @return 0 on success.
* ENOTSUP if API is unsupported.
*/
-int ASystemHealth_getGpuHeadroomMinIntervalMillis(int64_t *_Nonnull outMinIntervalMillis)
+int ASystemHealth_getGpuHeadroomMinIntervalMillis(int64_t* _Nonnull outMinIntervalMillis)
__INTRODUCED_IN(36);
#ifdef __cplusplus
diff --git a/include/audiomanager/IAudioManager.h b/include/audiomanager/IAudioManager.h
index a35a145..b0641b8 100644
--- a/include/audiomanager/IAudioManager.h
+++ b/include/audiomanager/IAudioManager.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_IAUDIOMANAGER_H
#define ANDROID_IAUDIOMANAGER_H
+#include <android/media/IAudioManagerNative.h>
#include <audiomanager/AudioManager.h>
#include <utils/Errors.h>
#include <binder/IInterface.h>
@@ -34,20 +35,23 @@
// These transaction IDs must be kept in sync with the method order from
// IAudioService.aidl.
enum {
- TRACK_PLAYER = IBinder::FIRST_CALL_TRANSACTION,
- PLAYER_ATTRIBUTES = IBinder::FIRST_CALL_TRANSACTION + 1,
- PLAYER_EVENT = IBinder::FIRST_CALL_TRANSACTION + 2,
- RELEASE_PLAYER = IBinder::FIRST_CALL_TRANSACTION + 3,
- TRACK_RECORDER = IBinder::FIRST_CALL_TRANSACTION + 4,
- RECORDER_EVENT = IBinder::FIRST_CALL_TRANSACTION + 5,
- RELEASE_RECORDER = IBinder::FIRST_CALL_TRANSACTION + 6,
- PLAYER_SESSION_ID = IBinder::FIRST_CALL_TRANSACTION + 7,
- PORT_EVENT = IBinder::FIRST_CALL_TRANSACTION + 8,
- PERMISSION_UPDATE_BARRIER = IBinder::FIRST_CALL_TRANSACTION + 9,
+ GET_NATIVE_INTERFACE = IBinder::FIRST_CALL_TRANSACTION,
+ TRACK_PLAYER = IBinder::FIRST_CALL_TRANSACTION + 1,
+ PLAYER_ATTRIBUTES = IBinder::FIRST_CALL_TRANSACTION + 2,
+ PLAYER_EVENT = IBinder::FIRST_CALL_TRANSACTION + 3,
+ RELEASE_PLAYER = IBinder::FIRST_CALL_TRANSACTION + 4,
+ TRACK_RECORDER = IBinder::FIRST_CALL_TRANSACTION + 5,
+ RECORDER_EVENT = IBinder::FIRST_CALL_TRANSACTION + 6,
+ RELEASE_RECORDER = IBinder::FIRST_CALL_TRANSACTION + 7,
+ PLAYER_SESSION_ID = IBinder::FIRST_CALL_TRANSACTION + 8,
+ PORT_EVENT = IBinder::FIRST_CALL_TRANSACTION + 9,
+ PERMISSION_UPDATE_BARRIER = IBinder::FIRST_CALL_TRANSACTION + 10,
};
DECLARE_META_INTERFACE(AudioManager)
+ virtual sp<media::IAudioManagerNative> getNativeInterface() = 0;
+
// The parcels created by these methods must be kept in sync with the
// corresponding methods from IAudioService.aidl and objects it imports.
virtual audio_unique_id_t trackPlayer(player_type_t playerType, audio_usage_t usage,
diff --git a/include/ftl/finalizer.h b/include/ftl/finalizer.h
new file mode 100644
index 0000000..0251957
--- /dev/null
+++ b/include/ftl/finalizer.h
@@ -0,0 +1,211 @@
+/*
+ * 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 <cstddef>
+
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+#include <ftl/function.h>
+
+namespace android::ftl {
+
+// An RAII wrapper that invokes a function object as a finalizer when destroyed.
+//
+// The function object must take no arguments, and must return void. If the function object needs
+// any context for the call, it must store it itself, for example with a lambda capture.
+//
+// The stored function object will be called once (unless canceled via the `cancel()` member
+// function) at the first of:
+//
+// - The Finalizer instance is destroyed.
+// - `operator()` is used to invoke the contained function.
+// - The Finalizer instance is move-assigned a new value. The function being replaced will be
+// invoked, and the replacement will be stored to be called later.
+//
+// The intent with this class is to keep cleanup code next to the code that requires that
+// cleanup be performed.
+//
+// bool read_file(std::string filename) {
+// FILE* f = fopen(filename.c_str(), "rb");
+// if (f == nullptr) return false;
+// const auto cleanup = ftl::Finalizer([f]() { fclose(f); });
+// // fread(...), etc
+// return true;
+// }
+//
+// The `FinalFunction` template argument to Finalizer<FinalFunction> allows a polymorphic function
+// type for storing the finalization function, such as `std::function` or `ftl::Function`.
+//
+// For convenience, this header defines a few useful aliases for using those types.
+//
+// - `FinalizerStd`, an alias for `Finalizer<std::function<void()>>`
+// - `FinalizerFtl`, an alias for `Finalizer<ftl::Function<void()>>`
+// - `FinalizerFtl1`, an alias for `Finalizer<ftl::Function<void(), 1>>`
+// - `FinalizerFtl2`, an alias for `Finalizer<ftl::Function<void(), 2>>`
+// - `FinalizerFtl3`, an alias for `Finalizer<ftl::Function<void(), 3>>`
+//
+// Clients of this header are free to define other aliases they need.
+//
+// A Finalizer that uses a polymorphic function type can be returned from a function call and/or
+// stored as member data (to be destroyed along with the containing class).
+//
+// auto register(Observer* observer) -> ftl::FinalizerStd<void()> {
+// const auto id = observers.add(observer);
+// return ftl::Finalizer([id]() { observers.remove(id); });
+// }
+//
+// {
+// const auto _ = register(observer);
+// // do the things that required the registered observer.
+// }
+// // the observer is removed.
+//
+// Cautions:
+//
+// 1. When a Finalizer is stored as member data, you will almost certainly want that cleanup to
+// happen first, before the rest of the other member data is destroyed. For safety you should
+// assume that the finalization function will access that data directly or indirectly.
+//
+// This means that Finalizers should be defined last, after all other normal member data in a
+// class.
+//
+// class MyClass {
+// public:
+// bool initialize() {
+// ready_ = true;
+// cleanup_ = ftl::Finalizer([this]() { ready_ = false; });
+// return true;
+// }
+//
+// bool ready_ = false;
+//
+// // Finalizers should be last so other class members can be accessed before being
+// // destroyed.
+// ftl::FinalizerStd<void()> cleanup_;
+// };
+//
+// 2. Care must be taken to use `ftl::Finalizer()` when constructing locally from a lambda. If you
+// forget to do so, you are just creating a lambda that won't be automatically invoked!
+//
+// const auto bad = [&counter](){ ++counter; }; // Just a lambda instance
+// const auto good = ftl::Finalizer([&counter](){ ++counter; });
+//
+template <typename FinalFunction>
+class Finalizer final {
+ // requires(std::is_invocable_r_v<void, FinalFunction>)
+ static_assert(std::is_invocable_r_v<void, FinalFunction>);
+
+ public:
+ // A default constructed Finalizer does nothing when destroyed.
+ // requires(std::is_default_constructible_v<FinalFunction>)
+ constexpr Finalizer() = default;
+
+ // Constructs a Finalizer from a function object.
+ // requires(std::is_invocable_v<F>)
+ template <typename F, typename = std::enable_if_t<std::is_invocable_v<F>>>
+ [[nodiscard]] explicit constexpr Finalizer(F&& function)
+ : Finalizer(std::forward<F>(function), false) {}
+
+ constexpr ~Finalizer() { maybe_invoke(); }
+
+ // Disallow copying.
+ Finalizer(const Finalizer& that) = delete;
+ auto operator=(const Finalizer& that) = delete;
+
+ // Move construction
+ // requires(std::is_move_constructible_v<FinalFunction>)
+ [[nodiscard]] constexpr Finalizer(Finalizer&& that)
+ : Finalizer(std::move(that.function_), std::exchange(that.canceled_, true)) {}
+
+ // Implicit conversion move construction
+ // requires(!std::is_same_v<Finalizer, Finalizer<F>>)
+ template <typename F, typename = std::enable_if_t<!std::is_same_v<Finalizer, Finalizer<F>>>>
+ // NOLINTNEXTLINE(google-explicit-constructor, cppcoreguidelines-rvalue-reference-param-not-moved)
+ [[nodiscard]] constexpr Finalizer(Finalizer<F>&& that)
+ : Finalizer(std::move(that.function_), std::exchange(that.canceled_, true)) {}
+
+ // Move assignment
+ // requires(std::is_move_assignable_v<FinalFunction>)
+ constexpr auto operator=(Finalizer&& that) -> Finalizer& {
+ maybe_invoke();
+
+ function_ = std::move(that.function_);
+ canceled_ = std::exchange(that.canceled_, true);
+
+ return *this;
+ }
+
+ // Implicit conversion move assignment
+ // requires(!std::is_same_v<Finalizer, Finalizer<F>>)
+ template <typename F, typename = std::enable_if_t<!std::is_same_v<Finalizer, Finalizer<F>>>>
+ // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
+ constexpr auto operator=(Finalizer<F>&& that) -> Finalizer& {
+ *this = Finalizer(std::move(that.function_), std::exchange(that.canceled_, true));
+ return *this;
+ }
+
+ // Cancels the final function, preventing it from being invoked.
+ constexpr void cancel() {
+ canceled_ = true;
+ maybe_nullify_function();
+ }
+
+ // Invokes the final function now, if not already invoked.
+ constexpr void operator()() { maybe_invoke(); }
+
+ private:
+ template <typename>
+ friend class Finalizer;
+
+ template <typename F, typename = std::enable_if_t<std::is_invocable_v<F>>>
+ [[nodiscard]] explicit constexpr Finalizer(F&& function, bool canceled)
+ : function_(std::forward<F>(function)), canceled_(canceled) {}
+
+ constexpr void maybe_invoke() {
+ if (!std::exchange(canceled_, true)) {
+ std::invoke(function_);
+ maybe_nullify_function();
+ }
+ }
+
+ constexpr void maybe_nullify_function() {
+ // Sets function_ to nullptr if that is supported for the backing type.
+ if constexpr (std::is_assignable_v<FinalFunction, nullptr_t>) {
+ function_ = nullptr;
+ }
+ }
+
+ FinalFunction function_;
+ bool canceled_ = true;
+};
+
+template <typename F>
+Finalizer(F&&) -> Finalizer<std::decay_t<F>>;
+
+// A standard alias for using `std::function` as the polymorphic function type.
+using FinalizerStd = Finalizer<std::function<void()>>;
+
+// Helpful aliases for using `ftl::Function` as the polymorphic function type.
+using FinalizerFtl = Finalizer<Function<void()>>;
+using FinalizerFtl1 = Finalizer<Function<void(), 1>>;
+using FinalizerFtl2 = Finalizer<Function<void(), 2>>;
+using FinalizerFtl3 = Finalizer<Function<void(), 3>>;
+
+} // namespace android::ftl
\ No newline at end of file
diff --git a/include/input/AccelerationCurve.h b/include/input/AccelerationCurve.h
index 0cf648a..8a4a5d4 100644
--- a/include/input/AccelerationCurve.h
+++ b/include/input/AccelerationCurve.h
@@ -46,4 +46,15 @@
std::vector<AccelerationCurveSegment> createAccelerationCurveForPointerSensitivity(
int32_t sensitivity);
+/*
+ * Creates a flat acceleration curve for disabling pointer acceleration.
+ *
+ * This method generates a single AccelerationCurveSegment with specific values
+ * to effectively disable acceleration for both mice and touchpads.
+ * A flat acceleration curve ensures a constant gain, meaning that the output
+ * velocity is directly proportional to the input velocity, resulting in
+ * a 1:1 movement ratio between the input device and the on-screen pointer.
+ */
+std::vector<AccelerationCurveSegment> createFlatAccelerationCurve(int32_t sensitivity);
+
} // namespace android
diff --git a/include/private/OWNERS b/include/private/OWNERS
index 37da96d..db3ae48 100644
--- a/include/private/OWNERS
+++ b/include/private/OWNERS
@@ -1,3 +1,4 @@
# ADPF
per-file thermal_private.h = file:platform/frameworks/base:/ADPF_OWNERS
per-file performance_hint_private.h = file:platform/frameworks/base:/ADPF_OWNERS
+per-file system_health_private.h = file:platform/frameworks/base:/ADPF_OWNERS
diff --git a/include/private/performance_hint_private.h b/include/private/performance_hint_private.h
index e3f98ba..a468313 100644
--- a/include/private/performance_hint_private.h
+++ b/include/private/performance_hint_private.h
@@ -125,8 +125,10 @@
/**
* Creates a session using ASessionCreationConfig
*/
-APerformanceHintSession* APerformanceHint_createSessionUsingConfigInternal(
- APerformanceHintManager* manager, ASessionCreationConfig* sessionCreationConfig,
+int APerformanceHint_createSessionUsingConfigInternal(
+ APerformanceHintManager* manager,
+ ASessionCreationConfig* config,
+ APerformanceHintSession** sessionOut,
SessionTag tag);
/**
diff --git a/include/private/system_health_private.h b/include/private/system_health_private.h
new file mode 100644
index 0000000..05a5a06
--- /dev/null
+++ b/include/private/system_health_private.h
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_PRIVATE_NATIVE_SYSTEM_HEALTH_H
+#define ANDROID_PRIVATE_NATIVE_SYSTEM_HEALTH_H
+
+#include <stdint.h>
+
+__BEGIN_DECLS
+
+/**
+ * For testing only.
+ */
+void ASystemHealth_setIHintManagerForTesting(void* iManager);
+
+__END_DECLS
+
+#endif // ANDROID_PRIVATE_NATIVE_SYSTEM_HEALTH_H
+
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index aac369d..fb00d4f 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -269,6 +269,7 @@
"-Wzero-as-null-pointer-constant",
"-Wreorder-init-list",
"-Wunused-const-variable",
+ "-Wunused-result",
"-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
"-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
// Hide symbols by default and set the BUILDING_LIBBINDER macro so that
diff --git a/libs/binder/BackendUnifiedServiceManager.cpp b/libs/binder/BackendUnifiedServiceManager.cpp
index 34d5a09..7c0319a 100644
--- a/libs/binder/BackendUnifiedServiceManager.cpp
+++ b/libs/binder/BackendUnifiedServiceManager.cpp
@@ -15,6 +15,7 @@
*/
#include "BackendUnifiedServiceManager.h"
+#include <android-base/strings.h>
#include <android/os/IAccessor.h>
#include <android/os/IServiceManager.h>
#include <binder/RpcSession.h>
@@ -47,6 +48,9 @@
using android::os::IAccessor;
using binder::Status;
+static const char* kUnsupportedOpNoServiceManager =
+ "Unsupported operation without a kernel binder servicemanager process";
+
static const char* kStaticCachableList[] = {
// go/keep-sorted start
"accessibility",
@@ -211,7 +215,9 @@
sp<IBinder>* _aidl_return) {
os::Service service;
Status status = getService2(name, &service);
- *_aidl_return = service.get<os::Service::Tag::serviceWithMetadata>().service;
+ if (status.isOk()) {
+ *_aidl_return = service.get<os::Service::Tag::serviceWithMetadata>().service;
+ }
return status;
}
@@ -220,7 +226,10 @@
return Status::ok();
}
os::Service service;
- Status status = mTheRealServiceManager->getService2(name, &service);
+ Status status = Status::ok();
+ if (mTheRealServiceManager) {
+ status = mTheRealServiceManager->getService2(name, &service);
+ }
if (status.isOk()) {
status = toBinderService(name, service, _out);
@@ -231,13 +240,26 @@
return status;
}
-Status BackendUnifiedServiceManager::checkService(const ::std::string& name, os::Service* _out) {
+Status BackendUnifiedServiceManager::checkService(const ::std::string& name,
+ sp<IBinder>* _aidl_return) {
+ os::Service service;
+ Status status = checkService2(name, &service);
+ if (status.isOk()) {
+ *_aidl_return = service.get<os::Service::Tag::serviceWithMetadata>().service;
+ }
+ return status;
+}
+
+Status BackendUnifiedServiceManager::checkService2(const ::std::string& name, os::Service* _out) {
os::Service service;
if (returnIfCached(name, _out)) {
return Status::ok();
}
- Status status = mTheRealServiceManager->checkService(name, &service);
+ Status status = Status::ok();
+ if (mTheRealServiceManager) {
+ status = mTheRealServiceManager->checkService2(name, &service);
+ }
if (status.isOk()) {
status = toBinderService(name, service, _out);
if (status.isOk()) {
@@ -315,66 +337,156 @@
Status BackendUnifiedServiceManager::addService(const ::std::string& name,
const sp<IBinder>& service, bool allowIsolated,
int32_t dumpPriority) {
- Status status = mTheRealServiceManager->addService(name, service, allowIsolated, dumpPriority);
- // mEnableAddServiceCache is true by default.
- if (kUseCacheInAddService && mEnableAddServiceCache && status.isOk()) {
- return updateCache(name, service,
- dumpPriority & android::os::IServiceManager::FLAG_IS_LAZY_SERVICE);
+ if (mTheRealServiceManager) {
+ Status status =
+ mTheRealServiceManager->addService(name, service, allowIsolated, dumpPriority);
+ // mEnableAddServiceCache is true by default.
+ if (kUseCacheInAddService && mEnableAddServiceCache && status.isOk()) {
+ return updateCache(name, service,
+ dumpPriority & android::os::IServiceManager::FLAG_IS_LAZY_SERVICE);
+ }
+ return status;
}
- return status;
+ return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION,
+ kUnsupportedOpNoServiceManager);
}
Status BackendUnifiedServiceManager::listServices(int32_t dumpPriority,
::std::vector<::std::string>* _aidl_return) {
- return mTheRealServiceManager->listServices(dumpPriority, _aidl_return);
+ Status status = Status::ok();
+ if (mTheRealServiceManager) {
+ status = mTheRealServiceManager->listServices(dumpPriority, _aidl_return);
+ }
+ if (!status.isOk()) return status;
+
+ appendInjectedAccessorServices(_aidl_return);
+
+ return status;
}
Status BackendUnifiedServiceManager::registerForNotifications(
const ::std::string& name, const sp<os::IServiceCallback>& callback) {
- return mTheRealServiceManager->registerForNotifications(name, callback);
+ if (mTheRealServiceManager) {
+ return mTheRealServiceManager->registerForNotifications(name, callback);
+ }
+ return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION,
+ kUnsupportedOpNoServiceManager);
}
Status BackendUnifiedServiceManager::unregisterForNotifications(
const ::std::string& name, const sp<os::IServiceCallback>& callback) {
- return mTheRealServiceManager->unregisterForNotifications(name, callback);
+ if (mTheRealServiceManager) {
+ return mTheRealServiceManager->unregisterForNotifications(name, callback);
+ }
+ return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION,
+ kUnsupportedOpNoServiceManager);
}
Status BackendUnifiedServiceManager::isDeclared(const ::std::string& name, bool* _aidl_return) {
- return mTheRealServiceManager->isDeclared(name, _aidl_return);
+ Status status = Status::ok();
+ if (mTheRealServiceManager) {
+ status = mTheRealServiceManager->isDeclared(name, _aidl_return);
+ }
+ if (!status.isOk()) return status;
+
+ if (!*_aidl_return) {
+ forEachInjectedAccessorService([&](const std::string& instance) {
+ if (name == instance) {
+ *_aidl_return = true;
+ }
+ });
+ }
+
+ return status;
}
Status BackendUnifiedServiceManager::getDeclaredInstances(
const ::std::string& iface, ::std::vector<::std::string>* _aidl_return) {
- return mTheRealServiceManager->getDeclaredInstances(iface, _aidl_return);
+ Status status = Status::ok();
+ if (mTheRealServiceManager) {
+ status = mTheRealServiceManager->getDeclaredInstances(iface, _aidl_return);
+ }
+ if (!status.isOk()) return status;
+
+ forEachInjectedAccessorService([&](const std::string& instance) {
+ // Declared instances have the format
+ // <interface>/instance like foo.bar.ISomething/instance
+ // If it does not have that format, consider the instance to be ""
+ std::string_view name(instance);
+ if (base::ConsumePrefix(&name, iface + "/")) {
+ _aidl_return->emplace_back(name);
+ } else if (iface == instance) {
+ _aidl_return->push_back("");
+ }
+ });
+
+ return status;
}
Status BackendUnifiedServiceManager::updatableViaApex(
const ::std::string& name, ::std::optional<::std::string>* _aidl_return) {
- return mTheRealServiceManager->updatableViaApex(name, _aidl_return);
+ if (mTheRealServiceManager) {
+ return mTheRealServiceManager->updatableViaApex(name, _aidl_return);
+ }
+ return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION,
+ kUnsupportedOpNoServiceManager);
}
Status BackendUnifiedServiceManager::getUpdatableNames(const ::std::string& apexName,
::std::vector<::std::string>* _aidl_return) {
- return mTheRealServiceManager->getUpdatableNames(apexName, _aidl_return);
+ if (mTheRealServiceManager) {
+ return mTheRealServiceManager->getUpdatableNames(apexName, _aidl_return);
+ }
+ return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION,
+ kUnsupportedOpNoServiceManager);
}
Status BackendUnifiedServiceManager::getConnectionInfo(
const ::std::string& name, ::std::optional<os::ConnectionInfo>* _aidl_return) {
- return mTheRealServiceManager->getConnectionInfo(name, _aidl_return);
+ if (mTheRealServiceManager) {
+ return mTheRealServiceManager->getConnectionInfo(name, _aidl_return);
+ }
+ return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION,
+ kUnsupportedOpNoServiceManager);
}
Status BackendUnifiedServiceManager::registerClientCallback(
const ::std::string& name, const sp<IBinder>& service,
const sp<os::IClientCallback>& callback) {
- return mTheRealServiceManager->registerClientCallback(name, service, callback);
+ if (mTheRealServiceManager) {
+ return mTheRealServiceManager->registerClientCallback(name, service, callback);
+ }
+ return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION,
+ kUnsupportedOpNoServiceManager);
}
Status BackendUnifiedServiceManager::tryUnregisterService(const ::std::string& name,
const sp<IBinder>& service) {
- return mTheRealServiceManager->tryUnregisterService(name, service);
+ if (mTheRealServiceManager) {
+ return mTheRealServiceManager->tryUnregisterService(name, service);
+ }
+ return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION,
+ kUnsupportedOpNoServiceManager);
}
Status BackendUnifiedServiceManager::getServiceDebugInfo(
::std::vector<os::ServiceDebugInfo>* _aidl_return) {
- return mTheRealServiceManager->getServiceDebugInfo(_aidl_return);
+ if (mTheRealServiceManager) {
+ return mTheRealServiceManager->getServiceDebugInfo(_aidl_return);
+ }
+ return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION,
+ kUnsupportedOpNoServiceManager);
}
[[clang::no_destroy]] static std::once_flag gUSmOnce;
[[clang::no_destroy]] static sp<BackendUnifiedServiceManager> gUnifiedServiceManager;
+static bool hasOutOfProcessServiceManager() {
+#ifndef BINDER_WITH_KERNEL_IPC
+ return false;
+#else
+#if defined(__BIONIC__) && !defined(__ANDROID_VNDK__)
+ return android::base::GetBoolProperty("servicemanager.installed", true);
+#else
+ return true;
+#endif
+#endif // BINDER_WITH_KERNEL_IPC
+}
+
sp<BackendUnifiedServiceManager> getBackendUnifiedServiceManager() {
std::call_once(gUSmOnce, []() {
#if defined(__BIONIC__) && !defined(__ANDROID_VNDK__)
- /* wait for service manager */ {
+ /* wait for service manager */
+ if (hasOutOfProcessServiceManager()) {
using std::literals::chrono_literals::operator""s;
using android::base::WaitForProperty;
while (!WaitForProperty("servicemanager.ready", "true", 1s)) {
@@ -384,7 +496,7 @@
#endif
sp<AidlServiceManager> sm = nullptr;
- while (sm == nullptr) {
+ while (hasOutOfProcessServiceManager() && sm == nullptr) {
sm = interface_cast<AidlServiceManager>(
ProcessState::self()->getContextObject(nullptr));
if (sm == nullptr) {
diff --git a/libs/binder/BackendUnifiedServiceManager.h b/libs/binder/BackendUnifiedServiceManager.h
index 6a0d06a..c14f280 100644
--- a/libs/binder/BackendUnifiedServiceManager.h
+++ b/libs/binder/BackendUnifiedServiceManager.h
@@ -122,7 +122,8 @@
binder::Status getService(const ::std::string& name, sp<IBinder>* _aidl_return) override;
binder::Status getService2(const ::std::string& name, os::Service* out) override;
- binder::Status checkService(const ::std::string& name, os::Service* out) override;
+ binder::Status checkService(const ::std::string& name, sp<IBinder>* _aidl_return) override;
+ binder::Status checkService2(const ::std::string& name, os::Service* out) override;
binder::Status addService(const ::std::string& name, const sp<IBinder>& service,
bool allowIsolated, int32_t dumpPriority) override;
binder::Status listServices(int32_t dumpPriority,
@@ -167,5 +168,9 @@
sp<BackendUnifiedServiceManager> getBackendUnifiedServiceManager();
android::binder::Status getInjectedAccessor(const std::string& name, android::os::Service* service);
+void appendInjectedAccessorServices(std::vector<std::string>* list);
+// Do not call any other service manager APIs that might take the accessor
+// mutex because this will be holding it!
+void forEachInjectedAccessorService(const std::function<void(const std::string&)>& f);
} // namespace android
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index 53bd08d..0a22588 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -288,7 +288,7 @@
// for below objects
RpcMutex mLock;
std::set<sp<RpcServerLink>> mRpcServerLinks;
- BpBinder::ObjectManager mObjects;
+ BpBinder::ObjectManager mObjectMgr;
unique_fd mRecordingFd;
};
@@ -468,7 +468,7 @@
LOG_ALWAYS_FATAL_IF(!e, "no memory");
RpcMutexUniqueLock _l(e->mLock);
- return e->mObjects.attach(objectID, object, cleanupCookie, func);
+ return e->mObjectMgr.attach(objectID, object, cleanupCookie, func);
}
void* BBinder::findObject(const void* objectID) const
@@ -477,7 +477,7 @@
if (!e) return nullptr;
RpcMutexUniqueLock _l(e->mLock);
- return e->mObjects.find(objectID);
+ return e->mObjectMgr.find(objectID);
}
void* BBinder::detachObject(const void* objectID) {
@@ -485,7 +485,7 @@
if (!e) return nullptr;
RpcMutexUniqueLock _l(e->mLock);
- return e->mObjects.detach(objectID);
+ return e->mObjectMgr.detach(objectID);
}
void BBinder::withLock(const std::function<void()>& doWithLock) {
@@ -501,7 +501,7 @@
Extras* e = getOrCreateExtras();
LOG_ALWAYS_FATAL_IF(!e, "no memory");
RpcMutexUniqueLock _l(e->mLock);
- return e->mObjects.lookupOrCreateWeak(objectID, make, makeArgs);
+ return e->mObjectMgr.lookupOrCreateWeak(objectID, make, makeArgs);
}
BBinder* BBinder::localBinder()
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 3758b65..444f061 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -78,7 +78,16 @@
BpBinder::ObjectManager::~ObjectManager()
{
- kill();
+ const size_t N = mObjects.size();
+ ALOGV("Killing %zu objects in manager %p", N, this);
+ for (auto i : mObjects) {
+ const entry_t& e = i.second;
+ if (e.func != nullptr) {
+ e.func(i.first, e.object, e.cleanupCookie);
+ }
+ }
+
+ mObjects.clear();
}
void* BpBinder::ObjectManager::attach(const void* objectID, void* object, void* cleanupCookie,
@@ -144,20 +153,6 @@
return newObj;
}
-void BpBinder::ObjectManager::kill()
-{
- const size_t N = mObjects.size();
- ALOGV("Killing %zu objects in manager %p", N, this);
- for (auto i : mObjects) {
- const entry_t& e = i.second;
- if (e.func != nullptr) {
- e.func(i.first, e.object, e.cleanupCookie);
- }
- }
-
- mObjects.clear();
-}
-
// ---------------------------------------------------------------------------
sp<BpBinder> BpBinder::create(int32_t handle, std::function<void()>* postTask) {
@@ -697,19 +692,19 @@
void* BpBinder::attachObject(const void* objectID, void* object, void* cleanupCookie,
object_cleanup_func func) {
RpcMutexUniqueLock _l(mLock);
- ALOGV("Attaching object %p to binder %p (manager=%p)", object, this, &mObjects);
- return mObjects.attach(objectID, object, cleanupCookie, func);
+ ALOGV("Attaching object %p to binder %p (manager=%p)", object, this, &mObjectMgr);
+ return mObjectMgr.attach(objectID, object, cleanupCookie, func);
}
void* BpBinder::findObject(const void* objectID) const
{
RpcMutexUniqueLock _l(mLock);
- return mObjects.find(objectID);
+ return mObjectMgr.find(objectID);
}
void* BpBinder::detachObject(const void* objectID) {
RpcMutexUniqueLock _l(mLock);
- return mObjects.detach(objectID);
+ return mObjectMgr.detach(objectID);
}
void BpBinder::withLock(const std::function<void()>& doWithLock) {
@@ -720,7 +715,7 @@
sp<IBinder> BpBinder::lookupOrCreateWeak(const void* objectID, object_make_func make,
const void* makeArgs) {
RpcMutexUniqueLock _l(mLock);
- return mObjects.lookupOrCreateWeak(objectID, make, makeArgs);
+ return mObjectMgr.lookupOrCreateWeak(objectID, make, makeArgs);
}
BpBinder* BpBinder::remoteBinder()
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 623e7b9..f191b97 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -626,12 +626,22 @@
{
if (mProcess->mDriverFD < 0)
return;
- talkWithDriver(false);
+
+ if (status_t res = talkWithDriver(false); res != OK) {
+ // TODO: we may want to abort for some of these cases
+ ALOGW("1st call to talkWithDriver returned error in flushCommands: %s",
+ statusToString(res).c_str());
+ }
+
// The flush could have caused post-write refcount decrements to have
// been executed, which in turn could result in BC_RELEASE/BC_DECREFS
// being queued in mOut. So flush again, if we need to.
if (mOut.dataSize() > 0) {
- talkWithDriver(false);
+ if (status_t res = talkWithDriver(false); res != OK) {
+ // TODO: we may want to abort for some of these cases
+ ALOGW("2nd call to talkWithDriver returned error in flushCommands: %s",
+ statusToString(res).c_str());
+ }
}
if (mOut.dataSize() > 0) {
ALOGW("mOut.dataSize() > 0 after flushCommands()");
@@ -803,7 +813,11 @@
mOut.writeInt32(BC_EXIT_LOOPER);
mIsLooper = false;
- talkWithDriver(false);
+ if (status_t res = talkWithDriver(false); res != OK) {
+ // TODO: we may want to abort for some of these cases
+ ALOGW("call to talkWithDriver in joinThreadPool returned error: %s, FD: %d",
+ statusToString(res).c_str(), mProcess->mDriverFD);
+ }
size_t oldCount = mProcess->mCurrentThreads.fetch_sub(1);
LOG_ALWAYS_FATAL_IF(oldCount == 0,
"Threadpool thread count underflowed. Thread cannot exist and exit in "
@@ -839,12 +853,8 @@
void IPCThreadState::stopProcess(bool /*immediate*/)
{
- //ALOGI("**** STOPPING PROCESS");
- flushCommands();
- int fd = mProcess->mDriverFD;
- mProcess->mDriverFD = -1;
- close(fd);
- //kill(getpid(), SIGKILL);
+ ALOGI("IPCThreadState::stopProcess() (deprecated) called. Exiting process.");
+ exit(0);
}
status_t IPCThreadState::transact(int32_t handle,
@@ -1494,7 +1504,14 @@
buffer.setDataSize(0);
constexpr uint32_t kForwardReplyFlags = TF_CLEAR_BUF;
- sendReply(reply, (tr.flags & kForwardReplyFlags));
+
+ // TODO: we may want to abort if there is an error here, or return as 'error'
+ // from this function, but the impact needs to be measured
+ status_t error2 = sendReply(reply, (tr.flags & kForwardReplyFlags));
+ if (error2 != OK) {
+ ALOGE("error in sendReply for synchronous call: %s",
+ statusToString(error2).c_str());
+ }
} else {
if (error != OK) {
std::ostringstream logStream;
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 53435c3..719e445 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -304,6 +304,25 @@
return android::binder::Status::ok();
}
+void appendInjectedAccessorServices(std::vector<std::string>* list) {
+ LOG_ALWAYS_FATAL_IF(list == nullptr,
+ "Attempted to get list of services from Accessors with nullptr");
+ std::lock_guard<std::mutex> lock(gAccessorProvidersMutex);
+ for (const auto& entry : gAccessorProviders) {
+ list->insert(list->end(), entry.mProvider->instances().begin(),
+ entry.mProvider->instances().end());
+ }
+}
+
+void forEachInjectedAccessorService(const std::function<void(const std::string&)>& f) {
+ std::lock_guard<std::mutex> lock(gAccessorProvidersMutex);
+ for (const auto& entry : gAccessorProviders) {
+ for (const auto& instance : entry.mProvider->instances()) {
+ f(instance);
+ }
+ }
+}
+
sp<IServiceManager> defaultServiceManager()
{
std::call_once(gSmOnce, []() {
@@ -605,7 +624,7 @@
sp<IBinder> CppBackendShim::checkService(const String16& name) const {
Service ret;
- if (!mUnifiedServiceManager->checkService(String8(name).c_str(), &ret).isOk()) {
+ if (!mUnifiedServiceManager->checkService2(String8(name).c_str(), &ret).isOk()) {
return nullptr;
}
return ret.get<Service::Tag::serviceWithMetadata>().service;
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 0b7cd81..c0ebee0 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -1293,10 +1293,6 @@
status_t Parcel::writeUniqueFileDescriptorVector(const std::optional<std::vector<unique_fd>>& val) {
return writeData(val);
}
-status_t Parcel::writeUniqueFileDescriptorVector(
- const std::unique_ptr<std::vector<unique_fd>>& val) {
- return writeData(val);
-}
status_t Parcel::writeStrongBinderVector(const std::vector<sp<IBinder>>& val) { return writeData(val); }
status_t Parcel::writeStrongBinderVector(const std::optional<std::vector<sp<IBinder>>>& val) { return writeData(val); }
@@ -1352,10 +1348,6 @@
status_t Parcel::readUniqueFileDescriptorVector(std::optional<std::vector<unique_fd>>* val) const {
return readData(val);
}
-status_t Parcel::readUniqueFileDescriptorVector(
- std::unique_ptr<std::vector<unique_fd>>* val) const {
- return readData(val);
-}
status_t Parcel::readUniqueFileDescriptorVector(std::vector<unique_fd>* val) const {
return readData(val);
}
@@ -3397,14 +3389,6 @@
}
#ifdef BINDER_WITH_KERNEL_IPC
-size_t Parcel::getBlobAshmemSize() const
-{
- // This used to return the size of all blobs that were written to ashmem, now we're returning
- // the ashmem currently referenced by this Parcel, which should be equivalent.
- // TODO(b/202029388): Remove method once ABI can be changed.
- return getOpenAshmemSize();
-}
-
size_t Parcel::getOpenAshmemSize() const
{
auto* kernelFields = maybeKernelFields();
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index 9e5e79f..4332f8a 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -37,6 +37,9 @@
"name": "binderStabilityTest"
},
{
+ "name": "binderStabilityIntegrationTest"
+ },
+ {
"name": "binderRpcWireProtocolTest"
},
{
diff --git a/libs/binder/aidl/android/os/IServiceManager.aidl b/libs/binder/aidl/android/os/IServiceManager.aidl
index 69edef8..6539238 100644
--- a/libs/binder/aidl/android/os/IServiceManager.aidl
+++ b/libs/binder/aidl/android/os/IServiceManager.aidl
@@ -83,11 +83,20 @@
/**
* Retrieve an existing service called @a name from the service
+ * manager. Non-blocking. Returns null if the service does not exist.
+ *
+ * @deprecated TODO(b/355394904): Use checkService2 instead. This does not
+ * return metadata that is included in ServiceWithMetadata
+ */
+ @UnsupportedAppUsage
+ @nullable IBinder checkService(@utf8InCpp String name);
+
+ /**
+ * Retrieve an existing service called @a name from the service
* manager. Non-blocking. Returns null if the service does not
* exist.
*/
- @UnsupportedAppUsage
- Service checkService(@utf8InCpp String name);
+ Service checkService2(@utf8InCpp String name);
/**
* Place a new @a service called @a name into the service
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index 7518044..935bd8d 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -104,6 +104,7 @@
// Stop the current recording.
LIBBINDER_EXPORTED status_t stopRecordingBinder();
+ // Note: This class is not thread safe so protect uses of it when necessary
class ObjectManager {
public:
ObjectManager();
@@ -116,8 +117,6 @@
sp<IBinder> lookupOrCreateWeak(const void* objectID, IBinder::object_make_func make,
const void* makeArgs);
- void kill();
-
private:
ObjectManager(const ObjectManager&);
ObjectManager& operator=(const ObjectManager&);
@@ -224,7 +223,7 @@
volatile int32_t mObitsSent;
Vector<Obituary>* mObituaries;
std::unique_ptr<FrozenStateChange> mFrozen;
- ObjectManager mObjects;
+ ObjectManager mObjectMgr;
mutable String16 mDescriptorCache;
int32_t mTrackedUid;
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index cdee17c..bb45ad2 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -243,7 +243,6 @@
"android.media.IMediaHTTPService",
"android.media.IMediaLogService",
"android.media.IMediaMetadataRetriever",
- "android.media.IMediaMetricsService",
"android.media.IMediaPlayer",
"android.media.IMediaPlayerClient",
"android.media.IMediaPlayerService",
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 9ef4e69..f7465e2 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -64,7 +64,10 @@
* Returns the PID of the process which has made the current binder
* call. If not in a binder call, this will return getpid.
*
- * Warning: oneway transactions do not receive PID. Even if you expect
+ * Warning do not use this as a security identifier! PID is unreliable
+ * as it may be re-used. This should mostly be used for debugging.
+ *
+ * oneway transactions do not receive PID. Even if you expect
* a transaction to be synchronous, a misbehaving client could send it
* as an asynchronous call and result in a 0 PID here. Additionally, if
* there is a race and the calling process dies, the PID may still be
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index 7d79baa..d248f22 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -80,6 +80,14 @@
/**
* Register a service.
+ *
+ * Note:
+ * This status_t return value may be an exception code from an underlying
+ * Status type that doesn't have a representive error code in
+ * utils/Errors.h.
+ * One example of this is a return value of -7
+ * (Status::Exception::EX_UNSUPPORTED_OPERATION) when the service manager
+ * process is not installed on the device when addService is called.
*/
// NOLINTNEXTLINE(google-default-arguments)
virtual status_t addService(const String16& name, const sp<IBinder>& service,
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index b4efa0a..6c4c6fe 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -383,11 +383,13 @@
LIBBINDER_EXPORTED status_t
writeUniqueFileDescriptorVector(const std::optional<std::vector<binder::unique_fd>>& val);
LIBBINDER_EXPORTED status_t
- writeUniqueFileDescriptorVector(const std::unique_ptr<std::vector<binder::unique_fd>>& val)
- __attribute__((deprecated("use std::optional version instead")));
- LIBBINDER_EXPORTED status_t
writeUniqueFileDescriptorVector(const std::vector<binder::unique_fd>& val);
+ // WARNING: deprecated and incompatible with AIDL. You should use Parcelable
+ // definitions outside of Parcel to represent shared memory, such as
+ // IMemory or with ParcelFileDescriptor. We should remove this, or move it to be
+ // external to Parcel, it's not a very encapsulated API.
+ //
// Writes a blob to the parcel.
// If the blob is small, then it is stored in-place, otherwise it is
// transferred by way of an anonymous shared memory region. Prefer sending
@@ -401,8 +403,6 @@
// as long as it keeps a dup of the blob file descriptor handy for later.
LIBBINDER_EXPORTED status_t writeDupImmutableBlobFileDescriptor(int fd);
- LIBBINDER_EXPORTED status_t writeObject(const flat_binder_object& val, bool nullMetaData);
-
// Like Parcel.java's writeNoException(). Just writes a zero int32.
// Currently the native implementation doesn't do any of the StrictMode
// stack gathering and serialization that the Java implementation does.
@@ -627,11 +627,13 @@
LIBBINDER_EXPORTED status_t
readUniqueFileDescriptorVector(std::optional<std::vector<binder::unique_fd>>* val) const;
LIBBINDER_EXPORTED status_t
- readUniqueFileDescriptorVector(std::unique_ptr<std::vector<binder::unique_fd>>* val) const
- __attribute__((deprecated("use std::optional version instead")));
- LIBBINDER_EXPORTED status_t
readUniqueFileDescriptorVector(std::vector<binder::unique_fd>* val) const;
+ // WARNING: deprecated and incompatible with AIDL. You should use Parcelable
+ // definitions outside of Parcel to represent shared memory, such as
+ // IMemory or with ParcelFileDescriptor. We should remove this, or move it to be
+ // external to Parcel, it's not a very encapsulated API.
+ //
// Reads a blob from the parcel.
// The caller should call release() on the blob after reading its contents.
LIBBINDER_EXPORTED status_t readBlob(size_t len, ReadableBlob* outBlob) const;
@@ -685,6 +687,7 @@
// Set the capacity to `desired`, truncating the Parcel if necessary.
status_t continueWrite(size_t desired);
status_t truncateRpcObjects(size_t newObjectsSize);
+ status_t writeObject(const flat_binder_object& val, bool nullMetaData);
status_t writePointer(uintptr_t val);
status_t readPointer(uintptr_t *pArg) const;
uintptr_t readPointer() const;
@@ -1485,14 +1488,15 @@
* Note: for historical reasons, this does not include ashmem memory which
* is referenced by this Parcel, but which this parcel doesn't own (e.g.
* writeFileDescriptor is called without 'takeOwnership' true).
+ *
+ * WARNING: you should not use this, but rather, unparcel, and inspect
+ * each FD independently. This counts ashmem size, but there may be
+ * other resources used for non-ashmem FDs, such as other types of
+ * shared memory, files, etc..
*/
LIBBINDER_EXPORTED size_t getOpenAshmemSize() const;
private:
- // TODO(b/202029388): Remove 'getBlobAshmemSize' once no prebuilts reference
- // this
- LIBBINDER_EXPORTED size_t getBlobAshmemSize() const;
-
// Needed so that we can save object metadata to the disk
friend class android::binder::debug::RecordedTransaction;
};
diff --git a/libs/binder/include/binder/SafeInterface.h b/libs/binder/include/binder/SafeInterface.h
index 0b4f196..bcbd14f 100644
--- a/libs/binder/include/binder/SafeInterface.h
+++ b/libs/binder/include/binder/SafeInterface.h
@@ -34,6 +34,13 @@
namespace android {
namespace SafeInterface {
+/**
+ * WARNING: Prefer to use AIDL-generated interfaces. Using SafeInterface to generate interfaces
+ * does not support tracing, and many other AIDL features out of the box. The general direction
+ * we should go is to migrate safe interface users to AIDL and then remove this so that there
+ * is only one thing to learn/use/test/integrate, not this as well.
+ */
+
// ParcelHandler is responsible for writing/reading various types to/from a Parcel in a generic way
class LIBBINDER_EXPORTED ParcelHandler {
public:
diff --git a/libs/binder/include/binder/Stability.h b/libs/binder/include/binder/Stability.h
index cafb8aa..bfe0a5a 100644
--- a/libs/binder/include/binder/Stability.h
+++ b/libs/binder/include/binder/Stability.h
@@ -20,6 +20,8 @@
#include <binder/IBinder.h>
#include <string>
+class BinderStabilityIntegrationTest_ExpectedStabilityForItsPartition_Test;
+
namespace android {
class BpBinder;
@@ -127,6 +129,8 @@
// through Parcel)
friend ::android::ProcessState;
+ friend ::BinderStabilityIntegrationTest_ExpectedStabilityForItsPartition_Test;
+
static void tryMarkCompilationUnit(IBinder* binder);
// Currently, we use int16_t for Level so that it can fit in BBinder.
@@ -156,11 +160,11 @@
uint32_t flags);
// get stability information as encoded on the wire
- static int16_t getRepr(IBinder* binder);
+ LIBBINDER_EXPORTED static int16_t getRepr(IBinder* binder);
// whether a transaction on binder is allowed, if the transaction
// is done from a context with a specific stability level
- static bool check(int16_t provided, Level required);
+ LIBBINDER_EXPORTED static bool check(int16_t provided, Level required);
static bool isDeclaredLevel(int32_t level);
static std::string levelString(int32_t level);
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index bd46c47..d69d318 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -419,6 +419,9 @@
* This can be used with higher-level system services to determine the caller's identity and check
* permissions.
*
+ * Warning do not use this as a security identifier! PID is unreliable as it may be re-used. This
+ * should mostly be used for debugging.
+ *
* Available since API level 29.
*
* \return calling uid or the current process's UID if this thread isn't processing a transaction.
diff --git a/libs/binder/ndk/tests/binderVendorDoubleLoadTest.cpp b/libs/binder/ndk/tests/binderVendorDoubleLoadTest.cpp
index 66be94f..fb92e05 100644
--- a/libs/binder/ndk/tests/binderVendorDoubleLoadTest.cpp
+++ b/libs/binder/ndk/tests/binderVendorDoubleLoadTest.cpp
@@ -30,6 +30,8 @@
#include <gtest/gtest.h>
#include <sys/prctl.h>
+static_assert(FLAG_PRIVATE_LOCAL != 0, "Build system configuration breaks stability");
+
using namespace android;
using ::android::binder::Status;
using ::android::internal::Stability;
diff --git a/libs/binder/rust/src/state.rs b/libs/binder/rust/src/state.rs
index 8a06274..c0cac83 100644
--- a/libs/binder/rust/src/state.rs
+++ b/libs/binder/rust/src/state.rs
@@ -101,7 +101,10 @@
/// dies and is replaced with another process with elevated permissions and
/// the same PID.
///
- /// Warning: oneway transactions do not receive PID. Even if you expect
+ /// Warning: do not use this as a security identifier! PID is unreliable
+ /// as it may be re-used. This should mostly be used for debugging.
+ ///
+ /// oneway transactions do not receive PID. Even if you expect
/// a transaction to be synchronous, a misbehaving client could send it
/// as a synchronous call and result in a 0 PID here. Additionally, if
/// there is a race and the calling process dies, the PID may still be
diff --git a/libs/binder/rust/src/system_only.rs b/libs/binder/rust/src/system_only.rs
index 1a58d6b..50aa336 100644
--- a/libs/binder/rust/src/system_only.rs
+++ b/libs/binder/rust/src/system_only.rs
@@ -23,22 +23,17 @@
use std::os::raw::c_char;
use libc::{sockaddr, sockaddr_un, sockaddr_vm, socklen_t};
-use std::sync::Arc;
-use std::{fmt, mem, ptr};
+use std::boxed::Box;
+use std::{mem, ptr};
/// Rust wrapper around ABinderRpc_Accessor objects for RPC binder service management.
///
/// Dropping the `Accessor` will drop the underlying object and the binder it owns.
+#[derive(Debug)]
pub struct Accessor {
accessor: *mut sys::ABinderRpc_Accessor,
}
-impl fmt::Debug for Accessor {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "ABinderRpc_Accessor({:p})", self.accessor)
- }
-}
-
/// Socket connection info required for libbinder to connect to a service.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConnectionInfo {
@@ -70,7 +65,7 @@
where
F: Fn(&str) -> Option<ConnectionInfo> + Send + Sync + 'static,
{
- let callback: *mut c_void = Arc::into_raw(Arc::new(callback)) as *mut c_void;
+ let callback: *mut c_void = Box::into_raw(Box::new(callback)) as *mut c_void;
let inst = CString::new(instance).unwrap();
// Safety: The function pointer is a valid connection_info callback.
@@ -154,7 +149,7 @@
/// the string within isize::MAX from the pointer. The memory must not be mutated for
/// the duration of this function call and must be valid for reads from the pointer
/// to the null terminator.
- /// - The `cookie` parameter must be the cookie for an `Arc<F>` and
+ /// - The `cookie` parameter must be the cookie for a `Box<F>` and
/// the caller must hold a ref-count to it.
unsafe extern "C" fn connection_info<F>(
instance: *const c_char,
@@ -167,7 +162,7 @@
log::error!("Cookie({cookie:p}) or instance({instance:p}) is null!");
return ptr::null_mut();
}
- // Safety: The caller promises that `cookie` is for an Arc<F>.
+ // Safety: The caller promises that `cookie` is for a Box<F>.
let callback = unsafe { (cookie as *const F).as_ref().unwrap() };
// Safety: The caller in libbinder_ndk will have already verified this is a valid
@@ -212,19 +207,19 @@
}
}
- /// Callback that decrements the ref-count.
+ /// Callback that drops the `Box<F>`.
/// This is invoked from C++ when a binder is unlinked.
///
/// # Safety
///
- /// - The `cookie` parameter must be the cookie for an `Arc<F>` and
+ /// - The `cookie` parameter must be the cookie for a `Box<F>` and
/// the owner must give up a ref-count to it.
unsafe extern "C" fn cookie_decr_refcount<F>(cookie: *mut c_void)
where
F: Fn(&str) -> Option<ConnectionInfo> + Send + Sync + 'static,
{
- // Safety: The caller promises that `cookie` is for an Arc<F>.
- unsafe { Arc::decrement_strong_count(cookie as *const F) };
+ // Safety: The caller promises that `cookie` is for a Box<F>.
+ unsafe { std::mem::drop(Box::from_raw(cookie as *mut F)) };
}
}
@@ -301,7 +296,7 @@
where
F: Fn(&str) -> Option<Accessor> + Send + Sync + 'static,
{
- let callback: *mut c_void = Arc::into_raw(Arc::new(provider)) as *mut c_void;
+ let callback: *mut c_void = Box::into_raw(Box::new(provider)) as *mut c_void;
let c_str_instances: Vec<CString> =
instances.iter().map(|s| CString::new(s.as_bytes()).unwrap()).collect();
let mut c_instances: Vec<*const c_char> =
@@ -351,7 +346,7 @@
log::error!("Cookie({cookie:p}) or instance({instance:p}) is null!");
return ptr::null_mut();
}
- // Safety: The caller promises that `cookie` is for an Arc<F>.
+ // Safety: The caller promises that `cookie` is for a Box<F>.
let callback = unsafe { (cookie as *const F).as_ref().unwrap() };
let inst = {
@@ -382,14 +377,14 @@
///
/// # Safety
///
- /// - The `cookie` parameter must be the cookie for an `Arc<F>` and
+ /// - The `cookie` parameter must be the cookie for a `Box<F>` and
/// the owner must give up a ref-count to it.
unsafe extern "C" fn accessor_cookie_decr_refcount<F>(cookie: *mut c_void)
where
F: Fn(&str) -> Option<Accessor> + Send + Sync + 'static,
{
- // Safety: The caller promises that `cookie` is for an Arc<F>.
- unsafe { Arc::decrement_strong_count(cookie as *const F) };
+ // Safety: The caller promises that `cookie` is for a Box<F>.
+ unsafe { std::mem::drop(Box::from_raw(cookie as *mut F)) };
}
}
diff --git a/libs/binder/servicedispatcher.cpp b/libs/binder/servicedispatcher.cpp
index be99065..78fe2a8 100644
--- a/libs/binder/servicedispatcher.cpp
+++ b/libs/binder/servicedispatcher.cpp
@@ -127,7 +127,12 @@
// We can't send BpBinder for regular binder over RPC.
return android::binder::Status::fromStatusT(android::INVALID_OPERATION);
}
- android::binder::Status checkService(const std::string&, android::os::Service*) override {
+ android::binder::Status checkService(const std::string&,
+ android::sp<android::IBinder>*) override {
+ // We can't send BpBinder for regular binder over RPC.
+ return android::binder::Status::fromStatusT(android::INVALID_OPERATION);
+ }
+ android::binder::Status checkService2(const std::string&, android::os::Service*) override {
// We can't send BpBinder for regular binder over RPC.
return android::binder::Status::fromStatusT(android::INVALID_OPERATION);
}
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index c21d7c6..f412dfb 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -803,6 +803,28 @@
}
cc_test {
+ name: "binderStabilityIntegrationTest",
+ defaults: ["binder_test_defaults"],
+ srcs: [
+ "binderStabilityIntegrationTest.cpp",
+ ],
+
+ shared_libs: [
+ "libbinder",
+ "libutils",
+ ],
+ static_libs: [
+ "libprocpartition",
+ ],
+
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+ require_root: true,
+}
+
+cc_test {
name: "binderAllocationLimits",
defaults: ["binder_test_defaults"],
srcs: ["binderAllocationLimits.cpp"],
diff --git a/libs/binder/tests/binderCacheUnitTest.cpp b/libs/binder/tests/binderCacheUnitTest.cpp
index 19395c2..121e5ae 100644
--- a/libs/binder/tests/binderCacheUnitTest.cpp
+++ b/libs/binder/tests/binderCacheUnitTest.cpp
@@ -74,7 +74,7 @@
public:
MockAidlServiceManager() : innerSm() {}
- binder::Status checkService(const ::std::string& name, os::Service* _out) override {
+ binder::Status checkService2(const ::std::string& name, os::Service* _out) override {
os::ServiceWithMetadata serviceWithMetadata = os::ServiceWithMetadata();
serviceWithMetadata.service = innerSm.getService(String16(name.c_str()));
serviceWithMetadata.isLazyService = false;
@@ -98,7 +98,7 @@
public:
MockAidlServiceManager2() : innerSm() {}
- binder::Status checkService(const ::std::string& name, os::Service* _out) override {
+ binder::Status checkService2(const ::std::string& name, os::Service* _out) override {
os::ServiceWithMetadata serviceWithMetadata = os::ServiceWithMetadata();
serviceWithMetadata.service = innerSm.getService(String16(name.c_str()));
serviceWithMetadata.isLazyService = true;
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index c038c95..891c0a2 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -44,6 +44,7 @@
#include <processgroup/processgroup.h>
#include <utils/Flattenable.h>
#include <utils/SystemClock.h>
+#include "binder/IServiceManagerUnitTestHelper.h"
#include <linux/sched.h>
#include <sys/epoll.h>
@@ -585,14 +586,14 @@
EXPECT_EQ(NO_ERROR, sm->addService(String16("binderLibTest-manager"), binder));
}
+class LocalRegistrationCallbackImpl : public virtual IServiceManager::LocalRegistrationCallback {
+ void onServiceRegistration(const String16&, const sp<IBinder>&) override {}
+ virtual ~LocalRegistrationCallbackImpl() {}
+};
+
TEST_F(BinderLibTest, RegisterForNotificationsFailure) {
auto sm = defaultServiceManager();
- using LocalRegistrationCallback = IServiceManager::LocalRegistrationCallback;
- class LocalRegistrationCallbackImpl : public virtual LocalRegistrationCallback {
- void onServiceRegistration(const String16&, const sp<IBinder>&) override {}
- virtual ~LocalRegistrationCallbackImpl() {}
- };
- sp<LocalRegistrationCallback> cb = sp<LocalRegistrationCallbackImpl>::make();
+ sp<IServiceManager::LocalRegistrationCallback> cb = sp<LocalRegistrationCallbackImpl>::make();
EXPECT_EQ(BAD_VALUE, sm->registerForNotifications(String16("ValidName"), nullptr));
EXPECT_EQ(UNKNOWN_ERROR, sm->registerForNotifications(String16("InvalidName!$"), cb));
@@ -600,12 +601,7 @@
TEST_F(BinderLibTest, UnregisterForNotificationsFailure) {
auto sm = defaultServiceManager();
- using LocalRegistrationCallback = IServiceManager::LocalRegistrationCallback;
- class LocalRegistrationCallbackImpl : public virtual LocalRegistrationCallback {
- void onServiceRegistration(const String16&, const sp<IBinder>&) override {}
- virtual ~LocalRegistrationCallbackImpl() {}
- };
- sp<LocalRegistrationCallback> cb = sp<LocalRegistrationCallbackImpl>::make();
+ sp<IServiceManager::LocalRegistrationCallback> cb = sp<LocalRegistrationCallbackImpl>::make();
EXPECT_EQ(OK, sm->registerForNotifications(String16("ValidName"), cb));
@@ -1788,6 +1784,43 @@
EXPECT_EQ(sm->unregisterForNotifications(String16("RogerRafa"), cb), OK);
}
+// Make sure all IServiceManager APIs will function without an AIDL service
+// manager registered on the device.
+TEST(ServiceManagerNoAidlServer, SanityCheck) {
+ String16 kServiceName("no_services_exist");
+ // This is what clients will see when there is no servicemanager process
+ // that registers itself as context object 0.
+ // Can't use setDefaultServiceManager() here because these test cases run in
+ // the same process and will abort when called twice or before/after
+ // defaultServiceManager().
+ sp<IServiceManager> sm = getServiceManagerShimFromAidlServiceManagerForTests(nullptr);
+ auto status = sm->addService(kServiceName, sp<BBinder>::make());
+ // CppBackendShim returns Status::exceptionCode as the status_t
+ EXPECT_EQ(status, Status::Exception::EX_UNSUPPORTED_OPERATION) << statusToString(status);
+ auto service = sm->checkService(String16("no_services_exist"));
+ EXPECT_TRUE(service == nullptr);
+ auto list = sm->listServices(android::IServiceManager::DUMP_FLAG_PRIORITY_ALL);
+ EXPECT_TRUE(list.isEmpty());
+ bool declared = sm->isDeclared(kServiceName);
+ EXPECT_FALSE(declared);
+ list = sm->getDeclaredInstances(kServiceName);
+ EXPECT_TRUE(list.isEmpty());
+ auto updatable = sm->updatableViaApex(kServiceName);
+ EXPECT_EQ(updatable, std::nullopt);
+ list = sm->getUpdatableNames(kServiceName);
+ EXPECT_TRUE(list.isEmpty());
+ auto conInfo = sm->getConnectionInfo(kServiceName);
+ EXPECT_EQ(conInfo, std::nullopt);
+ auto cb = sp<LocalRegistrationCallbackImpl>::make();
+ status = sm->registerForNotifications(kServiceName, cb);
+ EXPECT_EQ(status, UNKNOWN_ERROR) << statusToString(status);
+ status = sm->unregisterForNotifications(kServiceName, cb);
+ EXPECT_EQ(status, BAD_VALUE) << statusToString(status);
+ auto dbgInfos = sm->getServiceDebugInfo();
+ EXPECT_TRUE(dbgInfos.empty());
+ sm->enableAddServiceCache(true);
+}
+
TEST_F(BinderLibTest, ThreadPoolAvailableThreads) {
Parcel data, reply;
sp<IBinder> server = addServer();
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index da5a8e3..9f656ec 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -1328,6 +1328,109 @@
EXPECT_EQ(status, OK);
}
+class BinderRpcAccessorNoConnection : public ::testing::Test {};
+
+TEST_F(BinderRpcAccessorNoConnection, listServices) {
+ const String16 kInstanceName("super.cool.service/better_than_default");
+ const String16 kInstanceName2("super.cool.service/better_than_default2");
+
+ auto receipt =
+ addAccessorProvider({String8(kInstanceName).c_str(), String8(kInstanceName2).c_str()},
+ [&](const String16&) -> sp<IBinder> { return nullptr; });
+ EXPECT_FALSE(receipt.expired());
+ Vector<String16> list =
+ defaultServiceManager()->listServices(IServiceManager::DUMP_FLAG_PRIORITY_ALL);
+ bool name1 = false;
+ bool name2 = false;
+ for (auto name : list) {
+ if (name == kInstanceName) name1 = true;
+ if (name == kInstanceName2) name2 = true;
+ }
+ EXPECT_TRUE(name1);
+ EXPECT_TRUE(name2);
+ status_t status = removeAccessorProvider(receipt);
+ EXPECT_EQ(status, OK);
+}
+
+TEST_F(BinderRpcAccessorNoConnection, isDeclared) {
+ const String16 kInstanceName("super.cool.service/default");
+ const String16 kInstanceName2("still_counts_as_declared");
+
+ auto receipt =
+ addAccessorProvider({String8(kInstanceName).c_str(), String8(kInstanceName2).c_str()},
+ [&](const String16&) -> sp<IBinder> { return nullptr; });
+ EXPECT_FALSE(receipt.expired());
+ EXPECT_TRUE(defaultServiceManager()->isDeclared(kInstanceName));
+ EXPECT_TRUE(defaultServiceManager()->isDeclared(kInstanceName2));
+ EXPECT_FALSE(defaultServiceManager()->isDeclared(String16("doesnt_exist")));
+ status_t status = removeAccessorProvider(receipt);
+ EXPECT_EQ(status, OK);
+}
+
+TEST_F(BinderRpcAccessorNoConnection, getDeclaredInstances) {
+ const String16 kInstanceName("super.cool.service.IFoo/default");
+ const String16 kInstanceName2("super.cool.service.IFoo/extra/default");
+ const String16 kInstanceName3("super.cool.service.IFoo");
+
+ auto receipt =
+ addAccessorProvider({String8(kInstanceName).c_str(), String8(kInstanceName2).c_str(),
+ String8(kInstanceName3).c_str()},
+ [&](const String16&) -> sp<IBinder> { return nullptr; });
+ EXPECT_FALSE(receipt.expired());
+ Vector<String16> list =
+ defaultServiceManager()->getDeclaredInstances(String16("super.cool.service.IFoo"));
+ // We would prefer ASSERT_EQ here, but we must call removeAccessorProvider
+ EXPECT_EQ(list.size(), 3u);
+ if (list.size() == 3) {
+ bool name1 = false;
+ bool name2 = false;
+ bool name3 = false;
+ for (auto name : list) {
+ if (name == String16("default")) name1 = true;
+ if (name == String16("extra/default")) name2 = true;
+ if (name == String16()) name3 = true;
+ }
+ EXPECT_TRUE(name1) << String8(list[0]);
+ EXPECT_TRUE(name2) << String8(list[1]);
+ EXPECT_TRUE(name3) << String8(list[2]);
+ }
+
+ status_t status = removeAccessorProvider(receipt);
+ EXPECT_EQ(status, OK);
+}
+
+TEST_F(BinderRpcAccessorNoConnection, getDeclaredWrongInstances) {
+ const String16 kInstanceName("super.cool.service.IFoo");
+
+ auto receipt = addAccessorProvider({String8(kInstanceName).c_str()},
+ [&](const String16&) -> sp<IBinder> { return nullptr; });
+ EXPECT_FALSE(receipt.expired());
+ Vector<String16> list = defaultServiceManager()->getDeclaredInstances(String16("unknown"));
+ EXPECT_TRUE(list.empty());
+
+ status_t status = removeAccessorProvider(receipt);
+ EXPECT_EQ(status, OK);
+}
+
+TEST_F(BinderRpcAccessorNoConnection, getDeclaredInstancesSlash) {
+ // This is treated as if there were no '/' and the declared instance is ""
+ const String16 kInstanceName("super.cool.service.IFoo/");
+
+ auto receipt = addAccessorProvider({String8(kInstanceName).c_str()},
+ [&](const String16&) -> sp<IBinder> { return nullptr; });
+ EXPECT_FALSE(receipt.expired());
+ Vector<String16> list =
+ defaultServiceManager()->getDeclaredInstances(String16("super.cool.service.IFoo"));
+ bool name1 = false;
+ for (auto name : list) {
+ if (name == String16("")) name1 = true;
+ }
+ EXPECT_TRUE(name1);
+
+ status_t status = removeAccessorProvider(receipt);
+ EXPECT_EQ(status, OK);
+}
+
constexpr const char* kARpcInstance = "some.instance.name.IFoo/default";
const char* kARpcSupportedServices[] = {
kARpcInstance,
diff --git a/libs/binder/tests/binderRpcUniversalTests.cpp b/libs/binder/tests/binderRpcUniversalTests.cpp
index c6fd487..4b9dcf8 100644
--- a/libs/binder/tests/binderRpcUniversalTests.cpp
+++ b/libs/binder/tests/binderRpcUniversalTests.cpp
@@ -498,9 +498,9 @@
// same thread, everything should have happened in a nested call. Otherwise,
// the callback will be processed on another thread.
if (callIsOneway || callbackIsOneway || delayed) {
- using std::literals::chrono_literals::operator""s;
+ using std::literals::chrono_literals::operator""ms;
RpcMutexUniqueLock _l(cb->mMutex);
- cb->mCv.wait_for(_l, 1s, [&] { return !cb->mValues.empty(); });
+ cb->mCv.wait_for(_l, 1500ms, [&] { return !cb->mValues.empty(); });
}
EXPECT_EQ(cb->mValues.size(), 1UL)
diff --git a/libs/binder/tests/binderSafeInterfaceTest.cpp b/libs/binder/tests/binderSafeInterfaceTest.cpp
index 849dc7c..45b2103 100644
--- a/libs/binder/tests/binderSafeInterfaceTest.cpp
+++ b/libs/binder/tests/binderSafeInterfaceTest.cpp
@@ -789,7 +789,7 @@
std::optional<int32_t> waitForCallback() {
std::unique_lock<decltype(mMutex)> lock(mMutex);
bool success =
- mCondition.wait_for(lock, 100ms, [&]() { return static_cast<bool>(mValue); });
+ mCondition.wait_for(lock, 1000ms, [&]() { return static_cast<bool>(mValue); });
return success ? mValue : std::nullopt;
}
@@ -858,7 +858,13 @@
ASSERT_EQ(b + 1, bPlusOne);
}
-extern "C" int main(int argc, char **argv) {
+} // namespace tests
+} // namespace android
+
+int main(int argc, char** argv) {
+ using namespace android;
+ using namespace android::tests;
+
testing::InitGoogleTest(&argc, argv);
if (fork() == 0) {
@@ -875,6 +881,3 @@
return RUN_ALL_TESTS();
}
-
-} // namespace tests
-} // namespace android
diff --git a/libs/binder/tests/binderStabilityIntegrationTest.cpp b/libs/binder/tests/binderStabilityIntegrationTest.cpp
new file mode 100644
index 0000000..a3fc9cc
--- /dev/null
+++ b/libs/binder/tests/binderStabilityIntegrationTest.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2025 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 <binder/Binder.h>
+#include <binder/IServiceManager.h>
+#include <binder/Stability.h>
+#include <gtest/gtest.h>
+#include <procpartition/procpartition.h>
+
+using namespace android;
+using android::internal::Stability; // for testing only!
+using android::procpartition::getPartition;
+using android::procpartition::Partition;
+
+class BinderStabilityIntegrationTest : public testing::Test,
+ public ::testing::WithParamInterface<String16> {
+public:
+ virtual ~BinderStabilityIntegrationTest() {}
+};
+
+TEST_P(BinderStabilityIntegrationTest, ExpectedStabilityForItsPartition) {
+ const String16& serviceName = GetParam();
+
+ sp<IBinder> binder = defaultServiceManager()->checkService(serviceName);
+ if (!binder) GTEST_SKIP() << "Could not get service, may have gone away.";
+
+ pid_t pid;
+ status_t res = binder->getDebugPid(&pid);
+ if (res != OK) {
+ GTEST_SKIP() << "Could not talk to service to get PID, res: " << statusToString(res);
+ }
+
+ Partition partition = getPartition(pid);
+
+ Stability::Level level = Stability::Level::UNDECLARED;
+ switch (partition) {
+ case Partition::SYSTEM:
+ case Partition::SYSTEM_EXT:
+ level = Stability::Level::SYSTEM;
+ break;
+ case Partition::VENDOR:
+ case Partition::ODM:
+ level = Stability::Level::VENDOR;
+ break;
+ case Partition::UNKNOWN:
+ GTEST_SKIP() << "Not sure of partition of process.";
+ return;
+ default:
+ ADD_FAILURE() << "Unrecognized partition for service: " << partition;
+ return;
+ }
+
+ ASSERT_TRUE(Stability::check(Stability::getRepr(binder.get()), level))
+ << "Binder hosted on partition " << partition
+ << " should have corresponding stability set.";
+}
+
+std::string PrintTestParam(
+ const testing::TestParamInfo<BinderStabilityIntegrationTest::ParamType>& info) {
+ std::string name = String8(info.param).c_str();
+ for (size_t i = 0; i < name.size(); i++) {
+ bool alnum = false;
+ alnum |= (name[i] >= 'a' && name[i] <= 'z');
+ alnum |= (name[i] >= 'A' && name[i] <= 'Z');
+ alnum |= (name[i] >= '0' && name[i] <= '9');
+ alnum |= (name[i] == '_');
+ if (!alnum) name[i] = '_';
+ }
+
+ // index for uniqueness
+ return std::to_string(info.index) + "__" + name;
+}
+
+INSTANTIATE_TEST_CASE_P(RegisteredServices, BinderStabilityIntegrationTest,
+ ::testing::ValuesIn(defaultServiceManager()->listServices()),
+ PrintTestParam);
diff --git a/libs/binder/tests/binderUtilsHostTest.cpp b/libs/binder/tests/binderUtilsHostTest.cpp
index a62ad96..ca70b66 100644
--- a/libs/binder/tests/binderUtilsHostTest.cpp
+++ b/libs/binder/tests/binderUtilsHostTest.cpp
@@ -89,8 +89,8 @@
}
// ~CommandResult() called, child process is killed.
- // Assert that the second sleep does not finish.
- EXPECT_LT(millisSince(start), 6000);
+ // Assert that the last sleep does not finish.
+ EXPECT_LT(millisSince(start), 8000);
}
TEST(UtilsHost, KillWithSigKill) {
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index 07f0143..b2ba1ae 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -121,6 +121,11 @@
PARCEL_READ_NO_STATUS(size_t, hasFileDescriptors),
PARCEL_READ_NO_STATUS(std::vector<android::sp<android::IBinder>>, debugReadAllStrongBinders),
PARCEL_READ_NO_STATUS(std::vector<int>, debugReadAllFileDescriptors),
+ [] (const ::android::Parcel& p, FuzzedDataProvider&) {
+ FUZZ_LOG() << "about to markSensitive";
+ p.markSensitive();
+ FUZZ_LOG() << "markSensitive done";
+ },
[] (const ::android::Parcel& p, FuzzedDataProvider& provider) {
std::string interface = provider.ConsumeRandomLengthString();
FUZZ_LOG() << "about to enforceInterface: " << interface;
@@ -312,8 +317,6 @@
PARCEL_READ_NO_STATUS(int, readParcelFileDescriptor),
PARCEL_READ_WITH_STATUS(unique_fd, readUniqueFileDescriptor),
- PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<unique_fd>>,
- readUniqueFileDescriptorVector),
PARCEL_READ_WITH_STATUS(std::optional<std::vector<unique_fd>>, readUniqueFileDescriptorVector),
PARCEL_READ_WITH_STATUS(std::vector<unique_fd>, readUniqueFileDescriptorVector),
diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
index dfd178a..61b9612 100644
--- a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
+++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
@@ -40,6 +40,13 @@
const uint8_t fuzzerParcelOptions = provider.ConsumeIntegral<uint8_t>();
const bool resultShouldBeView = fuzzerParcelOptions & 1;
const bool resultShouldBeRpc = fuzzerParcelOptions & 2;
+ const bool resultShouldMarkSensitive = fuzzerParcelOptions & 4;
+
+ auto sensitivity_guard = binder::impl::make_scope_guard([&]() {
+ if (resultShouldMarkSensitive) {
+ outputParcel->markSensitive();
+ }
+ });
Parcel* p;
if (resultShouldBeView) {
@@ -49,6 +56,9 @@
} else {
p = outputParcel; // directly fill out the output Parcel
}
+
+ // must be last guard, so outputParcel gets setup as view before
+ // other guards
auto viewify_guard = binder::impl::make_scope_guard([&]() {
if (resultShouldBeView) {
outputParcel->makeDangerousViewOf(p);
diff --git a/libs/binderdebug/stats.cpp b/libs/binderdebug/stats.cpp
index 9c26afa..972fbd5 100644
--- a/libs/binderdebug/stats.cpp
+++ b/libs/binderdebug/stats.cpp
@@ -22,9 +22,9 @@
#include <inttypes.h>
-namespace android {
+int main() {
+ using namespace android;
-extern "C" int main() {
// ignore args - we only print csv
// we should use a csv library here for escaping, because
@@ -58,5 +58,3 @@
}
return 0;
}
-
-} // namespace android
diff --git a/libs/binderdebug/tests/binderdebug_test.cpp b/libs/binderdebug/tests/binderdebug_test.cpp
index ea799c0..ad2b581 100644
--- a/libs/binderdebug/tests/binderdebug_test.cpp
+++ b/libs/binderdebug/tests/binderdebug_test.cpp
@@ -60,8 +60,15 @@
EXPECT_GE(pidInfo.threadCount, 1);
}
-extern "C" {
+} // namespace test
+} // namespace binderdebug
+} // namespace android
+
int main(int argc, char** argv) {
+ using namespace android;
+ using namespace android::binderdebug;
+ using namespace android::binderdebug::test;
+
::testing::InitGoogleTest(&argc, argv);
// Create a child/client process to call into the main process so we can ensure
@@ -84,7 +91,3 @@
return RUN_ALL_TESTS();
}
-} // extern "C"
-} // namespace test
-} // namespace binderdebug
-} // namespace android
diff --git a/libs/debugstore/rust/src/core.rs b/libs/debugstore/rust/src/core.rs
index 6bf79d4..a8acded 100644
--- a/libs/debugstore/rust/src/core.rs
+++ b/libs/debugstore/rust/src/core.rs
@@ -48,7 +48,7 @@
///
/// This constant is used as a part of the debug store's data format,
/// allowing for version tracking and compatibility checks.
- const ENCODE_VERSION: u32 = 1;
+ const ENCODE_VERSION: u32 = 2;
/// Creates a new instance of `DebugStore` with specified event limit and maximum delay.
fn new() -> Self {
@@ -129,7 +129,7 @@
write!(
f,
"{}",
- self.event_store.fold(String::new(), |mut acc, event| {
+ self.event_store.rfold(String::new(), |mut acc, event| {
if !acc.is_empty() {
acc.push_str("||");
}
diff --git a/libs/debugstore/rust/src/storage.rs b/libs/debugstore/rust/src/storage.rs
index 2ad7f4e..47760f3 100644
--- a/libs/debugstore/rust/src/storage.rs
+++ b/libs/debugstore/rust/src/storage.rs
@@ -32,14 +32,18 @@
self.insertion_buffer.force_push(value);
}
- /// Folds over the elements in the storage using the provided function.
- pub fn fold<U, F>(&self, init: U, mut func: F) -> U
+ /// Folds over the elements in the storage in reverse order using the provided function.
+ pub fn rfold<U, F>(&self, init: U, mut func: F) -> U
where
F: FnMut(U, &T) -> U,
{
- let mut acc = init;
+ let mut items = Vec::new();
while let Some(value) = self.insertion_buffer.pop() {
- acc = func(acc, &value);
+ items.push(value);
+ }
+ let mut acc = init;
+ for value in items.iter().rev() {
+ acc = func(acc, value);
}
acc
}
@@ -59,18 +63,18 @@
let storage = Storage::<i32, 10>::new();
storage.insert(7);
- let sum = storage.fold(0, |acc, &x| acc + x);
+ let sum = storage.rfold(0, |acc, &x| acc + x);
assert_eq!(sum, 7, "The sum of the elements should be equal to the inserted value.");
}
#[test]
- fn test_fold_functionality() {
+ fn test_rfold_functionality() {
let storage = Storage::<i32, 5>::new();
storage.insert(1);
storage.insert(2);
storage.insert(3);
- let sum = storage.fold(0, |acc, &x| acc + x);
+ let sum = storage.rfold(0, |acc, &x| acc + x);
assert_eq!(
sum, 6,
"The sum of the elements should be equal to the sum of inserted values."
@@ -84,13 +88,13 @@
storage.insert(2);
storage.insert(5);
- let first_sum = storage.fold(0, |acc, &x| acc + x);
+ let first_sum = storage.rfold(0, |acc, &x| acc + x);
assert_eq!(first_sum, 8, "The sum of the elements should be equal to the inserted values.");
storage.insert(30);
storage.insert(22);
- let second_sum = storage.fold(0, |acc, &x| acc + x);
+ let second_sum = storage.rfold(0, |acc, &x| acc + x);
assert_eq!(
second_sum, 52,
"The sum of the elements should be equal to the inserted values."
@@ -103,7 +107,7 @@
storage.insert(1);
// This value should overwrite the previously inserted value (1).
storage.insert(4);
- let sum = storage.fold(0, |acc, &x| acc + x);
+ let sum = storage.rfold(0, |acc, &x| acc + x);
assert_eq!(sum, 4, "The sum of the elements should be equal to the inserted values.");
}
@@ -128,7 +132,24 @@
thread.join().expect("Thread should finish without panicking");
}
- let count = storage.fold(0, |acc, _| acc + 1);
+ let count = storage.rfold(0, |acc, _| acc + 1);
assert_eq!(count, 100, "Storage should be filled to its limit with concurrent insertions.");
}
+
+ #[test]
+ fn test_rfold_order() {
+ let storage = Storage::<i32, 5>::new();
+ storage.insert(1);
+ storage.insert(2);
+ storage.insert(3);
+
+ let mut result = Vec::new();
+ storage.rfold((), |_, &x| result.push(x));
+
+ assert_eq!(
+ result,
+ vec![3, 2, 1],
+ "Elements should be processed in reverse order of insertion"
+ );
+ }
}
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
index 368f5e0..08ce855 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -21,6 +21,7 @@
"enum_test.cpp",
"expected_test.cpp",
"fake_guard_test.cpp",
+ "finalizer_test.cpp",
"flags_test.cpp",
"function_test.cpp",
"future_test.cpp",
diff --git a/libs/ftl/finalizer_test.cpp b/libs/ftl/finalizer_test.cpp
new file mode 100644
index 0000000..4f5c225
--- /dev/null
+++ b/libs/ftl/finalizer_test.cpp
@@ -0,0 +1,209 @@
+/*
+ * 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 <memory>
+#include <type_traits>
+#include <utility>
+
+#include <ftl/finalizer.h>
+#include <gtest/gtest.h>
+
+namespace android::test {
+
+namespace {
+
+struct Counter {
+ constexpr auto increment_fn() {
+ return [this] { ++value_; };
+ }
+
+ auto increment_finalizer() {
+ return ftl::Finalizer([this] { ++value_; });
+ }
+
+ [[nodiscard]] constexpr auto value() const -> int { return value_; }
+
+ private:
+ int value_ = 0;
+};
+
+struct CounterPair {
+ constexpr auto increment_first_fn() { return first.increment_fn(); }
+ constexpr auto increment_second_fn() { return second.increment_fn(); }
+ [[nodiscard]] constexpr auto values() const -> std::pair<int, int> {
+ return {first.value(), second.value()};
+ }
+
+ private:
+ Counter first;
+ Counter second;
+};
+
+} // namespace
+
+TEST(Finalizer, DefaultConstructionAndNoOpDestructionWhenPolymorphicType) {
+ ftl::FinalizerStd finalizer1;
+ ftl::FinalizerFtl finalizer2;
+ ftl::FinalizerFtl1 finalizer3;
+ ftl::FinalizerFtl2 finalizer4;
+ ftl::FinalizerFtl3 finalizer5;
+}
+
+TEST(Finalizer, InvokesTheFunctionOnDestruction) {
+ Counter counter;
+ {
+ const auto finalizer = counter.increment_finalizer();
+ EXPECT_EQ(counter.value(), 0);
+ }
+ EXPECT_EQ(counter.value(), 1);
+}
+
+TEST(Finalizer, InvocationCanBeCanceled) {
+ Counter counter;
+ {
+ auto finalizer = counter.increment_finalizer();
+ EXPECT_EQ(counter.value(), 0);
+ finalizer.cancel();
+ EXPECT_EQ(counter.value(), 0);
+ }
+ EXPECT_EQ(counter.value(), 0);
+}
+
+TEST(Finalizer, InvokesTheFunctionOnce) {
+ Counter counter;
+ {
+ auto finalizer = counter.increment_finalizer();
+ EXPECT_EQ(counter.value(), 0);
+ finalizer();
+ EXPECT_EQ(counter.value(), 1);
+ finalizer();
+ EXPECT_EQ(counter.value(), 1);
+ }
+ EXPECT_EQ(counter.value(), 1);
+}
+
+TEST(Finalizer, SelfInvocationIsAllowedAndANoOp) {
+ Counter counter;
+ ftl::FinalizerStd finalizer;
+ finalizer = ftl::Finalizer([&]() {
+ counter.increment_fn()();
+ finalizer(); // recursive invocation should do nothing.
+ });
+ EXPECT_EQ(counter.value(), 0);
+ finalizer();
+ EXPECT_EQ(counter.value(), 1);
+}
+
+TEST(Finalizer, MoveConstruction) {
+ Counter counter;
+ {
+ ftl::FinalizerStd outer_finalizer = counter.increment_finalizer();
+ EXPECT_EQ(counter.value(), 0);
+ {
+ ftl::FinalizerStd inner_finalizer = std::move(outer_finalizer);
+ static_assert(std::is_same_v<decltype(inner_finalizer), decltype(outer_finalizer)>);
+ EXPECT_EQ(counter.value(), 0);
+ }
+ EXPECT_EQ(counter.value(), 1);
+ }
+ EXPECT_EQ(counter.value(), 1);
+}
+
+TEST(Finalizer, MoveConstructionWithImplicitConversion) {
+ Counter counter;
+ {
+ auto outer_finalizer = counter.increment_finalizer();
+ EXPECT_EQ(counter.value(), 0);
+ {
+ ftl::FinalizerStd inner_finalizer = std::move(outer_finalizer);
+ static_assert(!std::is_same_v<decltype(inner_finalizer), decltype(outer_finalizer)>);
+ EXPECT_EQ(counter.value(), 0);
+ }
+ EXPECT_EQ(counter.value(), 1);
+ }
+ EXPECT_EQ(counter.value(), 1);
+}
+
+TEST(Finalizer, MoveAssignment) {
+ CounterPair pair;
+ {
+ ftl::FinalizerStd outer_finalizer = ftl::Finalizer(pair.increment_first_fn());
+ EXPECT_EQ(pair.values(), std::make_pair(0, 0));
+
+ {
+ ftl::FinalizerStd inner_finalizer = ftl::Finalizer(pair.increment_second_fn());
+ static_assert(std::is_same_v<decltype(inner_finalizer), decltype(outer_finalizer)>);
+ EXPECT_EQ(pair.values(), std::make_pair(0, 0));
+ inner_finalizer = std::move(outer_finalizer);
+ EXPECT_EQ(pair.values(), std::make_pair(0, 1));
+ }
+ EXPECT_EQ(pair.values(), std::make_pair(1, 1));
+ }
+ EXPECT_EQ(pair.values(), std::make_pair(1, 1));
+}
+
+TEST(Finalizer, MoveAssignmentWithImplicitConversion) {
+ CounterPair pair;
+ {
+ auto outer_finalizer = ftl::Finalizer(pair.increment_first_fn());
+ EXPECT_EQ(pair.values(), std::make_pair(0, 0));
+
+ {
+ ftl::FinalizerStd inner_finalizer = ftl::Finalizer(pair.increment_second_fn());
+ static_assert(!std::is_same_v<decltype(inner_finalizer), decltype(outer_finalizer)>);
+ EXPECT_EQ(pair.values(), std::make_pair(0, 0));
+ inner_finalizer = std::move(outer_finalizer);
+ EXPECT_EQ(pair.values(), std::make_pair(0, 1));
+ }
+ EXPECT_EQ(pair.values(), std::make_pair(1, 1));
+ }
+ EXPECT_EQ(pair.values(), std::make_pair(1, 1));
+}
+
+TEST(Finalizer, NullifiesTheFunctionWhenInvokedIfPossible) {
+ auto shared = std::make_shared<int>(0);
+ std::weak_ptr<int> weak = shared;
+
+ int count = 0;
+ {
+ auto lambda = [capture = std::move(shared)]() {};
+ auto finalizer = ftl::Finalizer(std::move(lambda));
+ EXPECT_FALSE(weak.expired());
+
+ // A lambda is not nullable. Invoking the finalizer cannot destroy it to destroy the lambda's
+ // capture.
+ finalizer();
+ EXPECT_FALSE(weak.expired());
+ }
+ // The lambda is only destroyed when the finalizer instance is destroyed.
+ EXPECT_TRUE(weak.expired());
+
+ shared = std::make_shared<int>(0);
+ weak = shared;
+
+ {
+ auto lambda = [capture = std::move(shared)]() {};
+ auto finalizer = ftl::FinalizerStd(std::move(lambda));
+ EXPECT_FALSE(weak.expired());
+
+ // Since std::function is used, and is nullable, invoking the finalizer will destroy the
+ // contained function, which will destroy the lambda's capture.
+ finalizer();
+ EXPECT_TRUE(weak.expired());
+ }
+}
+
+} // namespace android::test
diff --git a/libs/graphicsenv/Android.bp b/libs/graphicsenv/Android.bp
index af50a29..1fde45b 100644
--- a/libs/graphicsenv/Android.bp
+++ b/libs/graphicsenv/Android.bp
@@ -21,9 +21,25 @@
default_applicable_licenses: ["frameworks_native_license"],
}
+aconfig_declarations {
+ name: "graphicsenv_flags",
+ package: "com.android.graphics.graphicsenv.flags",
+ container: "system",
+ srcs: ["graphicsenv_flags.aconfig"],
+}
+
+cc_aconfig_library {
+ name: "graphicsenv_flags_c_lib",
+ aconfig_declarations: "graphicsenv_flags",
+}
+
cc_library_shared {
name: "libgraphicsenv",
+ defaults: [
+ "aconfig_lib_cc_static_link.defaults",
+ ],
+
srcs: [
"GpuStatsInfo.cpp",
"GraphicsEnv.cpp",
@@ -35,6 +51,10 @@
"-Werror",
],
+ static_libs: [
+ "graphicsenv_flags_c_lib",
+ ],
+
shared_libs: [
"libbase",
"libbinder",
@@ -42,6 +62,7 @@
"libdl_android",
"liblog",
"libutils",
+ "server_configurable_flags",
],
header_libs: [
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index a8d5fe7..4bc2611 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -29,6 +29,7 @@
#include <android-base/strings.h>
#include <android/dlext.h>
#include <binder/IServiceManager.h>
+#include <com_android_graphics_graphicsenv_flags.h>
#include <graphicsenv/IGpuService.h>
#include <log/log.h>
#include <nativeloader/dlext_namespaces.h>
@@ -70,6 +71,8 @@
}
} // namespace
+namespace graphicsenv_flags = com::android::graphics::graphicsenv::flags;
+
namespace android {
enum NativeLibrary {
@@ -596,7 +599,7 @@
// If path is set to nonempty and shouldUseNativeDriver is true, ANGLE will be used regardless.
void GraphicsEnv::setAngleInfo(const std::string& path, const bool shouldUseNativeDriver,
const std::string& packageName,
- const std::vector<std::string> eglFeatures) {
+ const std::vector<std::string>& eglFeatures) {
if (mShouldUseAngle) {
// ANGLE is already set up for this application process, even if the application
// needs to switch from apk to system or vice versa, the application process must
@@ -606,11 +609,11 @@
return;
}
- mAngleEglFeatures = std::move(eglFeatures);
+ mAngleEglFeatures = eglFeatures;
ALOGV("setting ANGLE path to '%s'", path.c_str());
- mAnglePath = std::move(path);
+ mAnglePath = path;
ALOGV("setting app package name to '%s'", packageName.c_str());
- mPackageName = std::move(packageName);
+ mPackageName = packageName;
if (mAnglePath == "system") {
mShouldUseSystemAngle = true;
}
@@ -624,10 +627,36 @@
return mPackageName;
}
+// List of ANGLE features to enable, specified in the Global.Settings value "angle_egl_features".
const std::vector<std::string>& GraphicsEnv::getAngleEglFeatures() {
return mAngleEglFeatures;
}
+void GraphicsEnv::getAngleFeatureOverrides(std::vector<const char*>& enabled,
+ std::vector<const char*>& disabled) {
+ if (!graphicsenv_flags::feature_overrides()) {
+ return;
+ }
+
+ for (const FeatureConfig& feature : mFeatureOverrides.mGlobalFeatures) {
+ if (feature.mEnabled) {
+ enabled.push_back(feature.mFeatureName.c_str());
+ } else {
+ disabled.push_back(feature.mFeatureName.c_str());
+ }
+ }
+
+ if (mFeatureOverrides.mPackageFeatures.count(mPackageName)) {
+ for (const FeatureConfig& feature : mFeatureOverrides.mPackageFeatures[mPackageName]) {
+ if (feature.mEnabled) {
+ enabled.push_back(feature.mFeatureName.c_str());
+ } else {
+ disabled.push_back(feature.mFeatureName.c_str());
+ }
+ }
+ }
+}
+
android_namespace_t* GraphicsEnv::getAngleNamespace() {
std::lock_guard<std::mutex> lock(mNamespaceMutex);
diff --git a/libs/graphicsenv/graphicsenv_flags.aconfig b/libs/graphicsenv/graphicsenv_flags.aconfig
new file mode 100644
index 0000000..ac66362
--- /dev/null
+++ b/libs/graphicsenv/graphicsenv_flags.aconfig
@@ -0,0 +1,9 @@
+package: "com.android.graphics.graphicsenv.flags"
+container: "system"
+
+flag {
+ name: "feature_overrides"
+ namespace: "core_graphics"
+ description: "This flag controls the Feature Overrides in GraphicsEnv."
+ bug: "372694741"
+}
diff --git a/libs/graphicsenv/include/graphicsenv/FeatureOverrides.h b/libs/graphicsenv/include/graphicsenv/FeatureOverrides.h
new file mode 100644
index 0000000..2ef54ad
--- /dev/null
+++ b/libs/graphicsenv/include/graphicsenv/FeatureOverrides.h
@@ -0,0 +1,50 @@
+/*
+ * 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 <map>
+#include <string>
+#include <vector>
+
+namespace android {
+
+class FeatureConfig {
+public:
+ FeatureConfig() = default;
+ FeatureConfig(const FeatureConfig&) = default;
+ virtual ~FeatureConfig() = default;
+
+ std::string mFeatureName;
+ bool mEnabled;
+};
+
+/*
+ * Class for transporting OpenGL ES Feature configurations from GpuService to authorized
+ * recipients.
+ */
+class FeatureOverrides {
+public:
+ FeatureOverrides() = default;
+ FeatureOverrides(const FeatureOverrides&) = default;
+ virtual ~FeatureOverrides() = default;
+
+ std::vector<FeatureConfig> mGlobalFeatures;
+ /* Key: Package Name, Value: Package's Feature Configs */
+ std::map<std::string, std::vector<FeatureConfig>> mPackageFeatures;
+};
+
+} // namespace android
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index b0ab0b9..55fa13a 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_UI_GRAPHICS_ENV_H
#define ANDROID_UI_GRAPHICS_ENV_H 1
+#include <graphicsenv/FeatureOverrides.h>
#include <graphicsenv/GpuStatsInfo.h>
#include <mutex>
@@ -114,12 +115,14 @@
// If shouldUseNativeDriver is true, it means native GLES drivers must be used for the process.
// If path is set to nonempty and shouldUseNativeDriver is true, ANGLE will be used regardless.
void setAngleInfo(const std::string& path, const bool shouldUseNativeDriver,
- const std::string& packageName, const std::vector<std::string> eglFeatures);
+ const std::string& packageName, const std::vector<std::string>& eglFeatures);
// Get the ANGLE driver namespace.
android_namespace_t* getAngleNamespace();
// Get the app package name.
std::string& getPackageName();
const std::vector<std::string>& getAngleEglFeatures();
+ void getAngleFeatureOverrides(std::vector<const char*>& enabled,
+ std::vector<const char*>& disabled);
// Set the persist.graphics.egl system property value.
void nativeToggleAngleAsSystemDriver(bool enabled);
bool shouldUseSystemAngle();
@@ -177,6 +180,7 @@
std::string mPackageName;
// ANGLE EGL features;
std::vector<std::string> mAngleEglFeatures;
+ FeatureOverrides mFeatureOverrides;
// Whether ANGLE should be used.
bool mShouldUseAngle = false;
// Whether loader should load system ANGLE.
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index b0f6e69..f1374e2 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -108,6 +108,15 @@
}
#endif
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+void BufferQueue::ProxyConsumerListener::onSlotCountChanged(int slotCount) {
+ sp<ConsumerListener> listener(mConsumerListener.promote());
+ if (listener != nullptr) {
+ listener->onSlotCountChanged(slotCount);
+ }
+}
+#endif
+
void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
sp<IGraphicBufferConsumer>* outConsumer,
bool consumerIsSurfaceFlinger) {
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 9855b5b..f012586 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -341,9 +341,9 @@
return BAD_VALUE;
}
- if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
- BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)",
- slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
+ const int totalSlotCount = mCore->getTotalSlotCountLocked();
+ if (slot < 0 || slot >= totalSlotCount) {
+ BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)", slot, totalSlotCount);
return BAD_VALUE;
} else if (!mSlots[slot].mBufferState.isAcquired()) {
BQ_LOGE("detachBuffer: slot %d is not owned by the consumer "
@@ -483,10 +483,13 @@
ATRACE_CALL();
ATRACE_BUFFER_INDEX(slot);
- if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS ||
- releaseFence == nullptr) {
- BQ_LOGE("releaseBuffer: slot %d out of range or fence %p NULL", slot,
- releaseFence.get());
+ const int totalSlotCount = mCore->getTotalSlotCountLocked();
+ if (slot < 0 || slot >= totalSlotCount) {
+ BQ_LOGE("releaseBuffer: slot index %d out of range [0, %d)", slot, totalSlotCount);
+ return BAD_VALUE;
+ }
+ if (releaseFence == nullptr) {
+ BQ_LOGE("releaseBuffer: slot %d fence %p NULL", slot, releaseFence.get());
return BAD_VALUE;
}
@@ -515,6 +518,13 @@
{ // Autolock scope
std::lock_guard<std::mutex> lock(mCore->mMutex);
+ const int totalSlotCount = mCore->getTotalSlotCountLocked();
+ if (slot < 0 || slot >= totalSlotCount || releaseFence == nullptr) {
+ BQ_LOGE("releaseBuffer: slot %d out of range [0, %d) or fence %p NULL", slot,
+ totalSlotCount, releaseFence.get());
+ return BAD_VALUE;
+ }
+
// If the frame number has changed because the buffer has been reallocated,
// we can ignore this releaseBuffer for the old buffer.
// Ignore this for the shared buffer where the frame number can easily
@@ -661,6 +671,43 @@
return NO_ERROR;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+status_t BufferQueueConsumer::getReleasedBuffersExtended(std::vector<bool>* outSlotMask) {
+ ATRACE_CALL();
+
+ if (outSlotMask == nullptr) {
+ BQ_LOGE("getReleasedBuffersExtended: outSlotMask may not be NULL");
+ return BAD_VALUE;
+ }
+
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
+
+ if (mCore->mIsAbandoned) {
+ BQ_LOGE("getReleasedBuffersExtended: BufferQueue has been abandoned");
+ return NO_INIT;
+ }
+
+ const int totalSlotCount = mCore->getTotalSlotCountLocked();
+ outSlotMask->resize(totalSlotCount);
+ for (int s = 0; s < totalSlotCount; ++s) {
+ (*outSlotMask)[s] = !mSlots[s].mAcquireCalled;
+ }
+
+ // Remove from the mask queued buffers for which acquire has been called,
+ // since the consumer will not receive their buffer addresses and so must
+ // retain their cached information
+ BufferQueueCore::Fifo::iterator current(mCore->mQueue.begin());
+ while (current != mCore->mQueue.end()) {
+ if (current->mAcquireCalled) {
+ (*outSlotMask)[current->mSlot] = false;
+ }
+ ++current;
+ }
+
+ return NO_ERROR;
+}
+#endif
+
status_t BufferQueueConsumer::setDefaultBufferSize(uint32_t width,
uint32_t height) {
ATRACE_CALL();
@@ -679,6 +726,28 @@
return NO_ERROR;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+status_t BufferQueueConsumer::allowUnlimitedSlots(bool allowUnlimitedSlots) {
+ ATRACE_CALL();
+ BQ_LOGV("allowUnlimitedSlots: %d", allowUnlimitedSlots);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
+
+ if (mCore->mIsAbandoned) {
+ BQ_LOGE("allowUnlimitedSlots: BufferQueue has been abandoned");
+ return NO_INIT;
+ }
+
+ if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) {
+ BQ_LOGE("allowUnlimitedSlots: BufferQueue already connected");
+ return INVALID_OPERATION;
+ }
+
+ mCore->mAllowExtendedSlotCount = allowUnlimitedSlots;
+
+ return OK;
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+
status_t BufferQueueConsumer::setMaxBufferCount(int bufferCount) {
ATRACE_CALL();
@@ -718,16 +787,23 @@
int maxAcquiredBuffers) {
ATRACE_FORMAT("%s(%d)", __func__, maxAcquiredBuffers);
- if (maxAcquiredBuffers < 1 ||
- maxAcquiredBuffers > BufferQueueCore::MAX_MAX_ACQUIRED_BUFFERS) {
- BQ_LOGE("setMaxAcquiredBufferCount: invalid count %d",
- maxAcquiredBuffers);
- return BAD_VALUE;
- }
-
sp<IConsumerListener> listener;
{ // Autolock scope
std::unique_lock<std::mutex> lock(mCore->mMutex);
+
+ // We reserve two slots in order to guarantee that the producer and
+ // consumer can run asynchronously.
+ int maxMaxAcquiredBuffers =
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ mCore->getTotalSlotCountLocked() - 2;
+#else
+ BufferQueueCore::MAX_MAX_ACQUIRED_BUFFERS;
+#endif
+ if (maxAcquiredBuffers < 1 || maxAcquiredBuffers > maxMaxAcquiredBuffers) {
+ BQ_LOGE("setMaxAcquiredBufferCount: invalid count %d", maxAcquiredBuffers);
+ return BAD_VALUE;
+ }
+
mCore->waitWhileAllocatingLocked(lock);
if (mCore->mIsAbandoned) {
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index 5a09399..6c79904 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -38,6 +38,8 @@
#include <system/window.h>
+#include <ui/BufferQueueDefs.h>
+
namespace android {
// Macros for include BufferQueueCore information in log messages
@@ -97,7 +99,11 @@
mConnectedProducerListener(),
mBufferReleasedCbEnabled(false),
mBufferAttachedCbEnabled(false),
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ mSlots(BufferQueueDefs::NUM_BUFFER_SLOTS),
+#else
mSlots(),
+#endif
mQueue(),
mFreeSlots(),
mFreeBuffers(),
@@ -111,6 +117,9 @@
mDefaultWidth(1),
mDefaultHeight(1),
mDefaultBufferDataSpace(HAL_DATASPACE_UNKNOWN),
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ mAllowExtendedSlotCount(false),
+#endif
mMaxBufferCount(BufferQueueDefs::NUM_BUFFER_SLOTS),
mMaxAcquiredBufferCount(1),
mMaxDequeuedBufferCount(1),
@@ -221,6 +230,14 @@
}
}
+int BufferQueueCore::getTotalSlotCountLocked() const {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ return mAllowExtendedSlotCount ? mMaxBufferCount : BufferQueueDefs::NUM_BUFFER_SLOTS;
+#else
+ return BufferQueueDefs::NUM_BUFFER_SLOTS;
+#endif
+}
+
int BufferQueueCore::getMinUndequeuedBufferCountLocked() const {
// If dequeueBuffer is allowed to error out, we don't have to add an
// extra buffer.
@@ -253,6 +270,26 @@
return maxBufferCount;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+status_t BufferQueueCore::extendSlotCountLocked(int size) {
+ int previousSize = (int)mSlots.size();
+ if (previousSize > size) {
+ return BAD_VALUE;
+ }
+ if (previousSize == size) {
+ return NO_ERROR;
+ }
+
+ mSlots.resize(size);
+ for (int i = previousSize; i < size; i++) {
+ mUnusedSlots.push_back(i);
+ }
+
+ mMaxBufferCount = size;
+ return NO_ERROR;
+}
+#endif
+
void BufferQueueCore::clearBufferSlotLocked(int slot) {
BQ_LOGV("clearBufferSlotLocked: slot %d", slot);
@@ -383,7 +420,7 @@
void BufferQueueCore::validateConsistencyLocked() const {
static const useconds_t PAUSE_TIME = 0;
int allocatedSlots = 0;
- for (int slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
+ for (int slot = 0; slot < getTotalSlotCountLocked(); ++slot) {
bool isInFreeSlots = mFreeSlots.count(slot) != 0;
bool isInFreeBuffers =
std::find(mFreeBuffers.cbegin(), mFreeBuffers.cend(), slot) !=
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 2e7cef0..c241482 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -40,6 +40,7 @@
#include <gui/TraceUtils.h>
#include <private/gui/BufferQueueThreadState.h>
+#include <utils/Errors.h>
#include <utils/Log.h>
#include <utils/Trace.h>
@@ -108,9 +109,9 @@
return NO_INIT;
}
- if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
- BQ_LOGE("requestBuffer: slot index %d out of range [0, %d)",
- slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
+ int maxSlot = mCore->getTotalSlotCountLocked();
+ if (slot < 0 || slot >= maxSlot) {
+ BQ_LOGE("requestBuffer: slot index %d out of range [0, %d)", slot, maxSlot);
return BAD_VALUE;
} else if (!mSlots[slot].mBufferState.isDequeued()) {
BQ_LOGE("requestBuffer: slot %d is not owned by the producer "
@@ -123,6 +124,49 @@
return NO_ERROR;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+status_t BufferQueueProducer::extendSlotCount(int size) {
+ ATRACE_CALL();
+
+ sp<IConsumerListener> listener;
+ {
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
+ BQ_LOGV("extendSlotCount: size %d", size);
+
+ if (mCore->mIsAbandoned) {
+ BQ_LOGE("extendSlotCount: BufferQueue has been abandoned");
+ return NO_INIT;
+ }
+
+ if (!mCore->mAllowExtendedSlotCount) {
+ BQ_LOGE("extendSlotCount: Consumer did not allow unlimited slots");
+ return INVALID_OPERATION;
+ }
+
+ int maxBeforeExtension = mCore->mMaxBufferCount;
+
+ if (size == maxBeforeExtension) {
+ return NO_ERROR;
+ }
+
+ if (size < maxBeforeExtension) {
+ return BAD_VALUE;
+ }
+
+ if (status_t ret = mCore->extendSlotCountLocked(size); ret != OK) {
+ return ret;
+ }
+ listener = mCore->mConsumerListener;
+ }
+
+ if (listener) {
+ listener->onSlotCountChanged(size);
+ }
+
+ return NO_ERROR;
+}
+#endif
+
status_t BufferQueueProducer::setMaxDequeuedBufferCount(
int maxDequeuedBuffers) {
int maxBufferCount;
@@ -170,9 +214,10 @@
int bufferCount = mCore->getMinUndequeuedBufferCountLocked();
bufferCount += maxDequeuedBuffers;
- if (bufferCount > BufferQueueDefs::NUM_BUFFER_SLOTS) {
+ if (bufferCount > mCore->getTotalSlotCountLocked()) {
BQ_LOGE("setMaxDequeuedBufferCount: bufferCount %d too large "
- "(max %d)", bufferCount, BufferQueueDefs::NUM_BUFFER_SLOTS);
+ "(max %d)",
+ bufferCount, mCore->getTotalSlotCountLocked());
return BAD_VALUE;
}
@@ -756,9 +801,9 @@
return BAD_VALUE;
}
- if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
- BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)",
- slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
+ const int totalSlotCount = mCore->getTotalSlotCountLocked();
+ if (slot < 0 || slot >= totalSlotCount) {
+ BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)", slot, totalSlotCount);
return BAD_VALUE;
} else if (!mSlots[slot].mBufferState.isDequeued()) {
// TODO(http://b/140581935): This message is BQ_LOGW because it
@@ -993,9 +1038,9 @@
return NO_INIT;
}
- if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
- BQ_LOGE("queueBuffer: slot index %d out of range [0, %d)",
- slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
+ const int totalSlotCount = mCore->getTotalSlotCountLocked();
+ if (slot < 0 || slot >= totalSlotCount) {
+ BQ_LOGE("queueBuffer: slot index %d out of range [0, %d)", slot, totalSlotCount);
return BAD_VALUE;
} else if (!mSlots[slot].mBufferState.isDequeued()) {
BQ_LOGE("queueBuffer: slot %d is not owned by the producer "
@@ -1239,9 +1284,9 @@
return BAD_VALUE;
}
- if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
- BQ_LOGE("cancelBuffer: slot index %d out of range [0, %d)", slot,
- BufferQueueDefs::NUM_BUFFER_SLOTS);
+ const int totalSlotCount = mCore->getTotalSlotCountLocked();
+ if (slot < 0 || slot >= totalSlotCount) {
+ BQ_LOGE("cancelBuffer: slot index %d out of range [0, %d)", slot, totalSlotCount);
return BAD_VALUE;
} else if (!mSlots[slot].mBufferState.isDequeued()) {
BQ_LOGE("cancelBuffer: slot %d is not owned by the producer "
@@ -1409,6 +1454,9 @@
output->nextFrameNumber = mCore->mFrameCounter + 1;
output->bufferReplaced = false;
output->maxBufferCount = mCore->mMaxBufferCount;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ output->isSlotExpansionAllowed = mCore->mAllowExtendedSlotCount;
+#endif
if (listener != nullptr) {
// Set up a death notification so that we can disconnect
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index 602bba8..504509d 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -37,6 +37,8 @@
#include <private/gui/ComposerService.h>
+#include <ui/BufferQueueDefs.h>
+
#include <log/log.h>
#include <utils/Log.h>
#include <utils/String8.h>
@@ -59,7 +61,11 @@
return android_atomic_inc(&globalCounter);
}
-ConsumerBase::ConsumerBase(const sp<IGraphicBufferConsumer>& bufferQueue, bool controlledByApp) :
+ConsumerBase::ConsumerBase(const sp<IGraphicBufferConsumer>& bufferQueue, bool controlledByApp)
+ :
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ mSlots(BufferQueueDefs::NUM_BUFFER_SLOTS),
+#endif
mAbandoned(false),
mConsumer(bufferQueue),
mPrevFinalReleaseFence(Fence::NO_FENCE) {
@@ -68,7 +74,12 @@
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
ConsumerBase::ConsumerBase(bool controlledByApp, bool consumerIsSurfaceFlinger)
- : mAbandoned(false), mPrevFinalReleaseFence(Fence::NO_FENCE) {
+ :
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ mSlots(BufferQueueDefs::NUM_BUFFER_SLOTS),
+#endif
+ mAbandoned(false),
+ mPrevFinalReleaseFence(Fence::NO_FENCE) {
sp<IGraphicBufferProducer> producer;
BufferQueue::createBufferQueue(&producer, &mConsumer, consumerIsSurfaceFlinger);
mSurface = sp<Surface>::make(producer, controlledByApp);
@@ -77,7 +88,11 @@
ConsumerBase::ConsumerBase(const sp<IGraphicBufferProducer>& producer,
const sp<IGraphicBufferConsumer>& consumer, bool controlledByApp)
- : mAbandoned(false),
+ :
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ mSlots(BufferQueueDefs::NUM_BUFFER_SLOTS),
+#endif
+ mAbandoned(false),
mConsumer(consumer),
mSurface(sp<Surface>::make(producer, controlledByApp)),
mPrevFinalReleaseFence(Fence::NO_FENCE) {
@@ -101,9 +116,16 @@
if (err != NO_ERROR) {
CB_LOGE("ConsumerBase: error connecting to BufferQueue: %s (%d)",
strerror(-err), err);
- } else {
- mConsumer->setConsumerName(mName);
+ return;
}
+
+ mConsumer->setConsumerName(mName);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ if (err = mConsumer->allowUnlimitedSlots(true); err != NO_ERROR) {
+ CB_LOGE("ConsumerBase: error marking as allowed to have unlimited slots: %s (%d)",
+ strerror(-err), err);
+ }
+#endif
}
ConsumerBase::~ConsumerBase() {
@@ -130,7 +152,11 @@
}
uint64_t id = buffer->getId();
- for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; i++) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ for (int i = 0; i < (int)mSlots.size(); ++i) {
+#else
+ for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+#endif
auto& slot = mSlots[i];
if (slot.mGraphicBuffer && slot.mGraphicBuffer->getId() == id) {
return i;
@@ -242,6 +268,15 @@
return;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ std::vector<bool> mask;
+ mConsumer->getReleasedBuffersExtended(&mask);
+ for (size_t i = 0; i < mSlots.size(); i++) {
+ if (mask[i]) {
+ freeBufferLocked(i);
+ }
+ }
+#else
uint64_t mask = 0;
mConsumer->getReleasedBuffers(&mask);
for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
@@ -249,11 +284,23 @@
freeBufferLocked(i);
}
}
+#endif
}
void ConsumerBase::onSidebandStreamChanged() {
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+void ConsumerBase::onSlotCountChanged(int slotCount) {
+ CB_LOGV("onSlotCountChanged: %d", slotCount);
+ Mutex::Autolock lock(mMutex);
+
+ if (slotCount > (int)mSlots.size()) {
+ mSlots.resize(slotCount);
+ }
+}
+#endif
+
void ConsumerBase::abandon() {
CB_LOGV("abandon");
Mutex::Autolock lock(mMutex);
@@ -270,7 +317,11 @@
CB_LOGE("abandonLocked: ConsumerBase is abandoned!");
return;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ for (int i = 0; i < (int)mSlots.size(); ++i) {
+#else
for (int i =0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+#endif
freeBufferLocked(i);
}
// disconnect from the BufferQueue
@@ -387,6 +438,15 @@
CB_LOGE("setMaxBufferCount: ConsumerBase is abandoned!");
return NO_INIT;
}
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ if (status_t err = mConsumer->allowUnlimitedSlots(false); err != NO_ERROR) {
+ CB_LOGE("ConsumerBase: error marking as not allowed to have unlimited slots: %s (%d)",
+ strerror(-err), err);
+ return err;
+ }
+#endif
+
return mConsumer->setMaxBufferCount(bufferCount);
}
#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
@@ -448,6 +508,15 @@
if (err != OK) {
return err;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ std::vector<bool> mask;
+ mConsumer->getReleasedBuffersExtended(&mask);
+ for (int i = 0; i < (int)mSlots.size(); i++) {
+ if (mask[i]) {
+ freeBufferLocked(i);
+ }
+ }
+#else
uint64_t mask;
mConsumer->getReleasedBuffers(&mask);
for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
@@ -455,6 +524,8 @@
freeBufferLocked(i);
}
}
+#endif
+
return OK;
}
@@ -596,6 +667,9 @@
// buffer on the same slot), the buffer producer is definitely no longer
// tracking it.
if (!stillTracking(slot, graphicBuffer)) {
+ CB_LOGV("releaseBufferLocked: Not tracking, exiting without calling releaseBuffer for "
+ "slot=%d/%" PRIu64,
+ slot, mSlots[slot].mFrameNumber);
return OK;
}
@@ -615,7 +689,11 @@
bool ConsumerBase::stillTracking(int slot,
const sp<GraphicBuffer> graphicBuffer) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ if (slot < 0 || slot >= (int)mSlots.size()) {
+#else
if (slot < 0 || slot >= BufferQueue::NUM_BUFFER_SLOTS) {
+#endif
return false;
}
return (mSlots[slot].mGraphicBuffer != nullptr &&
diff --git a/libs/gui/FrameRateUtils.cpp b/libs/gui/FrameRateUtils.cpp
index 5c4879c..1b2354e 100644
--- a/libs/gui/FrameRateUtils.cpp
+++ b/libs/gui/FrameRateUtils.cpp
@@ -42,7 +42,7 @@
if (compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT &&
compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE &&
- compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE &&
+ compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_AT_LEAST &&
(!privileged ||
(compatibility != ANATIVEWINDOW_FRAME_RATE_EXACT &&
compatibility != ANATIVEWINDOW_FRAME_RATE_NO_VOTE))) {
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index f2173cd..168129b 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -119,6 +119,9 @@
mTexTarget(texTarget),
mEglDisplay(EGL_NO_DISPLAY),
mEglContext(EGL_NO_CONTEXT),
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ mEglSlots(BufferQueueDefs::NUM_BUFFER_SLOTS),
+#endif
mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
mAttached(true) {
GLC_LOGV("GLConsumer");
@@ -129,27 +132,29 @@
}
#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
-GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex,
- uint32_t texTarget, bool useFenceSync, bool isControlledByApp) :
- ConsumerBase(bq, isControlledByApp),
- mCurrentCrop(Rect::EMPTY_RECT),
- mCurrentTransform(0),
- mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
- mCurrentFence(Fence::NO_FENCE),
- mCurrentTimestamp(0),
- mCurrentDataSpace(HAL_DATASPACE_UNKNOWN),
- mCurrentFrameNumber(0),
- mDefaultWidth(1),
- mDefaultHeight(1),
- mFilteringEnabled(true),
- mTexName(tex),
- mUseFenceSync(useFenceSync),
- mTexTarget(texTarget),
- mEglDisplay(EGL_NO_DISPLAY),
- mEglContext(EGL_NO_CONTEXT),
- mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
- mAttached(true)
-{
+GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, uint32_t texTarget,
+ bool useFenceSync, bool isControlledByApp)
+ : ConsumerBase(bq, isControlledByApp),
+ mCurrentCrop(Rect::EMPTY_RECT),
+ mCurrentTransform(0),
+ mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
+ mCurrentFence(Fence::NO_FENCE),
+ mCurrentTimestamp(0),
+ mCurrentDataSpace(HAL_DATASPACE_UNKNOWN),
+ mCurrentFrameNumber(0),
+ mDefaultWidth(1),
+ mDefaultHeight(1),
+ mFilteringEnabled(true),
+ mTexName(tex),
+ mUseFenceSync(useFenceSync),
+ mTexTarget(texTarget),
+ mEglDisplay(EGL_NO_DISPLAY),
+ mEglContext(EGL_NO_CONTEXT),
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ mEglSlots(BufferQueueDefs::NUM_BUFFER_SLOTS),
+#endif
+ mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
+ mAttached(true) {
GLC_LOGV("GLConsumer");
memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(),
@@ -176,6 +181,9 @@
mTexTarget(texTarget),
mEglDisplay(EGL_NO_DISPLAY),
mEglContext(EGL_NO_CONTEXT),
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ mEglSlots(BufferQueueDefs::NUM_BUFFER_SLOTS),
+#endif
mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
mAttached(false) {
GLC_LOGV("GLConsumer");
@@ -204,6 +212,9 @@
mTexTarget(texTarget),
mEglDisplay(EGL_NO_DISPLAY),
mEglContext(EGL_NO_CONTEXT),
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ mEglSlots(BufferQueueDefs::NUM_BUFFER_SLOTS),
+#endif
mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
mAttached(false) {
GLC_LOGV("GLConsumer");
@@ -395,6 +406,17 @@
return NO_ERROR;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+void GLConsumer::onSlotCountChanged(int slotCount) {
+ ConsumerBase::onSlotCountChanged(slotCount);
+
+ Mutex::Autolock lock(mMutex);
+ if (slotCount > (int)mEglSlots.size()) {
+ mEglSlots.resize(slotCount);
+ }
+}
+#endif
+
status_t GLConsumer::releaseBufferLocked(int buf,
sp<GraphicBuffer> graphicBuffer,
EGLDisplay display, EGLSyncKHR eglFence) {
diff --git a/libs/gui/IConsumerListener.cpp b/libs/gui/IConsumerListener.cpp
index f3bd90c..939db59 100644
--- a/libs/gui/IConsumerListener.cpp
+++ b/libs/gui/IConsumerListener.cpp
@@ -31,7 +31,8 @@
ON_FRAME_DEQUEUED,
ON_FRAME_CANCELLED,
ON_FRAME_DETACHED,
- LAST = ON_FRAME_DETACHED,
+ ON_SLOT_COUNT_CHANGED,
+ LAST = ON_SLOT_COUNT_CHANGED,
};
} // Anonymous namespace
@@ -85,6 +86,14 @@
FrameEventHistoryDelta* /*outDelta*/) override {
LOG_ALWAYS_FATAL("IConsumerListener::addAndGetFrameTimestamps cannot be proxied");
}
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ void onSlotCountChanged(int slotCount) override {
+ callRemoteAsync<
+ decltype(&IConsumerListener::onSlotCountChanged)>(Tag::ON_SLOT_COUNT_CHANGED,
+ slotCount);
+ }
+#endif
};
// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
@@ -116,6 +125,13 @@
return callLocalAsync(data, reply, &IConsumerListener::onFrameCancelled);
case Tag::ON_FRAME_DETACHED:
return callLocalAsync(data, reply, &IConsumerListener::onFrameDetached);
+ case Tag::ON_SLOT_COUNT_CHANGED: {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ return callLocalAsync(data, reply, &IConsumerListener::onSlotCountChanged);
+#else
+ return INVALID_OPERATION;
+#endif
+ }
}
}
diff --git a/libs/gui/IGraphicBufferConsumer.cpp b/libs/gui/IGraphicBufferConsumer.cpp
index 282957b..c1b6568 100644
--- a/libs/gui/IGraphicBufferConsumer.cpp
+++ b/libs/gui/IGraphicBufferConsumer.cpp
@@ -16,6 +16,7 @@
#include <gui/IGraphicBufferConsumer.h>
+#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferItem.h>
#include <gui/IConsumerListener.h>
@@ -24,6 +25,7 @@
#include <ui/Fence.h>
#include <ui/GraphicBuffer.h>
+#include <utils/Errors.h>
#include <utils/NativeHandle.h>
#include <utils/String8.h>
#include <cstdint>
@@ -53,7 +55,9 @@
GET_OCCUPANCY_HISTORY,
DISCARD_FREE_BUFFERS,
DUMP_STATE,
- LAST = DUMP_STATE,
+ ALLOW_UNLIMITED_SLOTS,
+ GET_RELEASED_BUFFERS_EXTENDED,
+ LAST = GET_RELEASED_BUFFERS_EXTENDED,
};
} // Anonymous namespace
@@ -104,11 +108,25 @@
return callRemote<Signature>(Tag::GET_RELEASED_BUFFERS, slotMask);
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ status_t getReleasedBuffersExtended(std::vector<bool>* slotMask) override {
+ using Signature = decltype(&IGraphicBufferConsumer::getReleasedBuffersExtended);
+ return callRemote<Signature>(Tag::GET_RELEASED_BUFFERS_EXTENDED, slotMask);
+ }
+#endif
+
status_t setDefaultBufferSize(uint32_t width, uint32_t height) override {
using Signature = decltype(&IGraphicBufferConsumer::setDefaultBufferSize);
return callRemote<Signature>(Tag::SET_DEFAULT_BUFFER_SIZE, width, height);
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ status_t allowUnlimitedSlots(bool allowUnlimitedSlots) override {
+ using Signature = decltype(&IGraphicBufferConsumer::allowUnlimitedSlots);
+ return callRemote<Signature>(Tag::ALLOW_UNLIMITED_SLOTS, allowUnlimitedSlots);
+ }
+#endif
+
status_t setMaxBufferCount(int bufferCount) override {
using Signature = decltype(&IGraphicBufferConsumer::setMaxBufferCount);
return callRemote<Signature>(Tag::SET_MAX_BUFFER_COUNT, bufferCount);
@@ -228,6 +246,20 @@
using Signature = status_t (IGraphicBufferConsumer::*)(const String8&, String8*) const;
return callLocal<Signature>(data, reply, &IGraphicBufferConsumer::dumpState);
}
+ case Tag::GET_RELEASED_BUFFERS_EXTENDED: {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ return callLocal(data, reply, &IGraphicBufferConsumer::getReleasedBuffersExtended);
+#else
+ return INVALID_OPERATION;
+#endif
+ }
+ case Tag::ALLOW_UNLIMITED_SLOTS: {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ return callLocal(data, reply, &IGraphicBufferConsumer::allowUnlimitedSlots);
+#else
+ return INVALID_OPERATION;
+#endif
+ }
}
}
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 0914480..9f71eb1 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -81,6 +81,7 @@
GET_LAST_QUEUED_BUFFER2,
SET_FRAME_RATE,
SET_ADDITIONAL_OPTIONS,
+ SET_MAX_BUFER_COUNT_EXTENDED,
};
class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
@@ -149,6 +150,20 @@
return result;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ status_t extendSlotCount(int size) override {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+ data.writeInt32(size);
+ status_t result = remote()->transact(SET_MAX_BUFER_COUNT_EXTENDED, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.readInt32();
+ return result;
+ }
+#endif
+
virtual status_t setAsyncMode(bool async) {
Parcel data, reply;
data.writeInterfaceToken(
@@ -981,6 +996,14 @@
// ----------------------------------------------------------------------
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+status_t IGraphicBufferProducer::extendSlotCount(int size) {
+ // No-op for IGBP other than BufferQueue.
+ (void)size;
+ return INVALID_OPERATION;
+}
+#endif
+
status_t IGraphicBufferProducer::setLegacyBufferDrop(bool drop) {
// No-op for IGBP other than BufferQueue.
(void) drop;
@@ -1582,6 +1605,15 @@
return NO_ERROR;
}
#endif
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ case SET_MAX_BUFER_COUNT_EXTENDED: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ int size = data.readInt32();
+ status_t result = extendSlotCount(size);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ }
+#endif
}
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/IGraphicBufferProducerFlattenables.cpp b/libs/gui/IGraphicBufferProducerFlattenables.cpp
index 4e92a39..8b2e2dd 100644
--- a/libs/gui/IGraphicBufferProducerFlattenables.cpp
+++ b/libs/gui/IGraphicBufferProducerFlattenables.cpp
@@ -128,7 +128,7 @@
constexpr size_t IGraphicBufferProducer::QueueBufferOutput::minFlattenedSize() {
return sizeof(width) + sizeof(height) + sizeof(transformHint) + sizeof(numPendingBuffers) +
sizeof(nextFrameNumber) + sizeof(bufferReplaced) + sizeof(maxBufferCount) +
- sizeof(result);
+ sizeof(result) + sizeof(isSlotExpansionAllowed);
}
size_t IGraphicBufferProducer::QueueBufferOutput::getFlattenedSize() const {
return minFlattenedSize() + frameTimestamps.getFlattenedSize();
@@ -152,6 +152,7 @@
FlattenableUtils::write(buffer, size, nextFrameNumber);
FlattenableUtils::write(buffer, size, bufferReplaced);
FlattenableUtils::write(buffer, size, maxBufferCount);
+ FlattenableUtils::write(buffer, size, isSlotExpansionAllowed);
status_t result = frameTimestamps.flatten(buffer, size, fds, count);
if (result != NO_ERROR) {
@@ -175,6 +176,7 @@
FlattenableUtils::read(buffer, size, nextFrameNumber);
FlattenableUtils::read(buffer, size, bufferReplaced);
FlattenableUtils::read(buffer, size, maxBufferCount);
+ FlattenableUtils::read(buffer, size, isSlotExpansionAllowed);
status_t result = frameTimestamps.unflatten(buffer, size, fds, count);
if (result != NO_ERROR) {
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index c1a03fc..44aac9b 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -66,6 +66,8 @@
mask(0),
reserved(0),
cornerRadius(0.0f),
+ clientDrawnCornerRadius(0.0f),
+ clientDrawnShadowRadius(0.0f),
backgroundBlurRadius(0),
color(0),
bufferTransform(0),
@@ -140,6 +142,8 @@
SAFE_PARCEL(output.write, colorTransform.asArray(), 16 * sizeof(float));
SAFE_PARCEL(output.writeFloat, cornerRadius);
+ SAFE_PARCEL(output.writeFloat, clientDrawnCornerRadius);
+ SAFE_PARCEL(output.writeFloat, clientDrawnShadowRadius);
SAFE_PARCEL(output.writeUint32, backgroundBlurRadius);
SAFE_PARCEL(output.writeParcelable, metadata);
SAFE_PARCEL(output.writeFloat, bgColor.r);
@@ -274,6 +278,8 @@
SAFE_PARCEL(input.read, &colorTransform, 16 * sizeof(float));
SAFE_PARCEL(input.readFloat, &cornerRadius);
+ SAFE_PARCEL(input.readFloat, &clientDrawnCornerRadius);
+ SAFE_PARCEL(input.readFloat, &clientDrawnShadowRadius);
SAFE_PARCEL(input.readUint32, &backgroundBlurRadius);
SAFE_PARCEL(input.readParcelable, &metadata);
@@ -596,6 +602,14 @@
what |= eCornerRadiusChanged;
cornerRadius = other.cornerRadius;
}
+ if (other.what & eClientDrawnCornerRadiusChanged) {
+ what |= eClientDrawnCornerRadiusChanged;
+ clientDrawnCornerRadius = other.clientDrawnCornerRadius;
+ }
+ if (other.what & eClientDrawnShadowsChanged) {
+ what |= eClientDrawnShadowsChanged;
+ clientDrawnShadowRadius = other.clientDrawnShadowRadius;
+ }
if (other.what & eBackgroundBlurRadiusChanged) {
what |= eBackgroundBlurRadiusChanged;
backgroundBlurRadius = other.backgroundBlurRadius;
@@ -809,6 +823,8 @@
}
CHECK_DIFF(diff, eLayerStackChanged, other, layerStack);
CHECK_DIFF(diff, eCornerRadiusChanged, other, cornerRadius);
+ CHECK_DIFF(diff, eClientDrawnCornerRadiusChanged, other, clientDrawnCornerRadius);
+ CHECK_DIFF(diff, eClientDrawnShadowsChanged, other, clientDrawnShadowRadius);
CHECK_DIFF(diff, eBackgroundBlurRadiusChanged, other, backgroundBlurRadius);
if (other.what & eBlurRegionsChanged) diff |= eBlurRegionsChanged;
if (other.what & eRelativeLayerChanged) {
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index e41f9bb..ec23365 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -38,6 +38,7 @@
#include <utils/NativeHandle.h>
#include <utils/Trace.h>
+#include <ui/BufferQueueDefs.h>
#include <ui/DynamicDisplayInfo.h>
#include <ui/Fence.h>
#include <ui/GraphicBuffer.h>
@@ -98,7 +99,10 @@
: mGraphicBufferProducer(bufferProducer),
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
mSurfaceDeathListener(nullptr),
-#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+#endif
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ mSlots(NUM_BUFFER_SLOTS),
+#endif
mCrop(Rect::EMPTY_RECT),
mBufferAge(0),
mGenerationNumber(0),
@@ -192,7 +196,7 @@
status_t Surface::allowAllocation(bool allowAllocation) {
return mGraphicBufferProducer->allowAllocation(allowAllocation);
}
-#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+#endif
status_t Surface::setGenerationNumber(uint32_t generation) {
status_t result = mGraphicBufferProducer->setGenerationNumber(generation);
@@ -658,7 +662,11 @@
return result;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ if (buf < 0 || buf >= (int)mSlots.size()) {
+#else
if (buf < 0 || buf >= NUM_BUFFER_SLOTS) {
+#endif
ALOGE("dequeueBuffer: IGraphicBufferProducer returned invalid slot number %d", buf);
android_errorWriteLog(0x534e4554, "36991414"); // SafetyNet logging
return FAILED_TRANSACTION;
@@ -757,7 +765,11 @@
Mutex::Autolock lock(mMutex);
uint64_t bufferId = buffer->getId();
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ for (int slot = 0; slot < (int)mSlots.size(); ++slot) {
+#else
for (int slot = 0; slot < Surface::NUM_BUFFER_SLOTS; ++slot) {
+#endif
auto& bufferSlot = mSlots[slot];
if (bufferSlot.buffer != nullptr && bufferSlot.buffer->getId() == bufferId) {
bufferSlot.buffer = nullptr;
@@ -840,7 +852,11 @@
return output.result;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ if (output.slot < 0 || output.slot >= (int)mSlots.size()) {
+#else
if (output.slot < 0 || output.slot >= NUM_BUFFER_SLOTS) {
+#endif
mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs);
ALOGE("%s: IGraphicBufferProducer returned invalid slot number %d",
__FUNCTION__, output.slot);
@@ -1027,7 +1043,11 @@
return BAD_VALUE;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ for (int i = 0; i < (int)mSlots.size(); i++) {
+#else
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
+#endif
if (mSlots[i].buffer != nullptr &&
mSlots[i].buffer->handle == buffer->handle) {
return i;
@@ -2094,6 +2114,9 @@
mDefaultHeight = output.height;
mNextFrameNumber = output.nextFrameNumber;
mMaxBufferCount = output.maxBufferCount;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ mIsSlotExpansionAllowed = output.isSlotExpansionAllowed;
+#endif
// Ignore transform hint if sticky transform is set or transform to display inverse flag is
// set. Transform hint should be ignored if the client is expected to always submit buffers
@@ -2190,7 +2213,11 @@
*outFence = Fence::NO_FENCE;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ for (int i = 0; i < (int)mSlots.size(); i++) {
+#else
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
+#endif
if (mSlots[i].buffer != nullptr &&
mSlots[i].buffer->getId() == buffer->getId()) {
if (mReportRemovedBuffers) {
@@ -2292,8 +2319,35 @@
ALOGV("Surface::setMaxDequeuedBufferCount");
Mutex::Autolock lock(mMutex);
- status_t err = mGraphicBufferProducer->setMaxDequeuedBufferCount(
- maxDequeuedBuffers);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ if (maxDequeuedBuffers > BufferQueueDefs::NUM_BUFFER_SLOTS && !mIsSlotExpansionAllowed) {
+ return BAD_VALUE;
+ }
+
+ int minUndequeuedBuffers = 0;
+ status_t err = mGraphicBufferProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+ &minUndequeuedBuffers);
+ if (err != OK) {
+ ALOGE("IGraphicBufferProducer::query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS) returned %s",
+ strerror(-err));
+ return err;
+ }
+
+ if (maxDequeuedBuffers > (int)mSlots.size()) {
+ int newSlotCount = minUndequeuedBuffers + maxDequeuedBuffers;
+ err = mGraphicBufferProducer->extendSlotCount(newSlotCount);
+ if (err != OK) {
+ ALOGE("IGraphicBufferProducer::extendSlotCount(%d) returned %s", newSlotCount,
+ strerror(-err));
+ return err;
+ }
+
+ mSlots.resize(newSlotCount);
+ }
+ err = mGraphicBufferProducer->setMaxDequeuedBufferCount(maxDequeuedBuffers);
+#else
+ status_t err = mGraphicBufferProducer->setMaxDequeuedBufferCount(maxDequeuedBuffers);
+#endif
ALOGE_IF(err, "IGraphicBufferProducer::setMaxDequeuedBufferCount(%d) "
"returned %s", maxDequeuedBuffers, strerror(-err));
@@ -2501,7 +2555,11 @@
ALOGE("%s: %zu buffers were freed while being dequeued!",
__FUNCTION__, mDequeuedSlots.size());
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ for (int i = 0; i < (int)mSlots.size(); i++) {
+#else
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
+#endif
mSlots[i].buffer = nullptr;
}
}
@@ -2510,7 +2568,11 @@
std::vector<sp<GraphicBuffer>>* outBuffers) {
ALOGV("Surface::getAndFlushBuffersFromSlots");
for (int32_t i : slots) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ if (i < 0 || i >= (int)mSlots.size()) {
+#else
if (i < 0 || i >= NUM_BUFFER_SLOTS) {
+#endif
ALOGE("%s: Invalid slotIndex: %d", __FUNCTION__, i);
return BAD_VALUE;
}
@@ -2670,7 +2732,11 @@
newDirtyRegion.set(bounds);
mDirtyRegion.clear();
Mutex::Autolock lock(mMutex);
- for (size_t i=0 ; i<NUM_BUFFER_SLOTS ; i++) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ for (int i = 0; i < (int)mSlots.size(); i++) {
+#else
+ for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
+#endif
mSlots[i].dirtyRegion.clear();
}
}
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index cabde22..c6ba7d8 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -829,9 +829,7 @@
SurfaceComposerClient::Transaction::Transaction(const Transaction& other)
: mId(other.mId),
- mAnimation(other.mAnimation),
- mEarlyWakeupStart(other.mEarlyWakeupStart),
- mEarlyWakeupEnd(other.mEarlyWakeupEnd),
+ mFlags(other.mFlags),
mMayContainBuffer(other.mMayContainBuffer),
mDesiredPresentTime(other.mDesiredPresentTime),
mIsAutoTimestamp(other.mIsAutoTimestamp),
@@ -868,9 +866,7 @@
status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel) {
const uint64_t transactionId = parcel->readUint64();
- const bool animation = parcel->readBool();
- const bool earlyWakeupStart = parcel->readBool();
- const bool earlyWakeupEnd = parcel->readBool();
+ const uint32_t flags = parcel->readUint32();
const int64_t desiredPresentTime = parcel->readInt64();
const bool isAutoTimestamp = parcel->readBool();
const bool logCallPoints = parcel->readBool();
@@ -965,9 +961,7 @@
// Parsing was successful. Update the object.
mId = transactionId;
- mAnimation = animation;
- mEarlyWakeupStart = earlyWakeupStart;
- mEarlyWakeupEnd = earlyWakeupEnd;
+ mFlags = flags;
mDesiredPresentTime = desiredPresentTime;
mIsAutoTimestamp = isAutoTimestamp;
mFrameTimelineInfo = frameTimelineInfo;
@@ -996,9 +990,7 @@
const_cast<SurfaceComposerClient::Transaction*>(this)->cacheBuffers();
parcel->writeUint64(mId);
- parcel->writeBool(mAnimation);
- parcel->writeBool(mEarlyWakeupStart);
- parcel->writeBool(mEarlyWakeupEnd);
+ parcel->writeUint32(mFlags);
parcel->writeInt64(mDesiredPresentTime);
parcel->writeBool(mIsAutoTimestamp);
parcel->writeBool(mLogCallPoints);
@@ -1131,8 +1123,7 @@
mInputWindowCommands.merge(other.mInputWindowCommands);
mMayContainBuffer |= other.mMayContainBuffer;
- mEarlyWakeupStart = mEarlyWakeupStart || other.mEarlyWakeupStart;
- mEarlyWakeupEnd = mEarlyWakeupEnd || other.mEarlyWakeupEnd;
+ mFlags |= other.mFlags;
mApplyToken = other.mApplyToken;
mergeFrameTimelineInfo(mFrameTimelineInfo, other.mFrameTimelineInfo);
@@ -1154,15 +1145,13 @@
mInputWindowCommands.clear();
mUncacheBuffers.clear();
mMayContainBuffer = false;
- mAnimation = false;
- mEarlyWakeupStart = false;
- mEarlyWakeupEnd = false;
mDesiredPresentTime = 0;
mIsAutoTimestamp = true;
mFrameTimelineInfo = {};
mApplyToken = nullptr;
mMergedTransactionIds.clear();
mLogCallPoints = false;
+ mFlags = 0;
}
uint64_t SurfaceComposerClient::Transaction::getId() {
@@ -1325,7 +1314,6 @@
Vector<ComposerState> composerStates;
Vector<DisplayState> displayStates;
- uint32_t flags = 0;
for (auto const& kv : mComposerStates) {
composerStates.add(kv.second);
@@ -1333,32 +1321,26 @@
displayStates = std::move(mDisplayStates);
- if (mAnimation) {
- flags |= ISurfaceComposer::eAnimation;
- }
if (oneWay) {
if (synchronous) {
ALOGE("Transaction attempted to set synchronous and one way at the same time"
" this is an invalid request. Synchronous will win for safety");
} else {
- flags |= ISurfaceComposer::eOneWay;
+ mFlags |= ISurfaceComposer::eOneWay;
}
}
- // If both mEarlyWakeupStart and mEarlyWakeupEnd are set
+ // If both ISurfaceComposer::eEarlyWakeupStart and ISurfaceComposer::eEarlyWakeupEnd are set
// it is equivalent for none
- if (mEarlyWakeupStart && !mEarlyWakeupEnd) {
- flags |= ISurfaceComposer::eEarlyWakeupStart;
+ uint32_t wakeupFlags = ISurfaceComposer::eEarlyWakeupStart | ISurfaceComposer::eEarlyWakeupEnd;
+ if ((mFlags & wakeupFlags) == wakeupFlags) {
+ mFlags &= ~(wakeupFlags);
}
- if (mEarlyWakeupEnd && !mEarlyWakeupStart) {
- flags |= ISurfaceComposer::eEarlyWakeupEnd;
- }
-
sp<IBinder> applyToken = mApplyToken ? mApplyToken : getDefaultApplyToken();
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
status_t binderStatus =
- sf->setTransactionState(mFrameTimelineInfo, composerStates, displayStates, flags,
+ sf->setTransactionState(mFrameTimelineInfo, composerStates, displayStates, mFlags,
applyToken, mInputWindowCommands, mDesiredPresentTime,
mIsAutoTimestamp, mUncacheBuffers, hasListenerCallbacks,
listenerCallbacks, mId, mMergedTransactionIds);
@@ -1461,15 +1443,15 @@
}
void SurfaceComposerClient::Transaction::setAnimationTransaction() {
- mAnimation = true;
+ mFlags |= ISurfaceComposer::eAnimation;
}
void SurfaceComposerClient::Transaction::setEarlyWakeupStart() {
- mEarlyWakeupStart = true;
+ mFlags |= ISurfaceComposer::eEarlyWakeupStart;
}
void SurfaceComposerClient::Transaction::setEarlyWakeupEnd() {
- mEarlyWakeupEnd = true;
+ mFlags |= ISurfaceComposer::eEarlyWakeupEnd;
}
layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<SurfaceControl>& sc) {
@@ -1695,6 +1677,29 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setClientDrawnCornerRadius(
+ const sp<SurfaceControl>& sc, float clientDrawnCornerRadius) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+ s->what |= layer_state_t::eClientDrawnCornerRadiusChanged;
+ s->clientDrawnCornerRadius = clientDrawnCornerRadius;
+ return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setClientDrawnShadowRadius(
+ const sp<SurfaceControl>& sc, float clientDrawnShadowRadius) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+ s->what |= layer_state_t::eClientDrawnShadowsChanged;
+ s->clientDrawnShadowRadius = clientDrawnShadowRadius;
+ return *this;
+}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBackgroundBlurRadius(
const sp<SurfaceControl>& sc, int backgroundBlurRadius) {
layer_state_t* s = getLayerState(sc);
diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp
index 82d2554..3fb66d1 100644
--- a/libs/gui/WindowInfo.cpp
+++ b/libs/gui/WindowInfo.cpp
@@ -59,6 +59,32 @@
return out;
}
+status_t writeTransform(android::Parcel* parcel, const ui::Transform& transform) {
+ return parcel->writeFloat(transform.dsdx()) ?:
+ parcel->writeFloat(transform.dtdx()) ?:
+ parcel->writeFloat(transform.tx()) ?:
+ parcel->writeFloat(transform.dtdy()) ?:
+ parcel->writeFloat(transform.dsdy()) ?:
+ parcel->writeFloat(transform.ty());
+}
+
+status_t readTransform(const android::Parcel* parcel, ui::Transform& transform) {
+ float dsdx, dtdx, tx, dtdy, dsdy, ty;
+
+ const status_t status = parcel->readFloat(&dsdx) ?:
+ parcel->readFloat(&dtdx) ?:
+ parcel->readFloat(&tx) ?:
+ parcel->readFloat(&dtdy) ?:
+ parcel->readFloat(&dsdy) ?:
+ parcel->readFloat(&ty);
+ if (status != OK) {
+ return status;
+ }
+
+ transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1});
+ return OK;
+}
+
} // namespace
void WindowInfo::setInputConfig(ftl::Flags<InputConfig> config, bool value) {
@@ -73,10 +99,6 @@
touchableRegion.orSelf(region);
}
-bool WindowInfo::supportsSplitTouch() const {
- return !inputConfig.test(InputConfig::PREVENT_SPLITTING);
-}
-
bool WindowInfo::isSpy() const {
return inputConfig.test(InputConfig::SPY);
}
@@ -135,12 +157,7 @@
parcel->writeInt32(surfaceInset) ?:
parcel->writeFloat(globalScaleFactor) ?:
parcel->writeFloat(alpha) ?:
- parcel->writeFloat(transform.dsdx()) ?:
- parcel->writeFloat(transform.dtdx()) ?:
- parcel->writeFloat(transform.tx()) ?:
- parcel->writeFloat(transform.dtdy()) ?:
- parcel->writeFloat(transform.dsdy()) ?:
- parcel->writeFloat(transform.ty()) ?:
+ writeTransform(parcel, transform) ?:
parcel->writeInt32(static_cast<int32_t>(touchOcclusionMode)) ?:
parcel->writeInt32(ownerPid.val()) ?:
parcel->writeInt32(ownerUid.val()) ?:
@@ -153,8 +170,12 @@
parcel->writeStrongBinder(touchableRegionCropHandle.promote()) ?:
parcel->writeStrongBinder(windowToken) ?:
parcel->writeStrongBinder(focusTransferTarget) ?:
- parcel->writeBool(canOccludePresentation);
+ parcel->writeBool(canOccludePresentation) ?:
+ parcel->writeBool(cloneLayerStackTransform.has_value());
// clang-format on
+ if (cloneLayerStackTransform) {
+ status = status ?: writeTransform(parcel, *cloneLayerStackTransform);
+ }
return status;
}
@@ -174,10 +195,10 @@
return status;
}
- float dsdx, dtdx, tx, dtdy, dsdy, ty;
int32_t lpFlags, lpType, touchOcclusionModeInt, inputConfigInt, ownerPidInt, ownerUidInt,
displayIdInt;
sp<IBinder> touchableRegionCropHandleSp;
+ bool hasCloneLayerStackTransform = false;
// clang-format off
status = parcel->readInt32(&lpFlags) ?:
@@ -188,12 +209,7 @@
parcel->readInt32(&surfaceInset) ?:
parcel->readFloat(&globalScaleFactor) ?:
parcel->readFloat(&alpha) ?:
- parcel->readFloat(&dsdx) ?:
- parcel->readFloat(&dtdx) ?:
- parcel->readFloat(&tx) ?:
- parcel->readFloat(&dtdy) ?:
- parcel->readFloat(&dsdy) ?:
- parcel->readFloat(&ty) ?:
+ readTransform(parcel, /*byRef*/ transform) ?:
parcel->readInt32(&touchOcclusionModeInt) ?:
parcel->readInt32(&ownerPidInt) ?:
parcel->readInt32(&ownerUidInt) ?:
@@ -206,8 +222,8 @@
parcel->readNullableStrongBinder(&touchableRegionCropHandleSp) ?:
parcel->readNullableStrongBinder(&windowToken) ?:
parcel->readNullableStrongBinder(&focusTransferTarget) ?:
- parcel->readBool(&canOccludePresentation);
-
+ parcel->readBool(&canOccludePresentation)?:
+ parcel->readBool(&hasCloneLayerStackTransform);
// clang-format on
if (status != OK) {
@@ -216,7 +232,6 @@
layoutParamsFlags = ftl::Flags<Flag>(lpFlags);
layoutParamsType = static_cast<Type>(lpType);
- transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1});
touchOcclusionMode = static_cast<TouchOcclusionMode>(touchOcclusionModeInt);
inputConfig = ftl::Flags<InputConfig>(inputConfigInt);
ownerPid = Pid{ownerPidInt};
@@ -224,6 +239,15 @@
touchableRegionCropHandle = touchableRegionCropHandleSp;
displayId = ui::LogicalDisplayId{displayIdInt};
+ cloneLayerStackTransform =
+ hasCloneLayerStackTransform ? std::make_optional<ui::Transform>() : std::nullopt;
+ if (cloneLayerStackTransform) {
+ status = readTransform(parcel, /*byRef*/ *cloneLayerStackTransform);
+ if (status != OK) {
+ return status;
+ }
+ }
+
return OK;
}
diff --git a/libs/gui/include/gui/BufferQueue.h b/libs/gui/include/gui/BufferQueue.h
index 0948c4d0..f1c75d3 100644
--- a/libs/gui/include/gui/BufferQueue.h
+++ b/libs/gui/include/gui/BufferQueue.h
@@ -76,6 +76,9 @@
void onSetFrameRate(float frameRate, int8_t compatibility,
int8_t changeFrameRateStrategy) override;
#endif
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ void onSlotCountChanged(int slotCount) override;
+#endif
private:
// mConsumerListener is a weak reference to the IConsumerListener. This is
// the raison d'etre of ProxyConsumerListener.
diff --git a/libs/gui/include/gui/BufferQueueConsumer.h b/libs/gui/include/gui/BufferQueueConsumer.h
index 6aa801a..e00c44e 100644
--- a/libs/gui/include/gui/BufferQueueConsumer.h
+++ b/libs/gui/include/gui/BufferQueueConsumer.h
@@ -96,11 +96,26 @@
// This should be called from the onBuffersReleased() callback.
virtual status_t getReleasedBuffers(uint64_t* outSlotMask);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ // getReleasedBuffers sets the values pointed to by outSlotMask to the bits
+ // indicating which buffer slots have been released by the BufferQueue
+ // but have not yet been released by the consumer.
+ //
+ // This should be called from the onBuffersReleased() callback when
+ // allowUnlimitedSlots has been called.
+ virtual status_t getReleasedBuffersExtended(std::vector<bool>* outSlotMask) override;
+#endif
+
// setDefaultBufferSize is used to set the size of buffers returned by
// dequeueBuffer when a width and height of zero is requested. Default
// is 1x1.
virtual status_t setDefaultBufferSize(uint32_t width, uint32_t height);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ // see IGraphicBufferConsumer::allowUnlimitedSlots
+ virtual status_t allowUnlimitedSlots(bool allowUnlimitedSlots) override;
+#endif
+
// see IGraphicBufferConsumer::setMaxBufferCount
virtual status_t setMaxBufferCount(int bufferCount);
diff --git a/libs/gui/include/gui/BufferQueueCore.h b/libs/gui/include/gui/BufferQueueCore.h
index 77cdf2c..7f92a46 100644
--- a/libs/gui/include/gui/BufferQueueCore.h
+++ b/libs/gui/include/gui/BufferQueueCore.h
@@ -32,10 +32,11 @@
#include <utils/Trace.h>
#include <utils/Vector.h>
-#include <list>
-#include <set>
-#include <mutex>
#include <condition_variable>
+#include <list>
+#include <mutex>
+#include <set>
+#include <vector>
#define ATRACE_BUFFER_INDEX(index) \
do { \
@@ -91,6 +92,10 @@
// Dump our state in a string
void dumpState(const String8& prefix, String8* outResult) const;
+ // getTotalSlotCountLocked returns the total number of slots in use by the
+ // buffer queue at this time.
+ int getTotalSlotCountLocked() const;
+
// getMinUndequeuedBufferCountLocked returns the minimum number of buffers
// that must remain in a state other than DEQUEUED. The async parameter
// tells whether we're in asynchronous mode.
@@ -120,6 +125,10 @@
int getMaxBufferCountLocked(bool asyncMode,
bool dequeueBufferCannotBlock, int maxBufferCount) const;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ // This resizes mSlots to the given size, but only if it's increasing.
+ status_t extendSlotCountLocked(int size);
+#endif
// clearBufferSlotLocked frees the GraphicBuffer and sync resources for the
// given slot.
void clearBufferSlotLocked(int slot);
@@ -204,7 +213,7 @@
// mConnectedProducerListener will not trigger onBufferAttached() callback.
bool mBufferAttachedCbEnabled;
- // mSlots is an array of buffer slots that must be mirrored on the producer
+ // mSlots is a collection of buffer slots that must be mirrored on the producer
// side. This allows buffer ownership to be transferred between the producer
// and consumer without sending a GraphicBuffer over Binder. The entire
// array is initialized to NULL at construction time, and buffers are
@@ -266,8 +275,14 @@
// is specified.
android_dataspace mDefaultBufferDataSpace;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ // mAllowExtendedSlotCount is set by the consumer to permit the producer to
+ // request an unlimited number of slots.
+ bool mAllowExtendedSlotCount;
+#endif
+
// mMaxBufferCount is the limit on the number of buffers that will be
- // allocated at one time. This limit can be set by the consumer.
+ // allocated at one time.
int mMaxBufferCount;
// mMaxAcquiredBufferCount is the number of buffers that the consumer may
diff --git a/libs/gui/include/gui/BufferQueueDefs.h b/libs/gui/include/gui/BufferQueueDefs.h
index ffafb49..42cf439 100644
--- a/libs/gui/include/gui/BufferQueueDefs.h
+++ b/libs/gui/include/gui/BufferQueueDefs.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_GUI_BUFFERQUEUECOREDEFS_H
#define ANDROID_GUI_BUFFERQUEUECOREDEFS_H
+#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferSlot.h>
#include <ui/BufferQueueDefs.h>
@@ -24,7 +25,11 @@
class BufferQueueCore;
namespace BufferQueueDefs {
- typedef BufferSlot SlotsType[NUM_BUFFER_SLOTS];
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ typedef std::vector<BufferSlot> SlotsType;
+#else
+ typedef BufferSlot SlotsType[NUM_BUFFER_SLOTS];
+#endif
} // namespace BufferQueueDefs
} // namespace android
diff --git a/libs/gui/include/gui/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h
index 086ce7c..50abadb 100644
--- a/libs/gui/include/gui/BufferQueueProducer.h
+++ b/libs/gui/include/gui/BufferQueueProducer.h
@@ -47,6 +47,11 @@
// flags indicating that previously-returned buffers are no longer valid.
virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ // see IGraphicsBufferProducer::extendSlotCount
+ virtual status_t extendSlotCount(int size) override;
+#endif
+
// see IGraphicsBufferProducer::setMaxDequeuedBufferCount
virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers);
diff --git a/libs/gui/include/gui/ConsumerBase.h b/libs/gui/include/gui/ConsumerBase.h
index e976aa4..5cd19c1 100644
--- a/libs/gui/include/gui/ConsumerBase.h
+++ b/libs/gui/include/gui/ConsumerBase.h
@@ -185,7 +185,9 @@
virtual void onFrameDetached(const uint64_t bufferId) override;
virtual void onBuffersReleased() override;
virtual void onSidebandStreamChanged() override;
-
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ virtual void onSlotCountChanged(int slotCount) override;
+#endif
virtual int getSlotForBufferLocked(const sp<GraphicBuffer>& buffer);
virtual status_t detachBufferLocked(int slotIndex);
@@ -284,7 +286,11 @@
// slot that has not yet been used. The buffer allocated to a slot will also
// be replaced if the requested buffer usage or geometry differs from that
// of the buffer allocated to a slot.
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ std::vector<Slot> mSlots;
+#else
Slot mSlots[BufferQueueDefs::NUM_BUFFER_SLOTS];
+#endif
// mAbandoned indicates that the BufferQueue will no longer be used to
// consume images buffers pushed to it using the IGraphicBufferProducer
diff --git a/libs/gui/include/gui/GLConsumer.h b/libs/gui/include/gui/GLConsumer.h
index 8a66dc0..30cbfa2 100644
--- a/libs/gui/include/gui/GLConsumer.h
+++ b/libs/gui/include/gui/GLConsumer.h
@@ -266,6 +266,9 @@
virtual status_t acquireBufferLocked(BufferItem *item, nsecs_t presentWhen,
uint64_t maxFrameNumber = 0) override;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ virtual void onSlotCountChanged(int slotCount) override;
+#endif
// releaseBufferLocked overrides the ConsumerBase method to update the
// mEglSlots array in addition to the ConsumerBase.
virtual status_t releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer,
@@ -496,8 +499,11 @@
// slot that has not yet been used. The buffer allocated to a slot will also
// be replaced if the requested buffer usage or geometry differs from that
// of the buffer allocated to a slot.
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ std::vector<EglSlot> mEglSlots;
+#else
EglSlot mEglSlots[BufferQueueDefs::NUM_BUFFER_SLOTS];
-
+#endif
// mCurrentTexture is the buffer slot index of the buffer that is currently
// bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT,
// indicating that no buffer slot is currently bound to the texture. Note,
diff --git a/libs/gui/include/gui/IConsumerListener.h b/libs/gui/include/gui/IConsumerListener.h
index 51d3959..1695aae 100644
--- a/libs/gui/include/gui/IConsumerListener.h
+++ b/libs/gui/include/gui/IConsumerListener.h
@@ -98,6 +98,16 @@
virtual void onSetFrameRate(float /*frameRate*/, int8_t /*compatibility*/,
int8_t /*changeFrameRateStrategy*/) {}
#endif
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ // Notifies the consumer that IGraphicBufferProducer::extendSlotCount has
+ // been called and the total slot count has increased.
+ //
+ // This will only ever be called if
+ // IGraphicBufferConsumer::allowUnlimitedSlots has been called on the
+ // consumer.
+ virtual void onSlotCountChanged(int /* slotCount */) {}
+#endif
};
#ifndef NO_BINDER
diff --git a/libs/gui/include/gui/IGraphicBufferConsumer.h b/libs/gui/include/gui/IGraphicBufferConsumer.h
index 18f5488..56eb291 100644
--- a/libs/gui/include/gui/IGraphicBufferConsumer.h
+++ b/libs/gui/include/gui/IGraphicBufferConsumer.h
@@ -16,6 +16,7 @@
#pragma once
+#include <com_android_graphics_libgui_flags.h>
#include <gui/OccupancyTracker.h>
#include <binder/IInterface.h>
@@ -35,6 +36,10 @@
class GraphicBuffer;
class IConsumerListener;
class NativeHandle;
+
+/*
+ * See IGraphicBufferProducer for details on SLOT_COUNT.
+ */
#ifndef NO_BINDER
class IGraphicBufferConsumer : public IInterface {
public:
@@ -92,7 +97,7 @@
//
// Return of a value other than NO_ERROR means an error has occurred:
// * BAD_VALUE - the given slot number is invalid, either because it is out of the range
- // [0, NUM_BUFFER_SLOTS) or because the slot it refers to is not
+ // [0, SLOT_COUNT) or because the slot it refers to is not
// currently acquired.
virtual status_t detachBuffer(int slot) = 0;
@@ -173,6 +178,19 @@
// * NO_INIT - the BufferQueue has been abandoned.
virtual status_t getReleasedBuffers(uint64_t* slotMask) = 0;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ // getReleasedBuffersExtended for each slot, sets slotMask[slot] to 1 if it
+ // corresponds to a released buffer slot. In particular, a released buffer
+ // is one that has been released by the BufferQueue but has not yet been
+ // released by the consumer.
+ //
+ // This should be called from the onBuffersReleased() callback.
+ //
+ // Return of a value other than NO_ERROR means an error has occurred:
+ // * NO_INIT - the BufferQueue has been abandoned.
+ virtual status_t getReleasedBuffersExtended(std::vector<bool>* slotMask) = 0;
+#endif
+
// setDefaultBufferSize is used to set the size of buffers returned by dequeueBuffer when a
// width and height of zero is requested. Default is 1x1.
//
@@ -180,6 +198,26 @@
// * BAD_VALUE - either w or h was zero
virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h) = 0;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ // allowUnlimitedSlots allows the producer to set the upper bound on slots.
+ //
+ // Must be called before the producer is connected. If the producer
+ // increases the slot count, an IConsumerListener::onSlotCountChanged
+ // update is sent.
+ //
+ // This can not be used with setMaxBufferCount. Calls after
+ // setMaxBufferCount will fail and calls to setMaxBufferCount after setting
+ // this to true will fail.
+ //
+ // Return of a value other than NO_ERROR means an error has occurred:
+ // * NO_INIT - the BufferQueue has been abandoned
+ // * INVALID_OPERATION - one of the following errors has occurred:
+ // * Producer has been connected
+ // * setMaxBufferCount has been called and shrunk the
+ // BufferQueue.
+ virtual status_t allowUnlimitedSlots(bool allowUnlimitedSlots) = 0;
+#endif
+
// setMaxBufferCount sets the maximum value for the number of buffers used in the BufferQueue
// (the initial default is NUM_BUFFER_SLOTS). If a call to setMaxAcquiredBufferCount (by the
// consumer), or a call to setAsyncMode or setMaxDequeuedBufferCount (by the producer), would
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
index a42ddc4..7accca6 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -72,6 +72,14 @@
* dequeueBuffer() to get an empty buffer, fills it with data, then
* calls queueBuffer() to make it available to the consumer.
*
+ * BufferQueues have a size, which we'll refer to in other comments as
+ * SLOT_COUNT. Its default is 64 (NUM_BUFFER_SLOTS). It can be adjusted by
+ * the IGraphicBufferConsumer::setMaxBufferCount, or when
+ * IGraphicBufferConsumer::allowUnlimitedSlots is set to true, by
+ * IGraphicBufferProducer::extendSlotCount. The actual number of buffers in use
+ * is a function of various configurations, including whether we're in single
+ * buffer mode, the maximum dequeuable/aquirable buffers, and SLOT_COUNT.
+ *
* This class was previously called ISurfaceTexture.
*/
#ifndef NO_BINDER
@@ -106,7 +114,7 @@
// slot->buffer mapping so that it's not necessary to transfer a
// GraphicBuffer for every dequeue operation.
//
- // The slot must be in the range of [0, NUM_BUFFER_SLOTS).
+ // The slot must be in the range of [0, SLOT_COUNT).
//
// Return of a value other than NO_ERROR means an error has occurred:
// * NO_INIT - the buffer queue has been abandoned or the producer is not
@@ -116,6 +124,30 @@
// * buffer specified by the slot is not dequeued
virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) = 0;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ // extendSlotCount sets the maximum slot count (SLOT_COUNT) to the given
+ // size. This feature must be enabled by the consumer to function via
+ // IGraphicBufferConsumer::allowUnlimitedSlots. This must be called before
+ // the producer connects.
+ //
+ // After calling this, any slot can be returned in the [0, size) range.
+ // Callers are responsible for the allocation of the appropriate slots
+ // array for their own buffer cache.
+ //
+ // On success, the consumer is notified (so that it can increase its own
+ // slot cache).
+ //
+ // Return of a value other than NO_ERROR means that an error has occurred:
+ // * NO_INIT - the buffer queue has been abandoned
+ // * INVALID_OPERATION - one of the following conditions has occurred:
+ // * The producer is connected already
+ // * The consumer didn't call allowUnlimitedSlots
+ // * BAD_VALUE - The value is smaller than the previous max size
+ // (initialized to 64, then whatever the last call to this
+ // was)
+ virtual status_t extendSlotCount(int size);
+#endif
+
// setMaxDequeuedBufferCount sets the maximum number of buffers that can be
// dequeued by the producer at one time. If this method succeeds, any new
// buffer slots will be both unallocated and owned by the BufferQueue object
@@ -129,7 +161,7 @@
// will result in a BAD_VALUE error.
//
// The buffer count should be at least 1 (inclusive), but at most
- // (NUM_BUFFER_SLOTS - the minimum undequeued buffer count) (exclusive). The
+ // (SLOT_COUNT - the minimum undequeued buffer count) (exclusive). The
// minimum undequeued buffer count can be obtained by calling
// query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS).
//
@@ -239,8 +271,8 @@
// * NO_INIT - the buffer queue has been abandoned or the producer is not
// connected.
// * BAD_VALUE - the given slot number is invalid, either because it is
- // out of the range [0, NUM_BUFFER_SLOTS), or because the slot
- // it refers to is not currently dequeued and requested.
+ // out of the range [0, SLOT_COUNT), or because the slot it
+ // refers to is not currently dequeued and requested.
virtual status_t detachBuffer(int slot) = 0;
// detachNextBuffer is equivalent to calling dequeueBuffer, requestBuffer,
@@ -415,6 +447,7 @@
FrameEventHistoryDelta frameTimestamps;
bool bufferReplaced{false};
int maxBufferCount{BufferQueueDefs::NUM_BUFFER_SLOTS};
+ bool isSlotExpansionAllowed{false};
status_t result{NO_ERROR};
};
@@ -430,7 +463,7 @@
// below). Any other properties (zero point, etc)
// are client-dependent, and should be documented by the client.
//
- // The slot must be in the range of [0, NUM_BUFFER_SLOTS).
+ // The slot must be in the range of [0, SLOT_COUNT).
//
// Upon success, the output will be filled with meaningful values
// (refer to the documentation below).
@@ -460,7 +493,7 @@
//
// The buffer is not queued for use by the consumer.
//
- // The slot must be in the range of [0, NUM_BUFFER_SLOTS).
+ // The slot must be in the range of [0, SLOT_COUNT).
//
// The buffer will not be overwritten until the fence signals. The fence
// will usually be the one obtained from dequeueBuffer.
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 64f191b..1002614 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -231,6 +231,8 @@
eBufferReleaseChannelChanged = 0x40000'00000000,
ePictureProfileHandleChanged = 0x80000'00000000,
eAppContentPriorityChanged = 0x100000'00000000,
+ eClientDrawnCornerRadiusChanged = 0x200000'00000000,
+ eClientDrawnShadowsChanged = 0x400000'00000000,
};
layer_state_t();
@@ -251,9 +253,9 @@
// Geometry updates.
static constexpr uint64_t GEOMETRY_CHANGES = layer_state_t::eBufferCropChanged |
layer_state_t::eBufferTransformChanged | layer_state_t::eCornerRadiusChanged |
- layer_state_t::eCropChanged | layer_state_t::eDestinationFrameChanged |
- layer_state_t::eMatrixChanged | layer_state_t::ePositionChanged |
- layer_state_t::eTransformToDisplayInverseChanged |
+ layer_state_t::eClientDrawnCornerRadiusChanged | layer_state_t::eCropChanged |
+ layer_state_t::eDestinationFrameChanged | layer_state_t::eMatrixChanged |
+ layer_state_t::ePositionChanged | layer_state_t::eTransformToDisplayInverseChanged |
layer_state_t::eTransparentRegionChanged | layer_state_t::eEdgeExtensionChanged;
// Buffer and related updates.
@@ -274,8 +276,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::ePictureProfileHandleChanged |
- layer_state_t::eAppContentPriorityChanged;
+ layer_state_t::eClientDrawnShadowsChanged | 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 |
@@ -300,6 +302,11 @@
static constexpr uint64_t VISIBLE_REGION_CHANGES = layer_state_t::GEOMETRY_CHANGES |
layer_state_t::HIERARCHY_CHANGES | layer_state_t::eAlphaChanged;
+ // Changes that force GPU composition.
+ static constexpr uint64_t COMPOSITION_EFFECTS = layer_state_t::eBackgroundBlurRadiusChanged |
+ layer_state_t::eBlurRegionsChanged | layer_state_t::eCornerRadiusChanged |
+ layer_state_t::eShadowRadiusChanged | layer_state_t::eStretchChanged;
+
bool hasValidBuffer() const;
void sanitize(int32_t permissions);
@@ -328,6 +335,8 @@
uint8_t reserved;
matrix22_t matrix;
float cornerRadius;
+ float clientDrawnCornerRadius;
+ float clientDrawnShadowRadius;
uint32_t backgroundBlurRadius;
sp<SurfaceControl> relativeLayerSurfaceControl;
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 14a3513..755674d 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -558,7 +558,11 @@
// slot that has not yet been used. The buffer allocated to a slot will also
// be replaced if the requested buffer usage or geometry differs from that
// of the buffer allocated to a slot.
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ std::vector<BufferSlot> mSlots;
+#else
BufferSlot mSlots[NUM_BUFFER_SLOTS];
+#endif
// mReqWidth is the buffer width that will be requested at the next dequeue
// operation. It is initialized to 1.
@@ -732,6 +736,10 @@
std::vector<sp<GraphicBuffer>> mRemovedBuffers;
int mMaxBufferCount;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ bool mIsSlotExpansionAllowed;
+#endif
+
sp<IProducerListener> mListenerProxy;
// Get and flush the buffers of given slots, if the buffer in the slot
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 0f66c8b..2215632 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -467,10 +467,7 @@
std::vector<uint64_t> mMergedTransactionIds;
uint64_t mId;
-
- bool mAnimation = false;
- bool mEarlyWakeupStart = false;
- bool mEarlyWakeupEnd = false;
+ uint32_t mFlags = 0;
// Indicates that the Transaction may contain buffers that should be cached. The reason this
// is only a guess is that buffers can be removed before cache is called. This is only a
@@ -567,6 +564,15 @@
Transaction& setCrop(const sp<SurfaceControl>& sc, const Rect& crop);
Transaction& setCrop(const sp<SurfaceControl>& sc, const FloatRect& crop);
Transaction& setCornerRadius(const sp<SurfaceControl>& sc, float cornerRadius);
+ // Sets the client drawn corner radius for the layer. If both a corner radius and a client
+ // radius are sent to SF, the client radius will be used. This indicates that the corner
+ // radius is drawn by the client and not SurfaceFlinger.
+ Transaction& setClientDrawnCornerRadius(const sp<SurfaceControl>& sc,
+ float clientDrawnCornerRadius);
+ // Sets the client drawn shadow radius for the layer. This indicates that the shadows
+ // are drawn by the client and not SurfaceFlinger.
+ Transaction& setClientDrawnShadowRadius(const sp<SurfaceControl>& sc,
+ float clientDrawnShadowRadius);
Transaction& setBackgroundBlurRadius(const sp<SurfaceControl>& sc,
int backgroundBlurRadius);
Transaction& setBlurRegions(const sp<SurfaceControl>& sc,
diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h
index eb3be55..420dc21 100644
--- a/libs/gui/include/gui/WindowInfo.h
+++ b/libs/gui/include/gui/WindowInfo.h
@@ -150,8 +150,6 @@
static_cast<uint32_t>(os::InputConfig::NOT_FOCUSABLE),
NOT_TOUCHABLE =
static_cast<uint32_t>(os::InputConfig::NOT_TOUCHABLE),
- PREVENT_SPLITTING =
- static_cast<uint32_t>(os::InputConfig::PREVENT_SPLITTING),
DUPLICATE_TOUCH_TO_WALLPAPER =
static_cast<uint32_t>(os::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER),
IS_WALLPAPER =
@@ -220,9 +218,14 @@
// An alpha of 1.0 means fully opaque and 0.0 means fully transparent.
float alpha;
- // Transform applied to individual windows.
+ // Transform applied to individual windows for input.
+ // Maps display coordinates to the window's input coordinate space.
ui::Transform transform;
+ // Transform applied to get to the layer stack space of the cloned window for input.
+ // Maps display coordinates of the clone window to the layer stack space of the cloned window.
+ std::optional<ui::Transform> cloneLayerStackTransform;
+
/*
* This is filled in by the WM relative to the frame and then translated
* to absolute coordinates by SurfaceFlinger once the frame is computed.
diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig
index 6bf38c0..394a5cf 100644
--- a/libs/gui/libgui_flags.aconfig
+++ b/libs/gui/libgui_flags.aconfig
@@ -138,4 +138,7 @@
description: "Remove BufferQueueProducer::dequeue's wait on this fence (or the fence entirely) to prevent deadlocks"
bug: "339705065"
is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
} # bq_gl_fence_cleanup
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index f07747f..87051a7 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -55,6 +55,7 @@
"-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_BQ_EXTENDEDALLOCATE=true",
"-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_WB_CONSUMER_BASE_OWNS_BQ=true",
"-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_WB_PLATFORM_API_IMPROVEMENTS=true",
+ "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_WB_UNLIMITED_SLOTS=true",
],
srcs: [
diff --git a/libs/gui/tests/BufferItemConsumer_test.cpp b/libs/gui/tests/BufferItemConsumer_test.cpp
index 3b6a66e..6453885 100644
--- a/libs/gui/tests/BufferItemConsumer_test.cpp
+++ b/libs/gui/tests/BufferItemConsumer_test.cpp
@@ -22,8 +22,11 @@
#include <gui/BufferItemConsumer.h>
#include <gui/IProducerListener.h>
#include <gui/Surface.h>
+#include <ui/BufferQueueDefs.h>
#include <ui/GraphicBuffer.h>
+#include <unordered_set>
+
namespace android {
static constexpr int kWidth = 100;
@@ -57,6 +60,8 @@
};
void SetUp() override {
+ mBuffers.resize(BufferQueueDefs::NUM_BUFFER_SLOTS);
+
mBIC = new BufferItemConsumer(kUsage, kMaxLockedBuffers, true);
String8 name("BufferItemConsumer_Under_Test");
mBIC->setName(name);
@@ -137,6 +142,11 @@
ASSERT_EQ(NO_ERROR, ret);
}
+ void DetachBuffer(int slot) {
+ ALOGD("detachBuffer: slot=%d", slot);
+ status_t ret = mBIC->detachBuffer(mBuffers[slot]);
+ ASSERT_EQ(NO_ERROR, ret);
+ }
std::mutex mMutex;
int mFreedBufferCount{0};
@@ -146,7 +156,7 @@
sp<BufferFreedListener> mBFL;
sp<IGraphicBufferProducer> mProducer;
sp<IGraphicBufferConsumer> mConsumer;
- sp<GraphicBuffer> mBuffers[BufferQueueDefs::NUM_BUFFER_SLOTS];
+ std::vector<sp<GraphicBuffer>> mBuffers;
};
// Test that detaching buffer from consumer side triggers onBufferFreed.
@@ -239,4 +249,52 @@
}
#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+TEST_F(BufferItemConsumerTest, UnlimitedSlots_AcquireReleaseAll) {
+ ASSERT_EQ(OK, mProducer->extendSlotCount(256));
+ mBuffers.resize(256);
+
+ ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(100));
+
+ std::unordered_set<int> slots;
+ for (int i = 0; i < 100; i++) {
+ int slot;
+ DequeueBuffer(&slot);
+ slots.insert(slot);
+ }
+ EXPECT_EQ(100u, slots.size());
+
+ for (int dequeuedSlot : slots) {
+ QueueBuffer(dequeuedSlot);
+
+ int slot;
+ AcquireBuffer(&slot);
+ ReleaseBuffer(slot);
+ }
+}
+
+TEST_F(BufferItemConsumerTest, UnlimitedSlots_AcquireDetachAll) {
+ ASSERT_EQ(OK, mProducer->extendSlotCount(256));
+ mBuffers.resize(256);
+
+ ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(100));
+
+ std::unordered_set<int> slots;
+ for (int i = 0; i < 100; i++) {
+ int slot;
+ DequeueBuffer(&slot);
+ slots.insert(slot);
+ }
+ EXPECT_EQ(100u, slots.size());
+
+ for (int dequeuedSlot : slots) {
+ QueueBuffer(dequeuedSlot);
+
+ int slot;
+ AcquireBuffer(&slot);
+ DetachBuffer(slot);
+ }
+}
+#endif
+
} // namespace android
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 1606099..77b4ae8 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -20,6 +20,8 @@
#include "Constants.h"
#include "MockConsumer.h"
+#include <EGL/egl.h>
+
#include <gui/BufferItem.h>
#include <gui/BufferItemConsumer.h>
#include <gui/BufferQueue.h>
@@ -44,7 +46,9 @@
#include <gtest/gtest.h>
#include <future>
+#include <optional>
#include <thread>
+#include <unordered_map>
#include <com_android_graphics_libgui_flags.h>
@@ -1612,4 +1616,221 @@
}
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+struct MockUnlimitedSlotConsumer : public MockConsumer {
+ virtual void onSlotCountChanged(int size) override { mSize = size; }
+
+ std::optional<int> mSize;
+};
+
+TEST_F(BufferQueueTest, UnlimitedSlots_FailsWhenNotAllowed) {
+ createBufferQueue();
+
+ sp<MockUnlimitedSlotConsumer> mc = sp<MockUnlimitedSlotConsumer>::make();
+ EXPECT_EQ(OK, mConsumer->consumerConnect(mc, false));
+
+ EXPECT_EQ(INVALID_OPERATION, mProducer->extendSlotCount(64));
+ EXPECT_EQ(INVALID_OPERATION, mProducer->extendSlotCount(32));
+ EXPECT_EQ(INVALID_OPERATION, mProducer->extendSlotCount(128));
+
+ EXPECT_EQ(std::nullopt, mc->mSize);
+}
+
+TEST_F(BufferQueueTest, UnlimitedSlots_OnlyAllowedForExtensions) {
+ createBufferQueue();
+
+ sp<MockUnlimitedSlotConsumer> consumerListener = sp<MockUnlimitedSlotConsumer>::make();
+ EXPECT_EQ(OK, mConsumer->consumerConnect(consumerListener, false));
+ EXPECT_EQ(OK, mConsumer->allowUnlimitedSlots(true));
+
+ EXPECT_EQ(BAD_VALUE, mProducer->extendSlotCount(32));
+ EXPECT_EQ(OK, mProducer->extendSlotCount(64));
+ EXPECT_EQ(OK, mProducer->extendSlotCount(128));
+ EXPECT_EQ(128, *consumerListener->mSize);
+
+ EXPECT_EQ(OK, mProducer->extendSlotCount(128));
+ EXPECT_EQ(BAD_VALUE, mProducer->extendSlotCount(127));
+}
+
+class BufferQueueUnlimitedTest : public BufferQueueTest {
+protected:
+ static constexpr auto kMaxBufferCount = 128;
+ static constexpr auto kAcquirableBufferCount = 2;
+ static constexpr auto kDequeableBufferCount = kMaxBufferCount - kAcquirableBufferCount;
+
+ virtual void SetUp() override {
+ BufferQueueTest::SetUp();
+
+ createBufferQueue();
+ setUpConsumer();
+ setUpProducer();
+ }
+
+ void setUpConsumer() {
+ EXPECT_EQ(OK, mConsumer->consumerConnect(mConsumerListener, false));
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ EXPECT_EQ(OK, mConsumer->allowUnlimitedSlots(true));
+#endif
+ EXPECT_EQ(OK, mConsumer->setConsumerUsageBits(GraphicBuffer::USAGE_SW_READ_OFTEN));
+ EXPECT_EQ(OK, mConsumer->setDefaultBufferSize(10, 10));
+ EXPECT_EQ(OK, mConsumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888));
+ EXPECT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(kAcquirableBufferCount));
+ }
+
+ void setUpProducer() {
+ EXPECT_EQ(OK, mProducer->extendSlotCount(kMaxBufferCount));
+
+ IGraphicBufferProducer::QueueBufferOutput output;
+ EXPECT_EQ(OK,
+ mProducer->connect(mProducerListener, NATIVE_WINDOW_API_CPU,
+ /*producerControlledByApp*/ true, &output));
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ ASSERT_TRUE(output.isSlotExpansionAllowed);
+#endif
+ ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(kDequeableBufferCount));
+ ASSERT_EQ(OK, mProducer->allowAllocation(true));
+ }
+
+ std::unordered_map<int, sp<Fence>> dequeueAll() {
+ std::unordered_map<int, sp<Fence>> slotsToFences;
+
+ for (int i = 0; i < kDequeableBufferCount; ++i) {
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buffer;
+
+ status_t ret =
+ mProducer->dequeueBuffer(&slot, &fence, /*w*/ 0, /*h*/ 0, /*format*/ 0,
+ /*uint64_t*/ 0,
+ /*outBufferAge*/ nullptr, /*outTimestamps*/ nullptr);
+ if (ret & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) {
+ EXPECT_EQ(OK, mProducer->requestBuffer(slot, &buffer))
+ << "Unable to request buffer for slot " << slot;
+ }
+ EXPECT_FALSE(slotsToFences.contains(slot));
+ slotsToFences.emplace(slot, fence);
+ }
+ EXPECT_EQ(kDequeableBufferCount, (int)slotsToFences.size());
+ return slotsToFences;
+ }
+
+ sp<MockUnlimitedSlotConsumer> mConsumerListener = sp<MockUnlimitedSlotConsumer>::make();
+ sp<StubProducerListener> mProducerListener = sp<StubProducerListener>::make();
+};
+
+TEST_F(BufferQueueUnlimitedTest, ExpandOverridesConsumerMaxBuffers) {
+ createBufferQueue();
+ setUpConsumer();
+ EXPECT_EQ(OK, mConsumer->setMaxBufferCount(10));
+
+ setUpProducer();
+
+ EXPECT_EQ(kDequeableBufferCount, (int)dequeueAll().size());
+}
+
+TEST_F(BufferQueueUnlimitedTest, CanDetachAll) {
+ auto slotsToFences = dequeueAll();
+ for (auto& [slot, fence] : slotsToFences) {
+ EXPECT_EQ(OK, mProducer->detachBuffer(slot));
+ }
+}
+
+TEST_F(BufferQueueUnlimitedTest, CanCancelAll) {
+ auto slotsToFences = dequeueAll();
+ for (auto& [slot, fence] : slotsToFences) {
+ EXPECT_EQ(OK, mProducer->cancelBuffer(slot, fence));
+ }
+}
+
+TEST_F(BufferQueueUnlimitedTest, CanAcquireAndReleaseAll) {
+ auto slotsToFences = dequeueAll();
+ for (auto& [slot, fence] : slotsToFences) {
+ IGraphicBufferProducer::QueueBufferInput input;
+ input.fence = fence;
+
+ IGraphicBufferProducer::QueueBufferOutput output;
+ EXPECT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+
+ BufferItem buffer;
+ EXPECT_EQ(OK, mConsumer->acquireBuffer(&buffer, 0));
+ EXPECT_EQ(OK,
+ mConsumer->releaseBuffer(buffer.mSlot, buffer.mFrameNumber, EGL_NO_DISPLAY,
+ EGL_NO_SYNC, buffer.mFence));
+ }
+}
+
+TEST_F(BufferQueueUnlimitedTest, CanAcquireAndDetachAll) {
+ auto slotsToFences = dequeueAll();
+ for (auto& [slot, fence] : slotsToFences) {
+ IGraphicBufferProducer::QueueBufferInput input;
+ input.fence = fence;
+
+ IGraphicBufferProducer::QueueBufferOutput output;
+ EXPECT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+
+ BufferItem buffer;
+ EXPECT_EQ(OK, mConsumer->acquireBuffer(&buffer, 0));
+ EXPECT_EQ(OK, mConsumer->detachBuffer(buffer.mSlot));
+ }
+}
+
+TEST_F(BufferQueueUnlimitedTest, GetReleasedBuffersExtended) {
+ // First, acquire and release all the buffers so the consumer "knows" about
+ // them
+ auto slotsToFences = dequeueAll();
+
+ std::vector<bool> releasedSlots;
+ EXPECT_EQ(OK, mConsumer->getReleasedBuffersExtended(&releasedSlots));
+ for (auto& [slot, _] : slotsToFences) {
+ EXPECT_TRUE(releasedSlots[slot])
+ << "Slots that haven't been acquired will show up as released.";
+ }
+ for (auto& [slot, fence] : slotsToFences) {
+ IGraphicBufferProducer::QueueBufferInput input;
+ input.fence = fence;
+
+ IGraphicBufferProducer::QueueBufferOutput output;
+ EXPECT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+
+ BufferItem buffer;
+ EXPECT_EQ(OK, mConsumer->acquireBuffer(&buffer, 0));
+ EXPECT_EQ(OK,
+ mConsumer->releaseBuffer(buffer.mSlot, buffer.mFrameNumber, EGL_NO_DISPLAY,
+ EGL_NO_SYNC_KHR, buffer.mFence));
+ }
+
+ EXPECT_EQ(OK, mConsumer->getReleasedBuffersExtended(&releasedSlots));
+ for (auto& [slot, _] : slotsToFences) {
+ EXPECT_FALSE(releasedSlots[slot])
+ << "Slots that have been acquired will show up as not released.";
+ }
+
+ // Then, alternatively cancel and detach (release) buffers. Only detached
+ // buffers should be returned by getReleasedBuffersExtended
+ slotsToFences = dequeueAll();
+ std::set<int> cancelledSlots;
+ std::set<int> detachedSlots;
+ bool cancel;
+ for (auto& [slot, fence] : slotsToFences) {
+ if (cancel) {
+ EXPECT_EQ(OK, mProducer->cancelBuffer(slot, fence));
+ cancelledSlots.insert(slot);
+ } else {
+ EXPECT_EQ(OK, mProducer->detachBuffer(slot));
+ detachedSlots.insert(slot);
+ }
+ cancel = !cancel;
+ }
+
+ EXPECT_EQ(OK, mConsumer->getReleasedBuffersExtended(&releasedSlots));
+ for (int slot : detachedSlots) {
+ EXPECT_TRUE(releasedSlots[slot]) << "Slots that are detached are released.";
+ }
+ for (int slot : cancelledSlots) {
+ EXPECT_FALSE(releasedSlots[slot])
+ << "Slots that are still held in the queue are not released.";
+ }
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
} // namespace android
diff --git a/libs/gui/tests/CpuConsumer_test.cpp b/libs/gui/tests/CpuConsumer_test.cpp
index f4239cb..9476930 100644
--- a/libs/gui/tests/CpuConsumer_test.cpp
+++ b/libs/gui/tests/CpuConsumer_test.cpp
@@ -803,6 +803,27 @@
::testing::ValuesIn(rgba8888TestSets));
#endif
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+TEST(CpuConsumerSlotTest, UnlimitedSlots_AcquireReleaseAll) {
+ sp<CpuConsumer> cpuConsumer = sp<CpuConsumer>::make(3);
+ sp<Surface> surface = cpuConsumer->getSurface();
+ sp<SurfaceListener> listener = sp<StubSurfaceListener>::make();
+ ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, listener));
+ ASSERT_EQ(OK, surface->setMaxDequeuedBufferCount(256));
+ std::vector<Surface::BatchBuffer> buffers(256);
+ EXPECT_EQ(OK, surface->dequeueBuffers(&buffers));
+
+ for (auto& buffer : buffers) {
+ sp<GraphicBuffer> graphicBuffer = GraphicBuffer::from(buffer.buffer);
+ sp<Fence> fence = sp<Fence>::make(buffer.fenceFd);
+ EXPECT_EQ(OK, surface->queueBuffer(graphicBuffer, fence));
+
+ CpuConsumer::LockedBuffer nativeBuffer;
+ EXPECT_EQ(OK, cpuConsumer->lockNextBuffer(&nativeBuffer));
+ EXPECT_EQ(OK, cpuConsumer->unlockBuffer(nativeBuffer));
+ }
+}
+#endif
} // namespace android
diff --git a/libs/gui/tests/FillBuffer.cpp b/libs/gui/tests/FillBuffer.cpp
index b60995a..11383d9 100644
--- a/libs/gui/tests/FillBuffer.cpp
+++ b/libs/gui/tests/FillBuffer.cpp
@@ -76,7 +76,7 @@
}
void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride) {
- const size_t PIXEL_SIZE = 4;
+ constexpr size_t PIXEL_SIZE = 4;
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
off_t offset = (y * stride + x) * PIXEL_SIZE;
@@ -89,6 +89,21 @@
}
}
+void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride, uint8_t r, uint8_t g, uint8_t b,
+ uint8_t a) {
+ constexpr size_t PIXEL_SIZE = 4;
+
+ for (int x = 0; x < w; x++) {
+ for (int y = 0; y < h; y++) {
+ off_t offset = (y * stride + x) * PIXEL_SIZE;
+ buf[offset] = r;
+ buf[offset + 1] = g;
+ buf[offset + 2] = b;
+ buf[offset + 3] = a;
+ }
+ }
+}
+
void produceOneRGBA8Frame(const sp<ANativeWindow>& anw) {
android_native_buffer_t* anb;
ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(anw.get(),
diff --git a/libs/gui/tests/FillBuffer.h b/libs/gui/tests/FillBuffer.h
index b584179..f5d6b8b 100644
--- a/libs/gui/tests/FillBuffer.h
+++ b/libs/gui/tests/FillBuffer.h
@@ -30,6 +30,8 @@
const android_native_rect_t& rect);
void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride);
+void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride, uint8_t r, uint8_t g, uint8_t b,
+ uint8_t a);
// Produce a single RGBA8 frame by filling a buffer with a checkerboard pattern
// using the CPU. This assumes that the ANativeWindow is already configured to
diff --git a/libs/gui/tests/FrameRateUtilsTest.cpp b/libs/gui/tests/FrameRateUtilsTest.cpp
index 9ffe91f..c502533 100644
--- a/libs/gui/tests/FrameRateUtilsTest.cpp
+++ b/libs/gui/tests/FrameRateUtilsTest.cpp
@@ -34,7 +34,7 @@
ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS, ""));
EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
- EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE,
+ EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_AT_LEAST,
ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
// Privileged APIs.
diff --git a/libs/gui/tests/SurfaceTextureGL_test.cpp b/libs/gui/tests/SurfaceTextureGL_test.cpp
index 449533a..b22b853 100644
--- a/libs/gui/tests/SurfaceTextureGL_test.cpp
+++ b/libs/gui/tests/SurfaceTextureGL_test.cpp
@@ -17,6 +17,8 @@
#define LOG_TAG "SurfaceTextureGL_test"
//#define LOG_NDEBUG 0
+#include <gmock/gmock.h>
+
#include "SurfaceTextureGL.h"
#include "DisconnectWaiter.h"
@@ -735,4 +737,30 @@
ASSERT_NE(NO_ERROR, mST->updateTexImage());
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+TEST_F(SurfaceTextureGLTest, TestUnlimitedSlots) {
+ ASSERT_EQ(OK, mSTC->connect(NATIVE_WINDOW_API_CPU, sp<StubSurfaceListener>::make()));
+ ASSERT_EQ(OK, mSTC->setMaxDequeuedBufferCount(256));
+
+ std::vector<Surface::BatchBuffer> buffers(256);
+ ASSERT_EQ(OK, mSTC->dequeueBuffers(&buffers));
+ ASSERT_EQ(256u, buffers.size());
+ ASSERT_THAT(buffers, Each(Field(&Surface::BatchBuffer::buffer, ::testing::NotNull())));
+
+ for (size_t i = 0; i < buffers.size(); ++i) {
+ sp<GraphicBuffer> graphicBuffer = GraphicBuffer::from(buffers[i].buffer);
+ sp<Fence> fence = sp<Fence>::make(buffers[i].fenceFd);
+
+ void* buf;
+ ASSERT_EQ(OK, graphicBuffer->lock(AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, &buf));
+ fillRGBA8Buffer((uint8_t*)buf, graphicBuffer->getWidth(), graphicBuffer->getHeight(),
+ graphicBuffer->getStride(), i, i, i, i);
+ graphicBuffer->unlock();
+
+ ASSERT_EQ(OK, mSTC->queueBuffer(graphicBuffer, fence));
+ ASSERT_EQ(OK, mST->updateTexImage());
+ checkPixel(0, 0, i, i, i, i);
+ }
+}
+#endif
} // namespace android
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 3185778..646e30e 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-#include "gui/view/Surface.h"
-#include "Constants.h"
-#include "MockConsumer.h"
-
#include <gtest/gtest.h>
#include <SurfaceFlingerProperties.h>
@@ -36,10 +32,13 @@
#include <gui/IConsumerListener.h>
#include <gui/IGraphicBufferConsumer.h>
#include <gui/IGraphicBufferProducer.h>
+#include <gui/IProducerListener.h>
#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/SyncScreenCaptureListener.h>
+#include <gui/view/Surface.h>
+#include <nativebase/nativebase.h>
#include <private/gui/ComposerService.h>
#include <private/gui/ComposerServiceAIDL.h>
#include <sys/types.h>
@@ -47,6 +46,7 @@
#include <ui/BufferQueueDefs.h>
#include <ui/DisplayMode.h>
#include <ui/GraphicBuffer.h>
+#include <ui/PixelFormat.h>
#include <ui/Rect.h>
#include <utils/Errors.h>
#include <utils/String8.h>
@@ -55,9 +55,12 @@
#include <cstddef>
#include <cstdint>
#include <future>
+#include <iterator>
#include <limits>
#include <thread>
+#include "Constants.h"
+#include "MockConsumer.h"
#include "testserver/TestServerClient.h"
namespace android {
@@ -2533,4 +2536,128 @@
}
#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+TEST_F(SurfaceTest, UnlimitedSlots_FailsOnIncompatibleConsumer) {
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+
+ sp<IConsumerListener> consumerListener = sp<FakeConsumer>::make();
+
+ EXPECT_EQ(OK, consumer->allowUnlimitedSlots(false));
+ EXPECT_EQ(OK, consumer->consumerConnect(consumerListener, /* consumerListener */ true));
+
+ sp<Surface> surface = sp<Surface>::make(producer);
+ sp<SurfaceListener> surfaceListener = sp<StubSurfaceListener>::make();
+ EXPECT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, surfaceListener));
+
+ EXPECT_NE(OK, surface->setMaxDequeuedBufferCount(128))
+ << "We shouldn't be able to set high max buffer counts if the consumer doesn't allow "
+ "it";
+}
+
+TEST_F(SurfaceTest, UnlimitedSlots_CanDequeueAndQueueMoreThanOldMaximum) {
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+
+ sp<IConsumerListener> consumerListener = sp<FakeConsumer>::make();
+
+ EXPECT_EQ(OK, consumer->allowUnlimitedSlots(true));
+ EXPECT_EQ(OK, consumer->consumerConnect(consumerListener, /* consumerListener */ true));
+ EXPECT_EQ(OK, consumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888));
+ EXPECT_EQ(OK, consumer->setConsumerUsageBits(AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN));
+
+ sp<Surface> surface = sp<Surface>::make(producer);
+ sp<SurfaceListener> surfaceListener = sp<StubSurfaceListener>::make();
+ EXPECT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, surfaceListener));
+
+ EXPECT_EQ(OK, surface->setMaxDequeuedBufferCount(128))
+ << "If unlimited slots are allowed, we should be able increase the max dequeued buffer "
+ "count arbitrarily";
+
+ std::vector<std::tuple<sp<GraphicBuffer>, sp<Fence>, int>> buffers;
+ for (int i = 0; i < 128; i++) {
+ sp<GraphicBuffer> buffer;
+ sp<Fence> fence;
+ ASSERT_EQ(OK, surface->dequeueBuffer(&buffer, &fence)) << "Unable to dequeue buffer #" << i;
+ buffers.push_back({buffer, fence, i});
+ }
+
+ for (auto& [buffer, fence, idx] : buffers) {
+ ASSERT_EQ(OK, surface->queueBuffer(buffer, fence)) << "Unable to queue buffer #" << idx;
+ }
+}
+
+TEST_F(SurfaceTest, UnlimitedSlots_CanDequeueAndDetachMoreThanOldMaximum) {
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+
+ sp<IConsumerListener> consumerListener = sp<FakeConsumer>::make();
+
+ EXPECT_EQ(OK, consumer->allowUnlimitedSlots(true));
+ EXPECT_EQ(OK, consumer->consumerConnect(consumerListener, /* consumerListener */ true));
+ EXPECT_EQ(OK, consumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888));
+ EXPECT_EQ(OK, consumer->setConsumerUsageBits(AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN));
+
+ sp<Surface> surface = sp<Surface>::make(producer);
+ sp<SurfaceListener> surfaceListener = sp<StubSurfaceListener>::make();
+ EXPECT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, surfaceListener));
+
+ EXPECT_EQ(OK, surface->setMaxDequeuedBufferCount(128))
+ << "If unlimited slots are allowed, we should be able increase the max dequeued buffer "
+ "count arbitrarily";
+
+ std::vector<std::tuple<sp<GraphicBuffer>, sp<Fence>, int>> buffers;
+ for (int i = 0; i < 128; i++) {
+ sp<GraphicBuffer> buffer;
+ sp<Fence> fence;
+ ASSERT_EQ(OK, surface->dequeueBuffer(&buffer, &fence)) << "Unable to dequeue buffer #" << i;
+ buffers.push_back({buffer, fence, i});
+ }
+
+ for (auto& [buffer, _, idx] : buffers) {
+ ASSERT_EQ(OK, surface->detachBuffer(buffer)) << "Unable to detach buffer #" << idx;
+ }
+}
+
+TEST_F(SurfaceTest, UnlimitedSlots_BatchOperations) {
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+
+ sp<IConsumerListener> consumerListener = sp<FakeConsumer>::make();
+
+ EXPECT_EQ(OK, consumer->allowUnlimitedSlots(true));
+ EXPECT_EQ(OK, consumer->consumerConnect(consumerListener, /* consumerListener */ true));
+ EXPECT_EQ(OK, consumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888));
+ EXPECT_EQ(OK, consumer->setConsumerUsageBits(AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN));
+
+ sp<Surface> surface = sp<Surface>::make(producer);
+ sp<SurfaceListener> surfaceListener = sp<StubSurfaceListener>::make();
+ EXPECT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, surfaceListener));
+
+ EXPECT_EQ(OK, surface->setMaxDequeuedBufferCount(128))
+ << "If unlimited slots are allowed, we should be able increase the max dequeued buffer "
+ "count arbitrarily";
+
+ std::vector<Surface::BatchBuffer> buffers(128);
+ EXPECT_EQ(OK, surface->dequeueBuffers(&buffers));
+ EXPECT_EQ(128u, buffers.size());
+
+ std::vector<Surface::BatchQueuedBuffer> queuedBuffers;
+ std::transform(buffers.begin(), buffers.end(), std::back_inserter(queuedBuffers),
+ [](Surface::BatchBuffer& buffer) {
+ Surface::BatchQueuedBuffer out;
+ out.buffer = buffer.buffer;
+ out.fenceFd = buffer.fenceFd;
+ return out;
+ });
+
+ std::vector<SurfaceQueueBufferOutput> outputs;
+ EXPECT_EQ(OK, surface->queueBuffers(queuedBuffers, &outputs));
+ EXPECT_EQ(128u, outputs.size());
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
} // namespace android
diff --git a/libs/gui/tests/WindowInfo_test.cpp b/libs/gui/tests/WindowInfo_test.cpp
index ce22082..e3f9a07 100644
--- a/libs/gui/tests/WindowInfo_test.cpp
+++ b/libs/gui/tests/WindowInfo_test.cpp
@@ -40,7 +40,18 @@
ASSERT_EQ(OK, i.writeToParcel(&p));
p.setDataPosition(0);
i2.readFromParcel(&p);
- ASSERT_TRUE(i2.token == nullptr);
+ ASSERT_EQ(i2.token, nullptr);
+}
+
+TEST(WindowInfo, ParcellingWithoutCloneTransform) {
+ WindowInfo i, i2;
+ i.cloneLayerStackTransform.reset();
+
+ Parcel p;
+ ASSERT_EQ(OK, i.writeToParcel(&p));
+ p.setDataPosition(0);
+ i2.readFromParcel(&p);
+ ASSERT_EQ(i2.cloneLayerStackTransform, std::nullopt);
}
TEST(WindowInfo, Parcelling) {
@@ -71,6 +82,8 @@
i.applicationInfo.token = new BBinder();
i.applicationInfo.dispatchingTimeoutMillis = 0x12345678ABCD;
i.focusTransferTarget = new BBinder();
+ i.cloneLayerStackTransform = ui::Transform();
+ i.cloneLayerStackTransform->set({5, -1, 100, 4, 0, 40, 0, 0, 1});
Parcel p;
i.writeToParcel(&p);
@@ -100,6 +113,7 @@
ASSERT_EQ(i.touchableRegionCropHandle, i2.touchableRegionCropHandle);
ASSERT_EQ(i.applicationInfo, i2.applicationInfo);
ASSERT_EQ(i.focusTransferTarget, i2.focusTransferTarget);
+ ASSERT_EQ(i.cloneLayerStackTransform, i2.cloneLayerStackTransform);
}
TEST(InputApplicationInfo, Parcelling) {
diff --git a/libs/input/AccelerationCurve.cpp b/libs/input/AccelerationCurve.cpp
index 0a92a71..0b47f3e 100644
--- a/libs/input/AccelerationCurve.cpp
+++ b/libs/input/AccelerationCurve.cpp
@@ -40,6 +40,18 @@
constexpr std::array<double, 15> kSensitivityFactors = {1, 2, 4, 6, 7, 8, 9, 10,
11, 12, 13, 14, 16, 18, 20};
+// Calculates the base gain for a given pointer sensitivity value.
+//
+// The base gain is a scaling factor that is applied to the pointer movement.
+// Higher sensitivity values result in larger base gains, which in turn result
+// in faster pointer movements.
+//
+// The base gain is calculated using a linear mapping function that maps the
+// sensitivity range [-7, 7] to a base gain range [0.5, 2.0].
+double calculateBaseGain(int32_t sensitivity) {
+ return 0.5 + (sensitivity + 7) * (2.0 - 0.5) / (7 + 7);
+}
+
} // namespace
std::vector<AccelerationCurveSegment> createAccelerationCurveForPointerSensitivity(
@@ -60,4 +72,13 @@
return output;
}
+std::vector<AccelerationCurveSegment> createFlatAccelerationCurve(int32_t sensitivity) {
+ LOG_ALWAYS_FATAL_IF(sensitivity < -7 || sensitivity > 7, "Invalid pointer sensitivity value");
+ std::vector<AccelerationCurveSegment> output = {
+ AccelerationCurveSegment{std::numeric_limits<double>::infinity(),
+ calculateBaseGain(sensitivity),
+ /* reciprocal = */ 0}};
+ return output;
+}
+
} // namespace android
\ No newline at end of file
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 6a55726..56ccaab 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -327,8 +327,8 @@
android::base::unique_fd fd, sp<IBinder> token) {
const int result = fcntl(fd, F_SETFL, O_NONBLOCK);
if (result != 0) {
- LOG_ALWAYS_FATAL("channel '%s' ~ Could not make socket non-blocking: %s", name.c_str(),
- strerror(errno));
+ LOG_ALWAYS_FATAL("channel '%s' ~ Could not make socket (%d) non-blocking: %s", name.c_str(),
+ fd.get(), strerror(errno));
return nullptr;
}
// using 'new' to access a non-public constructor
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index 90d29dd..d2c49b1 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -615,7 +615,7 @@
ALOGE("%s: Null parcel", __func__);
return nullptr;
}
- std::string loadFileName = parcel->readCString();
+ std::string loadFileName = parcel->readString8().c_str();
std::unique_ptr<KeyCharacterMap> map =
std::make_unique<KeyCharacterMap>(KeyCharacterMap(loadFileName));
map->mType = static_cast<KeyCharacterMap::KeyboardType>(parcel->readInt32());
@@ -704,7 +704,7 @@
ALOGE("%s: Null parcel", __func__);
return;
}
- parcel->writeCString(mLoadFileName.c_str());
+ parcel->writeString8(String8(mLoadFileName.c_str()));
parcel->writeInt32(static_cast<int32_t>(mType));
parcel->writeBool(mLayoutOverlayApplied);
diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl
index 31592cd..6ce3fba 100644
--- a/libs/input/android/os/IInputConstants.aidl
+++ b/libs/input/android/os/IInputConstants.aidl
@@ -76,6 +76,9 @@
/* The default pointer acceleration value. */
const int DEFAULT_POINTER_ACCELERATION = 3;
+ /* The default mouse wheel acceleration value. */
+ const int DEFAULT_MOUSE_WHEEL_ACCELERATION = 4;
+
/**
* Use the default Velocity Tracker Strategy. Different axes may use different default
* strategies.
diff --git a/libs/input/android/os/InputConfig.aidl b/libs/input/android/os/InputConfig.aidl
index da62e03..e5f7b56 100644
--- a/libs/input/android/os/InputConfig.aidl
+++ b/libs/input/android/os/InputConfig.aidl
@@ -57,16 +57,9 @@
NOT_TOUCHABLE = 1 << 3,
/**
- * Indicates that this window will not accept a touch event that is split between
- * more than one window. When set:
- * - If this window receives a DOWN event with the first pointer, all successive
- * pointers that go down, regardless of their location on the screen, will be
- * directed to this window;
- * - If the DOWN event lands outside the touchable bounds of this window, no
- * successive pointers that go down, regardless of their location on the screen,
- * will be directed to this window.
+ * This flag is now deprecated and should not be used.
*/
- PREVENT_SPLITTING = 1 << 4,
+ DEPRECATED_PREVENT_SPLITTING = 1 << 4,
/**
* Indicates that this window shows the wallpaper behind it, so all touch events
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index fd77048..bf928f4 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -37,9 +37,9 @@
}
flag {
- name: "split_all_touches"
+ name: "deprecate_split_touch_apis"
namespace: "input"
- description: "Set FLAG_SPLIT_TOUCHES to true for all windows, regardless of what they specify. This is essentially deprecating this flag by forcefully enabling the split functionality"
+ description: "Deprecate all public APIs related to split touch because now all windows behave as if split touch is permanently enabled and there's no way for a window to disable split touch."
bug: "239934827"
}
@@ -109,13 +109,6 @@
}
flag {
- name: "enable_touchpad_fling_stop"
- namespace: "input"
- description: "Enable fling scrolling to be stopped by putting a finger on the touchpad again"
- bug: "281106755"
-}
-
-flag {
name: "enable_prediction_pruning_via_jerk_thresholding"
namespace: "input"
description: "Enable prediction pruning based on jerk thresholds."
@@ -195,6 +188,16 @@
}
flag {
+ name: "disable_touch_input_mapper_pointer_usage"
+ namespace: "input"
+ description: "Disable the PointerUsage concept in TouchInputMapper since the old touchpad stack is no longer used."
+ bug: "281840344"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "keyboard_repeat_keys"
namespace: "input"
description: "Allow user to enable key repeats or configure timeout before key repeat and key repeat delay rates."
@@ -231,3 +234,13 @@
description: "Allow cursor to transition across multiple connected displays"
bug: "362719483"
}
+
+flag {
+ name: "use_cloned_screen_coordinates_as_raw"
+ namespace: "input"
+ description: "Use the cloned window's layer stack (screen) space as the raw coordinate space for input going to clones"
+ bug: "377846505"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 0167c43..85a37fe 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -58,11 +58,19 @@
"-Wno-unused-parameter",
],
sanitize: {
+ address: true,
hwaddress: true,
undefined: true,
all_undefined: true,
diag: {
+ cfi: true,
+ integer_overflow: true,
+ memtag_heap: true,
undefined: true,
+ misc_undefined: [
+ "bounds",
+ "all",
+ ],
},
},
shared_libs: [
@@ -92,11 +100,6 @@
"libstatssocket_lazy",
],
},
- host: {
- sanitize: {
- address: true,
- },
- },
},
native_coverage: false,
}
diff --git a/libs/input/tests/InputVerifier_test.cpp b/libs/input/tests/InputVerifier_test.cpp
index e2eb080..5bb1d56 100644
--- a/libs/input/tests/InputVerifier_test.cpp
+++ b/libs/input/tests/InputVerifier_test.cpp
@@ -14,9 +14,13 @@
* limitations under the License.
*/
+#include <android/input.h>
+#include <android-base/result.h>
#include <gtest/gtest.h>
+#include <input/Input.h>
#include <input/InputVerifier.h>
#include <string>
+#include <vector>
namespace android {
@@ -48,7 +52,7 @@
AMOTION_EVENT_ACTION_DOWN,
/*pointerCount=*/properties.size(), properties.data(),
coords.data(), /*flags=*/0);
- ASSERT_TRUE(result.ok());
+ ASSERT_RESULT_OK(result);
}
} // namespace android
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index ed3e8c1..10abb7c 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -258,11 +258,11 @@
ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE = 1,
/**
- * The window requests a frame rate that is greater than or equal to the specified frame rate.
+ * The window requests a frame rate that is at least the specified frame rate.
* This value should be used for UIs, animations, scrolling, and anything that is not a game
* or video.
*/
- ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE = 2
+ ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_AT_LEAST = 2
};
/**
diff --git a/libs/permission/include/binder/AppOpsManager.h b/libs/permission/include/binder/AppOpsManager.h
index 243532b..7e179d6 100644
--- a/libs/permission/include/binder/AppOpsManager.h
+++ b/libs/permission/include/binder/AppOpsManager.h
@@ -148,7 +148,10 @@
OP_BLUETOOTH_ADVERTISE = 114,
OP_RECORD_INCOMING_PHONE_AUDIO = 115,
OP_NEARBY_WIFI_DEVICES = 116,
- _NUM_OP = 117
+ // 116 - 154 omitted due to lack of use in native
+ OP_CONTROL_AUDIO = 154,
+ OP_CONTROL_AUDIO_PARTIAL = 155,
+ _NUM_OP = 156,
};
enum {
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index 7e8ccef..14d08ee 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -544,9 +544,18 @@
}
if (graphicBuffer && parameters.layer.luts) {
+ const bool dimInLinearSpace = parameters.display.dimmingStage !=
+ aidl::android::hardware::graphics::composer3::DimmingStage::GAMMA_OETF;
+ const ui::Dataspace runtimeEffectDataspace = !dimInLinearSpace
+ ? static_cast<ui::Dataspace>(
+ (parameters.outputDataSpace & ui::Dataspace::STANDARD_MASK) |
+ ui::Dataspace::TRANSFER_GAMMA2_2 |
+ (parameters.outputDataSpace & ui::Dataspace::RANGE_MASK))
+ : parameters.outputDataSpace;
+
shader = mLutShader.lutShader(shader, parameters.layer.luts,
parameters.layer.sourceDataspace,
- toSkColorSpace(parameters.outputDataSpace));
+ toSkColorSpace(runtimeEffectDataspace));
}
if (parameters.requiresLinearEffect) {
diff --git a/libs/renderengine/skia/VulkanInterface.cpp b/libs/renderengine/skia/VulkanInterface.cpp
index 37b69f6..7331bbc 100644
--- a/libs/renderengine/skia/VulkanInterface.cpp
+++ b/libs/renderengine/skia/VulkanInterface.cpp
@@ -204,10 +204,10 @@
BAIL("[%s] null", #expr); \
}
-#define VK_CHECK(expr) \
- if ((expr) != VK_SUCCESS) { \
- BAIL("[%s] failed. err = %d", #expr, expr); \
- return; \
+#define VK_CHECK(expr) \
+ if (VkResult result = (expr); result != VK_SUCCESS) { \
+ BAIL("[%s] failed. err = %d", #expr, result); \
+ return; \
}
#define VK_GET_PROC(F) \
diff --git a/libs/renderengine/skia/compat/GraphiteGpuContext.cpp b/libs/renderengine/skia/compat/GraphiteGpuContext.cpp
index 69f5832..7a72d09 100644
--- a/libs/renderengine/skia/compat/GraphiteGpuContext.cpp
+++ b/libs/renderengine/skia/compat/GraphiteGpuContext.cpp
@@ -110,8 +110,40 @@
return mContext->isDeviceLost();
}
+void GraphiteGpuContext::setResourceCacheLimit(size_t maxResourceBytes) {
+ // Graphite has a separate budget for its Context and its Recorder. For now the majority of
+ // memory that Graphite will allocate will be on the Recorder and minimal amount on the Context.
+ // The main allocations on the Context are MSAA buffers (not often, if ever used in
+ // RenderEngine) and stencil buffers. However, both of these should be "memoryless" in Vulkan on
+ // tiled GPUs, so they don't actually use GPU memory. However, in Vulkan there are scenarios
+ // where Vulkan could end up using real memory for them. Skia will regularly query the device to
+ // get the real memory usage and update the budgeted appropriately. Though for all real usage
+ // patterns we don't expect to ever trigger the device to allocate real memory.
+ //
+ // Therefore, we set the full maxResourceBytes budget on the Recorder. However, in the rare
+ // chance that the devcies does allocate real memory we don't want to immediately kill device
+ // performance by constantly trashing allocations on the Context. Thus we set the Context's
+ // budget to be 50% of the total budget to make sure we allow the MSAA or Stencil buffers to be
+ // allocated in Skia and not immediately discarded. But even with this extra 50% budget, as
+ // described above, this shouldn't result in actual GPU memory usage.
+ //
+ // TODO: We will need to revise this strategy for GLES which does not have the same memoryless
+ // textures.
+ // TODO: Work in Graphite has started to move a lot more of its scratch resources to be owned
+ // by the Context and not on Recorders. This will mean most memory is actually owned by the
+ // Context and thus the budgeting here will need to be updated.
+ mContext->setMaxBudgetedBytes(maxResourceBytes / 2);
+ mRecorder->setMaxBudgetedBytes(maxResourceBytes);
+}
+
+void GraphiteGpuContext::purgeUnlockedScratchResources() {
+ mContext->freeGpuResources();
+ mRecorder->freeGpuResources();
+}
+
void GraphiteGpuContext::dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const {
mContext->dumpMemoryStatistics(traceMemoryDump);
+ mRecorder->dumpMemoryStatistics(traceMemoryDump);
}
} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/compat/GraphiteGpuContext.h b/libs/renderengine/skia/compat/GraphiteGpuContext.h
index 413817f..57da796 100644
--- a/libs/renderengine/skia/compat/GraphiteGpuContext.h
+++ b/libs/renderengine/skia/compat/GraphiteGpuContext.h
@@ -39,16 +39,10 @@
size_t getMaxRenderTargetSize() const override;
size_t getMaxTextureSize() const override;
bool isAbandonedOrDeviceLost() override;
- // No-op (large resources like textures, surfaces, images, etc. created by clients don't count
- // towards Graphite's internal caching budgets, so adjusting its limits based on display change
- // events should be unnecessary. Additionally, Graphite doesn't expose many cache tweaking
- // functions yet, as its design may evolve.)
- void setResourceCacheLimit(size_t maxResourceBytes) override{};
- // TODO: b/293371537 - Triple-check and validate that no cleanup is necessary when switching
- // contexts.
- // No-op (unnecessary during context switch for Graphite's client-budgeted memory model).
- void purgeUnlockedScratchResources() override{};
+ void setResourceCacheLimit(size_t maxResourceBytes) override;
+ void purgeUnlockedScratchResources() override;
+
// No-op (only applicable to GL).
void resetContextIfApplicable() override{};
diff --git a/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp b/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp
index da47aae..ef57c30 100644
--- a/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp
+++ b/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp
@@ -74,13 +74,6 @@
mBlurEffect = std::move(blurEffect);
}
-static sk_sp<SkSurface> makeSurface(SkiaGpuContext* context, const SkRect& origRect, int scale) {
- SkImageInfo scaledInfo =
- SkImageInfo::MakeN32Premul(ceil(static_cast<float>(origRect.width()) / scale),
- ceil(static_cast<float>(origRect.height()) / scale));
- return context->createRenderTarget(scaledInfo);
-}
-
void KawaseBlurDualFilter::blurInto(const sk_sp<SkSurface>& drawSurface,
const sk_sp<SkImage>& readImage, const float radius,
const float alpha) const {
@@ -124,11 +117,20 @@
const float filterDepth = std::min(kMaxSurfaces - 1.0f, radius * kInputScale / 2.5f);
const int filterPasses = std::min(kMaxSurfaces - 1, static_cast<int>(ceil(filterDepth)));
+ auto makeSurface = [&](float scale) -> sk_sp<SkSurface> {
+ const auto newW = ceil(static_cast<float>(blurRect.width() / scale));
+ const auto newH = ceil(static_cast<float>(blurRect.height() / scale));
+ sk_sp<SkSurface> surface =
+ context->createRenderTarget(input->imageInfo().makeWH(newW, newH));
+ LOG_ALWAYS_FATAL_IF(!surface, "%s: Failed to create surface for blurring!", __func__);
+ return surface;
+ };
+
// Render into surfaces downscaled by 1x, 2x, and 4x from the initial downscale.
sk_sp<SkSurface> surfaces[kMaxSurfaces] =
- {filterPasses >= 0 ? makeSurface(context, blurRect, 1 * kInverseInputScale) : nullptr,
- filterPasses >= 1 ? makeSurface(context, blurRect, 2 * kInverseInputScale) : nullptr,
- filterPasses >= 2 ? makeSurface(context, blurRect, 4 * kInverseInputScale) : nullptr};
+ {filterPasses >= 0 ? makeSurface(1 * kInverseInputScale) : nullptr,
+ filterPasses >= 1 ? makeSurface(2 * kInverseInputScale) : nullptr,
+ filterPasses >= 2 ? makeSurface(4 * kInverseInputScale) : nullptr};
// These weights for scaling offsets per-pass are handpicked to look good at 1 <= radius <= 250.
static const float kWeights[5] = {
diff --git a/libs/tracing_perfetto/Android.bp b/libs/tracing_perfetto/Android.bp
index 8bd0d0f..1ef83a4 100644
--- a/libs/tracing_perfetto/Android.bp
+++ b/libs/tracing_perfetto/Android.bp
@@ -21,7 +21,7 @@
default_applicable_licenses: ["frameworks_native_license"],
}
-cc_library_shared {
+cc_library {
name: "libtracing_perfetto",
export_include_dirs: [
"include",
@@ -37,6 +37,7 @@
srcs: [
"tracing_perfetto.cpp",
"tracing_perfetto_internal.cpp",
+ "tracing_sdk.cpp",
],
shared_libs: [
@@ -45,6 +46,10 @@
"libperfetto_c",
],
+ export_shared_lib_headers: [
+ "libperfetto_c",
+ ],
+
host_supported: true,
// for vndbinder
vendor_available: true,
diff --git a/libs/tracing_perfetto/include/tracing_sdk.h b/libs/tracing_perfetto/include/tracing_sdk.h
new file mode 100644
index 0000000..4a6e849
--- /dev/null
+++ b/libs/tracing_perfetto/include/tracing_sdk.h
@@ -0,0 +1,461 @@
+/*
+ * 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
+
+#include <android-base/logging.h>
+#include <stdint.h>
+
+#include <optional>
+#include <vector>
+
+#include "perfetto/public/producer.h"
+#include "perfetto/public/te_category_macros.h"
+#include "perfetto/public/te_macros.h"
+#include "perfetto/public/track_event.h"
+
+/**
+ * The objects declared here are intended to be managed by Java.
+ * This means the Java Garbage Collector is responsible for freeing the
+ * underlying native resources.
+ *
+ * The static methods prefixed with `delete_` are special. They are designed to be
+ * invoked by Java through the `NativeAllocationRegistry` when the
+ * corresponding Java object becomes unreachable. These methods act as
+ * callbacks to ensure proper deallocation of native resources.
+ */
+namespace tracing_perfetto {
+/**
+ * @brief Represents extra data associated with a trace event.
+ * This class manages a collection of PerfettoTeHlExtra pointers.
+ */
+class Extra;
+
+/**
+ * @brief Emits a trace event.
+ * @param type The type of the event.
+ * @param cat The category of the event.
+ * @param name The name of the event.
+ * @param arg_ptr Pointer to Extra data.
+ */
+void trace_event(int type, const PerfettoTeCategory* cat, const char* name,
+ Extra* extra);
+
+/**
+ * @brief Gets the process track UUID.
+ */
+uint64_t get_process_track_uuid();
+
+/**
+ * @brief Gets the thread track UUID for a given PID.
+ */
+uint64_t get_thread_track_uuid(pid_t tid);
+
+/**
+ * @brief Holder for all the other classes in the file.
+ */
+class Extra {
+ public:
+ Extra();
+ void push_extra(PerfettoTeHlExtra* extra);
+ void pop_extra();
+ void clear_extras();
+ static void delete_extra(Extra* extra);
+
+ PerfettoTeHlExtra* const* get() const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Extra);
+
+ // These PerfettoTeHlExtra pointers are really pointers to all the other
+ // types of extras: Category, DebugArg, Counter etc. Those objects are
+ // individually managed by Java.
+ std::vector<PerfettoTeHlExtra*> extras_;
+};
+
+/**
+ * @brief Represents a trace event category.
+ */
+class Category {
+ public:
+ Category(const std::string& name, const std::string& tag,
+ const std::string& severity);
+
+ ~Category();
+
+ void register_category();
+
+ void unregister_category();
+
+ bool is_category_enabled();
+
+ static void delete_category(Category* category);
+
+ const PerfettoTeCategory* get() const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Category);
+ PerfettoTeCategory category_;
+ const std::string name_;
+ const std::string tag_;
+ const std::string severity_;
+};
+
+/**
+ * @brief Represents one end of a flow between two events.
+ */
+class Flow {
+ public:
+ Flow();
+
+ void set_process_flow(uint64_t id);
+ void set_process_terminating_flow(uint64_t id);
+ static void delete_flow(Flow* flow);
+
+ const PerfettoTeHlExtraFlow* get() const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Flow);
+ PerfettoTeHlExtraFlow flow_;
+};
+
+/**
+ * @brief Represents a named track.
+ */
+class NamedTrack {
+ public:
+ NamedTrack(uint64_t id, uint64_t parent_uuid, const std::string& name);
+
+ static void delete_track(NamedTrack* track);
+
+ const PerfettoTeHlExtraNamedTrack* get() const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NamedTrack);
+ const std::string name_;
+ PerfettoTeHlExtraNamedTrack track_;
+};
+
+/**
+ * @brief Represents a registered track.
+ */
+class RegisteredTrack {
+ public:
+ RegisteredTrack(uint64_t id, uint64_t parent_uuid, const std::string& name,
+ bool is_counter);
+ ~RegisteredTrack();
+
+ void register_track();
+ void unregister_track();
+ static void delete_track(RegisteredTrack* track);
+
+ const PerfettoTeHlExtraRegisteredTrack* get() const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RegisteredTrack);
+ PerfettoTeRegisteredTrack registered_track_;
+ PerfettoTeHlExtraRegisteredTrack track_;
+ const std::string name_;
+ const uint64_t id_;
+ const uint64_t parent_uuid_;
+ const bool is_counter_;
+};
+
+/**
+ * @brief Represents a counter track event.
+ * @tparam T The data type of the counter (int64_t or double).
+ */
+template <typename T>
+class Counter {
+ public:
+ template <typename>
+ struct always_false : std::false_type {};
+
+ struct TypeMap {
+ using type = std::invoke_result_t<decltype([]() {
+ if constexpr (std::is_same_v<T, int64_t>) {
+ return std::type_identity<PerfettoTeHlExtraCounterInt64>{};
+ } else if constexpr (std::is_same_v<T, double>) {
+ return std::type_identity<PerfettoTeHlExtraCounterDouble>{};
+ } else {
+ return std::type_identity<void>{};
+ }
+ })>::type;
+
+ static constexpr int enum_value = []() {
+ if constexpr (std::is_same_v<T, int64_t>) {
+ return PERFETTO_TE_HL_EXTRA_TYPE_COUNTER_INT64;
+ } else if constexpr (std::is_same_v<T, double>) {
+ return PERFETTO_TE_HL_EXTRA_TYPE_COUNTER_DOUBLE;
+ } else {
+ static_assert(always_false<T>::value, "Unsupported type");
+ return 0; // Never reached, just to satisfy return type
+ }
+ }();
+ };
+
+ Counter() {
+ static_assert(!std::is_same_v<typename TypeMap::type, void>,
+ "Unsupported type for Counter");
+
+ typename TypeMap::type counter;
+ counter.header = {TypeMap::enum_value};
+ counter_ = std::move(counter);
+ }
+
+ void set_value(T value) {
+ if constexpr (std::is_same_v<T, int64_t>) {
+ counter_.value = value;
+ } else if constexpr (std::is_same_v<T, double>) {
+ counter_.value = value;
+ }
+ }
+
+ static void delete_counter(Counter* counter) {
+ delete counter;
+ }
+
+ const TypeMap::type* get() const {
+ return &counter_;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Counter);
+ TypeMap::type counter_;
+};
+
+/**
+ * @brief Represents a debug argument for a trace event.
+ * @tparam T The data type of the argument (bool, int64_t, double, const char*).
+ */
+template <typename T>
+class DebugArg {
+ public:
+ template <typename>
+ struct always_false : std::false_type {};
+
+ struct TypeMap {
+ using type = std::invoke_result_t<decltype([]() {
+ if constexpr (std::is_same_v<T, bool>) {
+ return std::type_identity<PerfettoTeHlExtraDebugArgBool>{};
+ } else if constexpr (std::is_same_v<T, int64_t>) {
+ return std::type_identity<PerfettoTeHlExtraDebugArgInt64>{};
+ } else if constexpr (std::is_same_v<T, double>) {
+ return std::type_identity<PerfettoTeHlExtraDebugArgDouble>{};
+ } else if constexpr (std::is_same_v<T, const char*>) {
+ return std::type_identity<PerfettoTeHlExtraDebugArgString>{};
+ } else {
+ return std::type_identity<void>{};
+ }
+ })>::type;
+
+ static constexpr int enum_value = []() {
+ if constexpr (std::is_same_v<T, bool>) {
+ return PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_BOOL;
+ } else if constexpr (std::is_same_v<T, int64_t>) {
+ return PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_INT64;
+ } else if constexpr (std::is_same_v<T, double>) {
+ return PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_DOUBLE;
+ } else if constexpr (std::is_same_v<T, const char*>) {
+ return PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_STRING;
+ } else {
+ static_assert(always_false<T>::value, "Unsupported type");
+ return 0; // Never reached, just to satisfy return type
+ }
+ }();
+ };
+
+ DebugArg(const std::string& name) : name_(name) {
+ static_assert(!std::is_same_v<typename TypeMap::type, void>,
+ "Unsupported type for DebugArg");
+
+ typename TypeMap::type arg;
+ arg.header = {TypeMap::enum_value};
+ arg.name = name_.c_str();
+ arg_ = std::move(arg);
+ }
+
+ ~DebugArg() {
+ free_string_value();
+ }
+
+ void set_value(T value) {
+ if constexpr (std::is_same_v<T, const char*>) {
+ free_string_value();
+ arg_.value = value;
+ } else if constexpr (std::is_same_v<T, int64_t>) {
+ arg_.value = value;
+ } else if constexpr (std::is_same_v<T, bool>) {
+ arg_.value = value;
+ } else if constexpr (std::is_same_v<T, double>) {
+ arg_.value = value;
+ }
+ }
+
+ static void delete_arg(DebugArg* arg) {
+ delete arg;
+ }
+
+ const TypeMap::type* get() const {
+ return &arg_;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DebugArg);
+ TypeMap::type arg_;
+ const std::string name_;
+
+ constexpr void free_string_value() {
+ if constexpr (std::is_same_v<typename TypeMap::type,
+ PerfettoTeHlExtraDebugArgString>) {
+ if (arg_.value) {
+ free((void*)arg_.value);
+ arg_.value = nullptr;
+ }
+ }
+ }
+};
+
+template <typename T>
+class ProtoField {
+ public:
+ template <typename>
+ struct always_false : std::false_type {};
+
+ struct TypeMap {
+ using type = std::invoke_result_t<decltype([]() {
+ if constexpr (std::is_same_v<T, int64_t>) {
+ return std::type_identity<PerfettoTeHlProtoFieldVarInt>{};
+ } else if constexpr (std::is_same_v<T, double>) {
+ return std::type_identity<PerfettoTeHlProtoFieldDouble>{};
+ } else if constexpr (std::is_same_v<T, const char*>) {
+ return std::type_identity<PerfettoTeHlProtoFieldCstr>{};
+ } else {
+ return std::type_identity<void>{};
+ }
+ })>::type;
+
+ static constexpr PerfettoTeHlProtoFieldType enum_value = []() {
+ if constexpr (std::is_same_v<T, int64_t>) {
+ return PERFETTO_TE_HL_PROTO_TYPE_VARINT;
+ } else if constexpr (std::is_same_v<T, double>) {
+ return PERFETTO_TE_HL_PROTO_TYPE_DOUBLE;
+ } else if constexpr (std::is_same_v<T, const char*>) {
+ return PERFETTO_TE_HL_PROTO_TYPE_CSTR;
+ } else {
+ static_assert(always_false<T>::value, "Unsupported type");
+ return 0; // Never reached, just to satisfy return type
+ }
+ }();
+ };
+
+ ProtoField() {
+ static_assert(!std::is_same_v<typename TypeMap::type, void>,
+ "Unsupported type for ProtoField");
+
+ typename TypeMap::type arg;
+ arg.header.type = TypeMap::enum_value;
+ arg_ = std::move(arg);
+ }
+
+ ~ProtoField() {
+ free_string_value();
+ }
+
+ void set_value(uint32_t id, T value) {
+ if constexpr (std::is_same_v<T, int64_t>) {
+ arg_.header.id = id;
+ arg_.value = value;
+ } else if constexpr (std::is_same_v<T, double>) {
+ arg_.header.id = id;
+ arg_.value = value;
+ } else if constexpr (std::is_same_v<T, const char*>) {
+ free_string_value();
+ arg_.header.id = id;
+ arg_.str = value;
+ }
+ }
+
+ static void delete_field(ProtoField* field) {
+ delete field;
+ }
+
+ const TypeMap::type* get() const {
+ return &arg_;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ProtoField);
+ TypeMap::type arg_;
+
+ constexpr void free_string_value() {
+ if constexpr (std::is_same_v<typename TypeMap::type,
+ PerfettoTeHlProtoFieldCstr>) {
+ if (arg_.str) {
+ free((void*)arg_.str);
+ arg_.str = nullptr;
+ }
+ }
+ }
+};
+
+class ProtoFieldNested {
+ public:
+ ProtoFieldNested();
+
+ void add_field(PerfettoTeHlProtoField* field);
+ void set_id(uint32_t id);
+ static void delete_field(ProtoFieldNested* field);
+
+ const PerfettoTeHlProtoFieldNested* get() const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ProtoFieldNested);
+ PerfettoTeHlProtoFieldNested field_;
+ // These PerfettoTeHlProtoField pointers are really pointers to all the other
+ // types of protos: PerfettoTeHlProtoFieldVarInt, PerfettoTeHlProtoFieldVarInt,
+ // PerfettoTeHlProtoFieldVarInt, PerfettoTeHlProtoFieldNested. Those objects are
+ // individually managed by Java.
+ std::vector<PerfettoTeHlProtoField*> fields_;
+};
+
+class Proto {
+ public:
+ Proto();
+
+ void add_field(PerfettoTeHlProtoField* field);
+ void clear_fields();
+ static void delete_proto(Proto* proto);
+
+ const PerfettoTeHlExtraProtoFields* get() const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Proto);
+ PerfettoTeHlExtraProtoFields proto_;
+ // These PerfettoTeHlProtoField pointers are really pointers to all the other
+ // types of protos: PerfettoTeHlProtoFieldVarInt, PerfettoTeHlProtoFieldVarInt,
+ // PerfettoTeHlProtoFieldVarInt, PerfettoTeHlProtoFieldNested. Those objects are
+ // individually managed by Java.
+ std::vector<PerfettoTeHlProtoField*> fields_;
+};
+
+/**
+ * @brief Activates a trigger.
+ * @param name The name of the trigger.
+ * @param ttl_ms The time-to-live of the trigger in milliseconds.
+ */
+void activate_trigger(const char* name, uint32_t ttl_ms);
+} // namespace tracing_perfetto
diff --git a/libs/tracing_perfetto/tests/Android.bp b/libs/tracing_perfetto/tests/Android.bp
index d203467..0dab517 100644
--- a/libs/tracing_perfetto/tests/Android.bp
+++ b/libs/tracing_perfetto/tests/Android.bp
@@ -21,12 +21,44 @@
default_applicable_licenses: ["frameworks_native_license"],
}
+cc_library_static {
+ name: "libtracing_perfetto_test_utils",
+ export_include_dirs: [
+ "include",
+ ],
+ static_libs: [
+ "libflagtest",
+ "libgmock",
+ "perfetto_trace_protos",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-enum-compare",
+ "-Wno-unused-function",
+ ],
+
+ srcs: [
+ "utils.cpp",
+ ],
+
+ shared_libs: [
+ "libperfetto_c",
+ "liblog",
+ "libprotobuf-cpp-lite",
+ ],
+ export_shared_lib_headers: [
+ "libperfetto_c",
+ ],
+}
+
cc_test {
name: "libtracing_perfetto_tests",
static_libs: [
"libflagtest",
"libgmock",
"perfetto_trace_protos",
+ "libtracing_perfetto_test_utils",
],
cflags: [
"-Wall",
@@ -42,7 +74,6 @@
],
srcs: [
"tracing_perfetto_test.cpp",
- "utils.cpp",
],
test_suites: ["device-tests"],
}
diff --git a/libs/tracing_perfetto/tests/include/utils.h b/libs/tracing_perfetto/tests/include/utils.h
new file mode 100644
index 0000000..b2630e1
--- /dev/null
+++ b/libs/tracing_perfetto/tests/include/utils.h
@@ -0,0 +1,124 @@
+/*
+ * 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.
+ */
+
+// Copied from //external/perfetto/src/shared_lib/test/utils.h
+
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <cassert>
+#include <condition_variable>
+#include <cstdint>
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <mutex>
+#include <ostream>
+#include <string>
+#include <vector>
+
+#include "perfetto/public/abi/pb_decoder_abi.h"
+#include "perfetto/public/pb_utils.h"
+#include "perfetto/public/tracing_session.h"
+
+// Pretty printer for gtest
+void PrintTo(const PerfettoPbDecoderField& field, std::ostream*);
+
+namespace perfetto {
+namespace shlib {
+namespace test_utils {
+
+class WaitableEvent {
+ public:
+ WaitableEvent() = default;
+ void Notify() {
+ std::unique_lock<std::mutex> lock(m_);
+ notified_ = true;
+ cv_.notify_one();
+ }
+ bool WaitForNotification() {
+ std::unique_lock<std::mutex> lock(m_);
+ cv_.wait(lock, [this] { return notified_; });
+ return notified_;
+ }
+ bool IsNotified() {
+ std::unique_lock<std::mutex> lock(m_);
+ return notified_;
+ }
+
+ private:
+ std::mutex m_;
+ std::condition_variable cv_;
+ bool notified_ = false;
+};
+
+class TracingSession {
+ public:
+ class Builder {
+ public:
+ Builder() = default;
+ Builder& add_enabled_category(std::string category) {
+ enabled_categories_.push_back(std::move(category));
+ return *this;
+ }
+ Builder& add_disabled_category(std::string category) {
+ disabled_categories_.push_back(std::move(category));
+ return *this;
+ }
+ Builder& add_atrace_category(std::string category) {
+ atrace_categories_.push_back(std::move(category));
+ return *this;
+ }
+ Builder& add_atrace_category_prefer_sdk(std::string category) {
+ atrace_categories_prefer_sdk_.push_back(std::move(category));
+ return *this;
+ }
+ TracingSession Build();
+
+ private:
+ std::vector<std::string> enabled_categories_;
+ std::vector<std::string> disabled_categories_;
+ std::vector<std::string> atrace_categories_;
+ std::vector<std::string> atrace_categories_prefer_sdk_;
+ };
+
+ static TracingSession Adopt(struct PerfettoTracingSessionImpl*);
+ static TracingSession FromBytes(void *buf, size_t len);
+
+ TracingSession(TracingSession&&) noexcept;
+
+ ~TracingSession();
+
+ struct PerfettoTracingSessionImpl* session() const {
+ return session_;
+ }
+
+ bool FlushBlocking(uint32_t timeout_ms);
+ void WaitForStopped();
+ void StopBlocking();
+ std::vector<uint8_t> ReadBlocking();
+
+ private:
+ TracingSession() = default;
+ struct PerfettoTracingSessionImpl* session_;
+ std::unique_ptr<WaitableEvent> stopped_;
+};
+
+} // namespace test_utils
+} // namespace shlib
+} // namespace perfetto
+
+#endif // UTILS_H
diff --git a/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp b/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp
index e9fee2e..b21a090 100644
--- a/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp
+++ b/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp
@@ -22,6 +22,7 @@
#include <unistd.h>
#include "gtest/gtest.h"
+
#include "perfetto/public/abi/data_source_abi.h"
#include "perfetto/public/abi/heap_buffer.h"
#include "perfetto/public/abi/pb_decoder_abi.h"
diff --git a/libs/tracing_perfetto/tests/utils.cpp b/libs/tracing_perfetto/tests/utils.cpp
index 8c4d4a8..af61bc2 100644
--- a/libs/tracing_perfetto/tests/utils.cpp
+++ b/libs/tracing_perfetto/tests/utils.cpp
@@ -34,36 +34,17 @@
namespace perfetto {
namespace shlib {
namespace test_utils {
-namespace {
-
-std::string ToHexChars(uint8_t val) {
- std::string ret;
- uint8_t high_nibble = (val & 0xF0) >> 4;
- uint8_t low_nibble = (val & 0xF);
- static const char hex_chars[] = "0123456789ABCDEF";
- ret.push_back(hex_chars[high_nibble]);
- ret.push_back(hex_chars[low_nibble]);
- return ret;
-}
-
-} // namespace
-
TracingSession TracingSession::Builder::Build() {
perfetto::protos::TraceConfig trace_config;
trace_config.add_buffers()->set_size_kb(1024);
- auto* track_event_ds_config = trace_config.add_data_sources()->mutable_config();
- auto* ftrace_ds_config = trace_config.add_data_sources()->mutable_config();
-
- track_event_ds_config->set_name("track_event");
- track_event_ds_config->set_target_buffer(0);
-
- ftrace_ds_config->set_name("linux.ftrace");
- ftrace_ds_config->set_target_buffer(0);
-
{
- auto* ftrace_config = ftrace_ds_config->mutable_ftrace_config();
if (!atrace_categories_.empty()) {
+ auto* ftrace_ds_config = trace_config.add_data_sources()->mutable_config();
+ ftrace_ds_config->set_name("linux.ftrace");
+ ftrace_ds_config->set_target_buffer(0);
+
+ auto* ftrace_config = ftrace_ds_config->mutable_ftrace_config();
ftrace_config->add_ftrace_events("ftrace/print");
for (const std::string& cat : atrace_categories_) {
ftrace_config->add_atrace_categories(cat);
@@ -76,8 +57,14 @@
}
{
- auto* track_event_config = track_event_ds_config->mutable_track_event_config();
if (!enabled_categories_.empty() || !disabled_categories_.empty()) {
+ auto* track_event_ds_config = trace_config.add_data_sources()->mutable_config();
+
+ track_event_ds_config->set_name("track_event");
+ track_event_ds_config->set_target_buffer(0);
+
+ auto* track_event_config = track_event_ds_config->mutable_track_event_config();
+
for (const std::string& cat : enabled_categories_) {
track_event_config->add_enabled_categories(cat);
}
@@ -88,13 +75,17 @@
}
}
- struct PerfettoTracingSessionImpl* ts =
- PerfettoTracingSessionCreate(PERFETTO_BACKEND_SYSTEM);
-
std::string trace_config_string;
trace_config.SerializeToString(&trace_config_string);
- PerfettoTracingSessionSetup(ts, trace_config_string.data(), trace_config_string.length());
+ return TracingSession::FromBytes(trace_config_string.data(), trace_config_string.length());
+}
+
+TracingSession TracingSession::FromBytes(void *buf, size_t len) {
+ struct PerfettoTracingSessionImpl* ts =
+ PerfettoTracingSessionCreate(PERFETTO_BACKEND_SYSTEM);
+
+ PerfettoTracingSessionSetup(ts, buf, len);
// Fails to start here
PerfettoTracingSessionStartBlocking(ts);
@@ -177,39 +168,3 @@
} // namespace test_utils
} // namespace shlib
} // namespace perfetto
-
-void PrintTo(const PerfettoPbDecoderField& field, std::ostream* pos) {
- std::ostream& os = *pos;
- PerfettoPbDecoderStatus status =
- static_cast<PerfettoPbDecoderStatus>(field.status);
- switch (status) {
- case PERFETTO_PB_DECODER_ERROR:
- os << "MALFORMED PROTOBUF";
- break;
- case PERFETTO_PB_DECODER_DONE:
- os << "DECODER DONE";
- break;
- case PERFETTO_PB_DECODER_OK:
- switch (field.wire_type) {
- case PERFETTO_PB_WIRE_TYPE_DELIMITED:
- os << "\"";
- for (size_t i = 0; i < field.value.delimited.len; i++) {
- os << perfetto::shlib::test_utils::ToHexChars(
- field.value.delimited.start[i])
- << " ";
- }
- os << "\"";
- break;
- case PERFETTO_PB_WIRE_TYPE_VARINT:
- os << "varint: " << field.value.integer64;
- break;
- case PERFETTO_PB_WIRE_TYPE_FIXED32:
- os << "fixed32: " << field.value.integer32;
- break;
- case PERFETTO_PB_WIRE_TYPE_FIXED64:
- os << "fixed64: " << field.value.integer64;
- break;
- }
- break;
- }
-}
diff --git a/libs/tracing_perfetto/tests/utils.h b/libs/tracing_perfetto/tests/utils.h
deleted file mode 100644
index 8edb414..0000000
--- a/libs/tracing_perfetto/tests/utils.h
+++ /dev/null
@@ -1,457 +0,0 @@
-/*
- * 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.
- */
-
-// Copied from //external/perfetto/src/shared_lib/test/utils.h
-
-#ifndef UTILS_H
-#define UTILS_H
-
-#include <cassert>
-#include <condition_variable>
-#include <cstdint>
-#include <functional>
-#include <iterator>
-#include <memory>
-#include <mutex>
-#include <ostream>
-#include <string>
-#include <vector>
-
-#include "gmock/gmock-matchers.h"
-#include "gmock/gmock-more-matchers.h"
-#include "gtest/gtest-matchers.h"
-#include "gtest/gtest.h"
-#include "perfetto/public/abi/pb_decoder_abi.h"
-#include "perfetto/public/pb_utils.h"
-#include "perfetto/public/tracing_session.h"
-
-// Pretty printer for gtest
-void PrintTo(const PerfettoPbDecoderField& field, std::ostream*);
-
-namespace perfetto {
-namespace shlib {
-namespace test_utils {
-
-class WaitableEvent {
- public:
- WaitableEvent() = default;
- void Notify() {
- std::unique_lock<std::mutex> lock(m_);
- notified_ = true;
- cv_.notify_one();
- }
- bool WaitForNotification() {
- std::unique_lock<std::mutex> lock(m_);
- cv_.wait(lock, [this] { return notified_; });
- return notified_;
- }
- bool IsNotified() {
- std::unique_lock<std::mutex> lock(m_);
- return notified_;
- }
-
- private:
- std::mutex m_;
- std::condition_variable cv_;
- bool notified_ = false;
-};
-
-class TracingSession {
- public:
- class Builder {
- public:
- Builder() = default;
- Builder& add_enabled_category(std::string category) {
- enabled_categories_.push_back(std::move(category));
- return *this;
- }
- Builder& add_disabled_category(std::string category) {
- disabled_categories_.push_back(std::move(category));
- return *this;
- }
- Builder& add_atrace_category(std::string category) {
- atrace_categories_.push_back(std::move(category));
- return *this;
- }
- Builder& add_atrace_category_prefer_sdk(std::string category) {
- atrace_categories_prefer_sdk_.push_back(std::move(category));
- return *this;
- }
- TracingSession Build();
-
- private:
- std::vector<std::string> enabled_categories_;
- std::vector<std::string> disabled_categories_;
- std::vector<std::string> atrace_categories_;
- std::vector<std::string> atrace_categories_prefer_sdk_;
- };
-
- static TracingSession Adopt(struct PerfettoTracingSessionImpl*);
-
- TracingSession(TracingSession&&) noexcept;
-
- ~TracingSession();
-
- struct PerfettoTracingSessionImpl* session() const {
- return session_;
- }
-
- bool FlushBlocking(uint32_t timeout_ms);
- void WaitForStopped();
- void StopBlocking();
- std::vector<uint8_t> ReadBlocking();
-
- private:
- TracingSession() = default;
- struct PerfettoTracingSessionImpl* session_;
- std::unique_ptr<WaitableEvent> stopped_;
-};
-
-template <typename FieldSkipper>
-class FieldViewBase {
- public:
- class Iterator {
- public:
- using iterator_category = std::input_iterator_tag;
- using value_type = const PerfettoPbDecoderField;
- using pointer = value_type;
- using reference = value_type;
- reference operator*() const {
- struct PerfettoPbDecoder decoder;
- decoder.read_ptr = read_ptr_;
- decoder.end_ptr = end_ptr_;
- struct PerfettoPbDecoderField field;
- do {
- field = PerfettoPbDecoderParseField(&decoder);
- } while (field.status == PERFETTO_PB_DECODER_OK &&
- skipper_.ShouldSkip(field));
- return field;
- }
- Iterator& operator++() {
- struct PerfettoPbDecoder decoder;
- decoder.read_ptr = read_ptr_;
- decoder.end_ptr = end_ptr_;
- PerfettoPbDecoderSkipField(&decoder);
- read_ptr_ = decoder.read_ptr;
- AdvanceToFirstInterestingField();
- return *this;
- }
- Iterator operator++(int) {
- Iterator tmp = *this;
- ++(*this);
- return tmp;
- }
-
- friend bool operator==(const Iterator& a, const Iterator& b) {
- return a.read_ptr_ == b.read_ptr_;
- }
- friend bool operator!=(const Iterator& a, const Iterator& b) {
- return a.read_ptr_ != b.read_ptr_;
- }
-
- private:
- Iterator(const uint8_t* read_ptr, const uint8_t* end_ptr,
- const FieldSkipper& skipper)
- : read_ptr_(read_ptr), end_ptr_(end_ptr), skipper_(skipper) {
- AdvanceToFirstInterestingField();
- }
- void AdvanceToFirstInterestingField() {
- struct PerfettoPbDecoder decoder;
- decoder.read_ptr = read_ptr_;
- decoder.end_ptr = end_ptr_;
- struct PerfettoPbDecoderField field;
- const uint8_t* prev_read_ptr;
- do {
- prev_read_ptr = decoder.read_ptr;
- field = PerfettoPbDecoderParseField(&decoder);
- } while (field.status == PERFETTO_PB_DECODER_OK &&
- skipper_.ShouldSkip(field));
- if (field.status == PERFETTO_PB_DECODER_OK) {
- read_ptr_ = prev_read_ptr;
- } else {
- read_ptr_ = decoder.read_ptr;
- }
- }
- friend class FieldViewBase<FieldSkipper>;
- const uint8_t* read_ptr_;
- const uint8_t* end_ptr_;
- const FieldSkipper& skipper_;
- };
- using value_type = const PerfettoPbDecoderField;
- using const_iterator = Iterator;
- template <typename... Args>
- explicit FieldViewBase(const uint8_t* begin, const uint8_t* end, Args... args)
- : begin_(begin), end_(end), s_(args...) {
- }
- template <typename... Args>
- explicit FieldViewBase(const std::vector<uint8_t>& data, Args... args)
- : FieldViewBase(data.data(), data.data() + data.size(), args...) {
- }
- template <typename... Args>
- explicit FieldViewBase(const struct PerfettoPbDecoderField& field,
- Args... args)
- : s_(args...) {
- if (field.wire_type != PERFETTO_PB_WIRE_TYPE_DELIMITED) {
- abort();
- }
- begin_ = field.value.delimited.start;
- end_ = begin_ + field.value.delimited.len;
- }
- Iterator begin() const {
- return Iterator(begin_, end_, s_);
- }
- Iterator end() const {
- return Iterator(end_, end_, s_);
- }
- PerfettoPbDecoderField front() const {
- return *begin();
- }
-
- size_t size() const {
- size_t count = 0;
- for (auto field : *this) {
- (void)field;
- count++;
- }
- return count;
- }
-
- bool ok() const {
- for (auto field : *this) {
- if (field.status != PERFETTO_PB_DECODER_OK) {
- return false;
- }
- }
- return true;
- }
-
- private:
- const uint8_t* begin_;
- const uint8_t* end_;
- FieldSkipper s_;
-};
-
-// Pretty printer for gtest
-template <typename FieldSkipper>
-void PrintTo(const FieldViewBase<FieldSkipper>& field_view, std::ostream* pos) {
- std::ostream& os = *pos;
- os << "{";
- for (PerfettoPbDecoderField f : field_view) {
- PrintTo(f, pos);
- os << ", ";
- }
- os << "}";
-}
-
-class IdFieldSkipper {
- public:
- explicit IdFieldSkipper(uint32_t id) : id_(id) {
- }
- explicit IdFieldSkipper(int32_t id) : id_(static_cast<uint32_t>(id)) {
- }
- bool ShouldSkip(const struct PerfettoPbDecoderField& field) const {
- return field.id != id_;
- }
-
- private:
- uint32_t id_;
-};
-
-class NoFieldSkipper {
- public:
- NoFieldSkipper() = default;
- bool ShouldSkip(const struct PerfettoPbDecoderField&) const {
- return false;
- }
-};
-
-// View over all the fields of a contiguous serialized protobuf message.
-//
-// Examples:
-//
-// for (struct PerfettoPbDecoderField field : FieldView(msg_begin, msg_end)) {
-// //...
-// }
-// FieldView fields2(/*PerfettoPbDecoderField*/ nested_field);
-// FieldView fields3(/*std::vector<uint8_t>*/ data);
-// size_t num = fields1.size(); // The number of fields.
-// bool ok = fields1.ok(); // Checks that the message is not malformed.
-using FieldView = FieldViewBase<NoFieldSkipper>;
-
-// Like `FieldView`, but only considers fields with a specific id.
-//
-// Examples:
-//
-// IdFieldView fields(msg_begin, msg_end, id)
-using IdFieldView = FieldViewBase<IdFieldSkipper>;
-
-// Matches a PerfettoPbDecoderField with the specified id. Accepts another
-// matcher to match the contents of the field.
-//
-// Example:
-// PerfettoPbDecoderField field = ...
-// EXPECT_THAT(field, PbField(900, VarIntField(5)));
-template <typename M>
-auto PbField(int32_t id, M m) {
- return testing::AllOf(
- testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK),
- testing::Field(&PerfettoPbDecoderField::id, id), m);
-}
-
-// Matches a PerfettoPbDecoderField submessage field. Accepts a container
-// matcher for the subfields.
-//
-// Example:
-// PerfettoPbDecoderField field = ...
-// EXPECT_THAT(field, MsgField(ElementsAre(...)));
-template <typename M>
-auto MsgField(M m) {
- auto f = [](const PerfettoPbDecoderField& field) { return FieldView(field); };
- return testing::AllOf(
- testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK),
- testing::Field(&PerfettoPbDecoderField::wire_type,
- PERFETTO_PB_WIRE_TYPE_DELIMITED),
- testing::ResultOf(f, m));
-}
-
-// Matches a PerfettoPbDecoderField length delimited field. Accepts a string
-// matcher.
-//
-// Example:
-// PerfettoPbDecoderField field = ...
-// EXPECT_THAT(field, StringField("string"));
-template <typename M>
-auto StringField(M m) {
- auto f = [](const PerfettoPbDecoderField& field) {
- return std::string(
- reinterpret_cast<const char*>(field.value.delimited.start),
- field.value.delimited.len);
- };
- return testing::AllOf(
- testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK),
- testing::Field(&PerfettoPbDecoderField::wire_type,
- PERFETTO_PB_WIRE_TYPE_DELIMITED),
- testing::ResultOf(f, m));
-}
-
-// Matches a PerfettoPbDecoderField VarInt field. Accepts an integer matcher
-//
-// Example:
-// PerfettoPbDecoderField field = ...
-// EXPECT_THAT(field, VarIntField(1)));
-template <typename M>
-auto VarIntField(M m) {
- auto f = [](const PerfettoPbDecoderField& field) {
- return field.value.integer64;
- };
- return testing::AllOf(
- testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK),
- testing::Field(&PerfettoPbDecoderField::wire_type,
- PERFETTO_PB_WIRE_TYPE_VARINT),
- testing::ResultOf(f, m));
-}
-
-// Matches a PerfettoPbDecoderField fixed64 field. Accepts an integer matcher
-//
-// Example:
-// PerfettoPbDecoderField field = ...
-// EXPECT_THAT(field, Fixed64Field(1)));
-template <typename M>
-auto Fixed64Field(M m) {
- auto f = [](const PerfettoPbDecoderField& field) {
- return field.value.integer64;
- };
- return testing::AllOf(
- testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK),
- testing::Field(&PerfettoPbDecoderField::wire_type,
- PERFETTO_PB_WIRE_TYPE_FIXED64),
- testing::ResultOf(f, m));
-}
-
-// Matches a PerfettoPbDecoderField fixed32 field. Accepts an integer matcher
-//
-// Example:
-// PerfettoPbDecoderField field = ...
-// EXPECT_THAT(field, Fixed32Field(1)));
-template <typename M>
-auto Fixed32Field(M m) {
- auto f = [](const PerfettoPbDecoderField& field) {
- return field.value.integer32;
- };
- return testing::AllOf(
- testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK),
- testing::Field(&PerfettoPbDecoderField::wire_type,
- PERFETTO_PB_WIRE_TYPE_FIXED32),
- testing::ResultOf(f, m));
-}
-
-// Matches a PerfettoPbDecoderField double field. Accepts a double matcher
-//
-// Example:
-// PerfettoPbDecoderField field = ...
-// EXPECT_THAT(field, DoubleField(1.0)));
-template <typename M>
-auto DoubleField(M m) {
- auto f = [](const PerfettoPbDecoderField& field) {
- return field.value.double_val;
- };
- return testing::AllOf(
- testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK),
- testing::Field(&PerfettoPbDecoderField::wire_type,
- PERFETTO_PB_WIRE_TYPE_FIXED64),
- testing::ResultOf(f, m));
-}
-
-// Matches a PerfettoPbDecoderField float field. Accepts a float matcher
-//
-// Example:
-// PerfettoPbDecoderField field = ...
-// EXPECT_THAT(field, FloatField(1.0)));
-template <typename M>
-auto FloatField(M m) {
- auto f = [](const PerfettoPbDecoderField& field) {
- return field.value.float_val;
- };
- return testing::AllOf(
- testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK),
- testing::Field(&PerfettoPbDecoderField::wire_type,
- PERFETTO_PB_WIRE_TYPE_FIXED32),
- testing::ResultOf(f, m));
-}
-
-// Matches a PerfettoPbDecoderField submessage field. Accepts a container
-// matcher for the subfields.
-//
-// Example:
-// PerfettoPbDecoderField field = ...
-// EXPECT_THAT(field, AllFieldsWithId(900, ElementsAre(...)));
-template <typename M>
-auto AllFieldsWithId(int32_t id, M m) {
- auto f = [id](const PerfettoPbDecoderField& field) {
- return IdFieldView(field, id);
- };
- return testing::AllOf(
- testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK),
- testing::Field(&PerfettoPbDecoderField::wire_type,
- PERFETTO_PB_WIRE_TYPE_DELIMITED),
- testing::ResultOf(f, m));
-}
-
-} // namespace test_utils
-} // namespace shlib
-} // namespace perfetto
-
-#endif // UTILS_H
diff --git a/libs/tracing_perfetto/tracing_perfetto.cpp b/libs/tracing_perfetto/tracing_perfetto.cpp
index c35e078..4b70213 100644
--- a/libs/tracing_perfetto/tracing_perfetto.cpp
+++ b/libs/tracing_perfetto/tracing_perfetto.cpp
@@ -17,6 +17,7 @@
#include "tracing_perfetto.h"
#include <cutils/trace.h>
+
#include <cstdarg>
#include "perfetto/public/te_category_macros.h"
@@ -43,8 +44,10 @@
void traceFormatBegin(uint64_t category, const char* fmt, ...) {
struct PerfettoTeCategory* perfettoTeCategory =
internal::toPerfettoCategory(category);
- const bool preferAtrace = internal::shouldPreferAtrace(perfettoTeCategory, category);
- const bool preferPerfetto = internal::isPerfettoCategoryEnabled(perfettoTeCategory);
+ const bool preferAtrace =
+ internal::shouldPreferAtrace(perfettoTeCategory, category);
+ const bool preferPerfetto =
+ internal::isPerfettoCategoryEnabled(perfettoTeCategory);
if (CC_LIKELY(!(preferAtrace || preferPerfetto))) {
return;
}
@@ -57,7 +60,6 @@
vsnprintf(buf, BUFFER_SIZE, fmt, ap);
va_end(ap);
-
if (preferAtrace) {
atrace_begin(category, buf);
} else if (preferPerfetto) {
@@ -99,26 +101,28 @@
}
void traceAsyncBeginForTrack(uint64_t category, const char* name,
- const char* trackName, int32_t cookie) {
+ const char* trackName, int32_t cookie) {
struct PerfettoTeCategory* perfettoTeCategory =
internal::toPerfettoCategory(category);
if (internal::shouldPreferAtrace(perfettoTeCategory, category)) {
atrace_async_for_track_begin(category, trackName, name, cookie);
} else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) {
- internal::perfettoTraceAsyncBeginForTrack(*perfettoTeCategory, name, trackName, cookie);
+ internal::perfettoTraceAsyncBeginForTrack(*perfettoTeCategory, name,
+ trackName, cookie);
}
}
void traceAsyncEndForTrack(uint64_t category, const char* trackName,
- int32_t cookie) {
+ int32_t cookie) {
struct PerfettoTeCategory* perfettoTeCategory =
internal::toPerfettoCategory(category);
if (internal::shouldPreferAtrace(perfettoTeCategory, category)) {
atrace_async_for_track_end(category, trackName, cookie);
} else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) {
- internal::perfettoTraceAsyncEndForTrack(*perfettoTeCategory, trackName, cookie);
+ internal::perfettoTraceAsyncEndForTrack(*perfettoTeCategory, trackName,
+ cookie);
}
}
@@ -136,8 +140,10 @@
void traceFormatInstant(uint64_t category, const char* fmt, ...) {
struct PerfettoTeCategory* perfettoTeCategory =
internal::toPerfettoCategory(category);
- const bool preferAtrace = internal::shouldPreferAtrace(perfettoTeCategory, category);
- const bool preferPerfetto = internal::isPerfettoCategoryEnabled(perfettoTeCategory);
+ const bool preferAtrace =
+ internal::shouldPreferAtrace(perfettoTeCategory, category);
+ const bool preferPerfetto =
+ internal::isPerfettoCategoryEnabled(perfettoTeCategory);
if (CC_LIKELY(!(preferAtrace || preferPerfetto))) {
return;
}
@@ -158,7 +164,7 @@
}
void traceInstantForTrack(uint64_t category, const char* trackName,
- const char* name) {
+ const char* name) {
struct PerfettoTeCategory* perfettoTeCategory =
internal::toPerfettoCategory(category);
@@ -181,20 +187,21 @@
}
void traceCounter32(uint64_t category, const char* name, int32_t value) {
- struct PerfettoTeCategory* perfettoTeCategory = internal::toPerfettoCategory(category);
+ struct PerfettoTeCategory* perfettoTeCategory =
+ internal::toPerfettoCategory(category);
if (internal::shouldPreferAtrace(perfettoTeCategory, category)) {
atrace_int(category, name, value);
} else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) {
internal::perfettoTraceCounter(*perfettoTeCategory, name,
- static_cast<int64_t>(value));
+ static_cast<int64_t>(value));
}
}
bool isTagEnabled(uint64_t category) {
struct PerfettoTeCategory* perfettoTeCategory =
internal::toPerfettoCategory(category);
- return internal::isPerfettoCategoryEnabled(perfettoTeCategory)
- || atrace_is_tag_enabled(category);
+ return internal::isPerfettoCategoryEnabled(perfettoTeCategory) ||
+ atrace_is_tag_enabled(category);
}
} // namespace tracing_perfetto
diff --git a/libs/tracing_perfetto/tracing_sdk.cpp b/libs/tracing_perfetto/tracing_sdk.cpp
new file mode 100644
index 0000000..02e8d10
--- /dev/null
+++ b/libs/tracing_perfetto/tracing_sdk.cpp
@@ -0,0 +1,261 @@
+/*
+ * 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 "tracing_sdk.h"
+
+#include <android-base/logging.h>
+#include <cutils/trace.h>
+
+#include <cstdarg>
+#include <cstdlib>
+
+#include "perfetto/public/abi/producer_abi.h"
+#include "perfetto/public/te_category_macros.h"
+#include "perfetto/public/te_macros.h"
+#include "perfetto/public/track_event.h"
+#include "tracing_perfetto.h"
+
+namespace tracing_perfetto {
+void trace_event(int type, const PerfettoTeCategory* perfettoTeCategory,
+ const char* name, tracing_perfetto::Extra* extra) {
+ bool enabled = PERFETTO_UNLIKELY(PERFETTO_ATOMIC_LOAD_EXPLICIT(
+ perfettoTeCategory->enabled, PERFETTO_MEMORY_ORDER_RELAXED));
+ if (enabled) {
+ extra->push_extra(nullptr);
+ PerfettoTeHlEmitImpl(perfettoTeCategory->impl, type,
+ type == PERFETTO_TE_TYPE_COUNTER ? nullptr : name,
+ extra->get());
+ extra->pop_extra();
+ }
+}
+
+uint64_t get_process_track_uuid() {
+ return PerfettoTeProcessTrackUuid();
+}
+
+uint64_t get_thread_track_uuid(pid_t tid) {
+ // Cating a signed pid_t to unsigned
+ return PerfettoTeProcessTrackUuid() ^ PERFETTO_STATIC_CAST(uint64_t, tid);
+}
+
+Extra::Extra() {
+}
+
+void Extra::push_extra(PerfettoTeHlExtra* ptr) {
+ extras_.push_back(ptr);
+}
+
+void Extra::pop_extra() {
+ extras_.pop_back();
+}
+
+void Extra::clear_extras() {
+ extras_.clear();
+}
+
+void Extra::delete_extra(Extra* ptr) {
+ delete ptr;
+}
+
+PerfettoTeHlExtra* const* Extra::get() const {
+ return extras_.data();
+}
+
+Category::Category(const std::string& name, const std::string& tag,
+ const std::string& severity)
+ : category_({.enabled = &perfetto_atomic_false}),
+ name_(name),
+ tag_(tag),
+ severity_(severity) {
+}
+
+Category::~Category() {
+ unregister_category();
+}
+
+void Category::register_category() {
+ if (category_.impl) return;
+
+ std::vector<const char*> tags;
+ if (!tag_.empty()) tags.push_back(tag_.data());
+ if (!severity_.empty()) tags.push_back(severity_.data());
+
+ category_.desc = {name_.c_str(), name_.c_str(), tags.data(), tags.size()};
+
+ PerfettoTeCategoryRegister(&category_);
+ PerfettoTePublishCategories();
+}
+
+void Category::unregister_category() {
+ if (!category_.impl) return;
+
+ PerfettoTeCategoryUnregister(&category_);
+ PerfettoTePublishCategories();
+}
+
+bool Category::is_category_enabled() {
+ return PERFETTO_UNLIKELY(PERFETTO_ATOMIC_LOAD_EXPLICIT(
+ (category_).enabled, PERFETTO_MEMORY_ORDER_RELAXED));
+}
+
+const PerfettoTeCategory* Category::get() const {
+ return &category_;
+}
+
+void Category::delete_category(Category* ptr) {
+ delete ptr;
+}
+
+Flow::Flow() : flow_{} {
+}
+
+void Flow::set_process_flow(uint64_t id) {
+ flow_.header.type = PERFETTO_TE_HL_EXTRA_TYPE_FLOW;
+ PerfettoTeFlow ret = PerfettoTeProcessScopedFlow(id);
+ flow_.id = ret.id;
+}
+
+void Flow::set_process_terminating_flow(uint64_t id) {
+ flow_.header.type = PERFETTO_TE_HL_EXTRA_TYPE_TERMINATING_FLOW;
+ PerfettoTeFlow ret = PerfettoTeProcessScopedFlow(id);
+ flow_.id = ret.id;
+}
+
+const PerfettoTeHlExtraFlow* Flow::get() const {
+ return &flow_;
+}
+
+void Flow::delete_flow(Flow* ptr) {
+ delete ptr;
+}
+
+NamedTrack::NamedTrack(uint64_t id, uint64_t parent_uuid,
+ const std::string& name)
+ : name_(name),
+ track_{{PERFETTO_TE_HL_EXTRA_TYPE_NAMED_TRACK},
+ name_.data(),
+ id,
+ parent_uuid} {
+}
+
+const PerfettoTeHlExtraNamedTrack* NamedTrack::get() const {
+ return &track_;
+}
+
+void NamedTrack::delete_track(NamedTrack* ptr) {
+ delete ptr;
+}
+
+RegisteredTrack::RegisteredTrack(uint64_t id, uint64_t parent_uuid,
+ const std::string& name, bool is_counter)
+ : registered_track_{},
+ track_{{PERFETTO_TE_HL_EXTRA_TYPE_REGISTERED_TRACK},
+ &(registered_track_.impl)},
+ name_(name),
+ id_(id),
+ parent_uuid_(parent_uuid),
+ is_counter_(is_counter) {
+ register_track();
+}
+
+RegisteredTrack::~RegisteredTrack() {
+ unregister_track();
+}
+
+void RegisteredTrack::register_track() {
+ if (registered_track_.impl.descriptor) return;
+
+ if (is_counter_) {
+ PerfettoTeCounterTrackRegister(®istered_track_, name_.data(),
+ parent_uuid_);
+ } else {
+ PerfettoTeNamedTrackRegister(®istered_track_, name_.data(), id_,
+ parent_uuid_);
+ }
+}
+
+void RegisteredTrack::unregister_track() {
+ if (!registered_track_.impl.descriptor) return;
+ PerfettoTeRegisteredTrackUnregister(®istered_track_);
+}
+
+const PerfettoTeHlExtraRegisteredTrack* RegisteredTrack::get() const {
+ return &track_;
+}
+
+void RegisteredTrack::delete_track(RegisteredTrack* ptr) {
+ delete ptr;
+}
+
+Proto::Proto() : proto_({PERFETTO_TE_HL_EXTRA_TYPE_PROTO_FIELDS}, nullptr) {
+}
+
+void Proto::add_field(PerfettoTeHlProtoField* ptr) {
+ if (!fields_.empty()) {
+ fields_.pop_back();
+ }
+
+ fields_.push_back(ptr);
+ fields_.push_back(nullptr);
+ proto_.fields = fields_.data();
+}
+
+void Proto::clear_fields() {
+ fields_.clear();
+ proto_.fields = nullptr;
+}
+
+void Proto::delete_proto(Proto* ptr) {
+ delete ptr;
+}
+
+const PerfettoTeHlExtraProtoFields* Proto::get() const {
+ return &proto_;
+}
+
+ProtoFieldNested::ProtoFieldNested()
+ : field_({PERFETTO_TE_HL_PROTO_TYPE_NESTED}, nullptr) {
+}
+
+void ProtoFieldNested::add_field(PerfettoTeHlProtoField* ptr) {
+ if (!fields_.empty()) {
+ fields_.pop_back();
+ }
+
+ fields_.push_back(ptr);
+ fields_.push_back(nullptr);
+ field_.fields = fields_.data();
+}
+
+void ProtoFieldNested::set_id(uint32_t id) {
+ fields_.clear();
+ field_.header.id = id;
+ field_.fields = nullptr;
+}
+
+void ProtoFieldNested::delete_field(ProtoFieldNested* ptr) {
+ delete ptr;
+}
+
+const PerfettoTeHlProtoFieldNested* ProtoFieldNested::get() const {
+ return &field_;
+}
+
+void activate_trigger(const char* name, uint32_t ttl_ms) {
+ const char* names[] = {name, nullptr};
+ PerfettoProducerActivateTriggers(names, ttl_ms);
+}
+} // namespace tracing_perfetto
diff --git a/libs/ui/include/ui/ShadowSettings.h b/libs/ui/include/ui/ShadowSettings.h
index c0b83b8..06be6db 100644
--- a/libs/ui/include/ui/ShadowSettings.h
+++ b/libs/ui/include/ui/ShadowSettings.h
@@ -46,6 +46,9 @@
// Length of the cast shadow. If length is <= 0.f no shadows will be drawn.
float length = 0.f;
+ // Length of the cast shadow that is drawn by the client.
+ float clientDrawnLength = 0.f;
+
// If true fill in the casting layer is translucent and the shadow needs to fill the bounds.
// Otherwise the shadow will only be drawn around the edges of the casting layer.
bool casterIsTranslucent = false;
@@ -55,6 +58,7 @@
return lhs.boundaries == rhs.boundaries && lhs.ambientColor == rhs.ambientColor &&
lhs.spotColor == rhs.spotColor && lhs.lightPos == rhs.lightPos &&
lhs.lightRadius == rhs.lightRadius && lhs.length == rhs.length &&
+ lhs.clientDrawnLength == rhs.clientDrawnLength &&
lhs.casterIsTranslucent == rhs.casterIsTranslucent;
}
diff --git a/services/audiomanager/Android.bp b/services/audiomanager/Android.bp
index d11631b..afcdf74 100644
--- a/services/audiomanager/Android.bp
+++ b/services/audiomanager/Android.bp
@@ -15,6 +15,7 @@
],
shared_libs: [
+ "av-types-aidl-cpp",
"libutils",
"libbinder",
"liblog",
diff --git a/services/audiomanager/IAudioManager.cpp b/services/audiomanager/IAudioManager.cpp
index f8a38d1..99360b9 100644
--- a/services/audiomanager/IAudioManager.cpp
+++ b/services/audiomanager/IAudioManager.cpp
@@ -35,6 +35,24 @@
{
}
+ // This should never fail
+ virtual sp<media::IAudioManagerNative> getNativeInterface() {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioManager::getInterfaceDescriptor());
+ const status_t res = remote()->transact(GET_NATIVE_INTERFACE, data, &reply, 0);
+ LOG_ALWAYS_FATAL_IF(res != OK, "%s failed with result %d", __func__, res);
+ const int ex = reply.readExceptionCode();
+ LOG_ALWAYS_FATAL_IF(ex != binder::Status::EX_NONE, "%s failed with exception %d",
+ __func__,
+ ex);
+ sp<IBinder> binder;
+ const status_t err = reply.readNullableStrongBinder(&binder);
+ LOG_ALWAYS_FATAL_IF(binder == nullptr, "%s failed unexpected nullptr %d", __func__, err);
+ const auto iface = checked_interface_cast<media::IAudioManagerNative>(std::move(binder));
+ LOG_ALWAYS_FATAL_IF(iface == nullptr, "%s failed unexpected interface", __func__);
+ return iface;
+ }
+
virtual audio_unique_id_t trackPlayer(player_type_t playerType, audio_usage_t usage,
audio_content_type_t content, const sp<IBinder>& player, audio_session_t sessionId) {
Parcel data, reply;
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index 811692f..5bf6ebb 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -98,6 +98,22 @@
return privacySensitiveDisplays;
}
+vec2 calculatePositionOnDestinationViewport(const DisplayViewport& destinationViewport,
+ float pointerOffset,
+ DisplayTopologyPosition sourceBoundary) {
+ // destination is opposite of the source boundary
+ switch (sourceBoundary) {
+ case DisplayTopologyPosition::RIGHT:
+ return {0, pointerOffset}; // left edge
+ case DisplayTopologyPosition::TOP:
+ return {pointerOffset, destinationViewport.logicalBottom}; // bottom edge
+ case DisplayTopologyPosition::LEFT:
+ return {destinationViewport.logicalRight, pointerOffset}; // right edge
+ case DisplayTopologyPosition::BOTTOM:
+ return {pointerOffset, 0}; // top edge
+ }
+}
+
} // namespace
// --- PointerChoreographer ---
@@ -327,19 +343,19 @@
// except sometimes near the corners.
// In these cases this behaviour is not noticeable. We also do not apply unconsumed delta on
// the destination display for the same reason.
- DisplayPosition sourceBoundary;
+ DisplayTopologyPosition sourceBoundary;
float cursorOffset = 0.0f;
if (rotatedUnconsumedDelta.x > 0) {
- sourceBoundary = DisplayPosition::RIGHT;
+ sourceBoundary = DisplayTopologyPosition::RIGHT;
cursorOffset = rotatedCursorPosition.y;
} else if (rotatedUnconsumedDelta.x < 0) {
- sourceBoundary = DisplayPosition::LEFT;
+ sourceBoundary = DisplayTopologyPosition::LEFT;
cursorOffset = rotatedCursorPosition.y;
} else if (rotatedUnconsumedDelta.y > 0) {
- sourceBoundary = DisplayPosition::BOTTOM;
+ sourceBoundary = DisplayTopologyPosition::BOTTOM;
cursorOffset = rotatedCursorPosition.x;
} else {
- sourceBoundary = DisplayPosition::TOP;
+ sourceBoundary = DisplayTopologyPosition::TOP;
cursorOffset = rotatedCursorPosition.x;
}
@@ -369,8 +385,9 @@
pc.fade(PointerControllerInterface::Transition::IMMEDIATE);
pc.setDisplayViewport(destinationViewport);
vec2 destinationPosition =
- calculateDestinationPosition(destinationViewport, cursorOffset - destinationOffset,
- sourceBoundary);
+ calculatePositionOnDestinationViewport(destinationViewport,
+ cursorOffset - destinationOffset,
+ sourceBoundary);
// Transform position back to un-rotated coordinate space before sending it to controller
destinationPosition = pc.getDisplayTransform().inverse().transform(destinationPosition.x,
@@ -379,22 +396,6 @@
pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
}
-vec2 PointerChoreographer::calculateDestinationPosition(const DisplayViewport& destinationViewport,
- float pointerOffset,
- DisplayPosition sourceBoundary) {
- // destination is opposite of the source boundary
- switch (sourceBoundary) {
- case DisplayPosition::RIGHT:
- return {0, pointerOffset}; // left edge
- case DisplayPosition::TOP:
- return {pointerOffset, destinationViewport.logicalBottom}; // bottom edge
- case DisplayPosition::LEFT:
- return {destinationViewport.logicalRight, pointerOffset}; // right edge
- case DisplayPosition::BOTTOM:
- return {pointerOffset, 0}; // top edge
- }
-}
-
void PointerChoreographer::processDrawingTabletEventLocked(const android::NotifyMotionArgs& args) {
if (args.displayId == ui::LogicalDisplayId::INVALID) {
return;
@@ -540,8 +541,7 @@
}
void PointerChoreographer::onControllerAddedOrRemovedLocked() {
- if (!com::android::input::flags::hide_pointer_indicators_for_secure_windows() &&
- !com::android::input::flags::connected_displays_cursor()) {
+ if (!com::android::input::flags::hide_pointer_indicators_for_secure_windows()) {
return;
}
bool requireListener = !mTouchPointersByDevice.empty() || !mMousePointersByDisplay.empty() ||
@@ -607,11 +607,16 @@
mNextListener.notify(args);
}
-void PointerChoreographer::setDisplayTopology(
- const std::unordered_map<ui::LogicalDisplayId, std::vector<AdjacentDisplay>>&
- displayTopology) {
+void PointerChoreographer::setDisplayTopology(const DisplayTopologyGraph& displayTopologyGraph) {
std::scoped_lock _l(getLock());
- mTopology = displayTopology;
+ mTopology = displayTopologyGraph;
+
+ // make primary display default mouse display, if it was not set
+ // or the existing display was removed
+ if (mDefaultMouseDisplayId == ui::LogicalDisplayId::INVALID ||
+ mTopology.graph.find(mDefaultMouseDisplayId) != mTopology.graph.end()) {
+ mDefaultMouseDisplayId = mTopology.primaryDisplayId;
+ }
}
void PointerChoreographer::dump(std::string& dump) {
@@ -985,73 +990,17 @@
return ConstructorDelegate(std::move(ctor));
}
-void PointerChoreographer::populateFakeDisplayTopologyLocked(
- const std::vector<gui::DisplayInfo>& displayInfos) {
- if (!com::android::input::flags::connected_displays_cursor()) {
- return;
- }
-
- if (displayInfos.size() == mTopology.size()) {
- bool displaysChanged = false;
- for (const auto& displayInfo : displayInfos) {
- if (mTopology.find(displayInfo.displayId) == mTopology.end()) {
- displaysChanged = true;
- break;
- }
- }
-
- if (!displaysChanged) {
- return;
- }
- }
-
- // create a fake topology assuming following order
- // default-display (top-edge) -> next-display (right-edge) -> next-display (right-edge) ...
- // This also adds a 100px offset on corresponding edge for better manual testing
- // ┌────────┐
- // │ next ├─────────┐
- // ┌─└───────┐┤ next 2 │ ...
- // │ default │└─────────┘
- // └─────────┘
- mTopology.clear();
-
- // treat default display as base, in real topology it should be the primary-display
- ui::LogicalDisplayId previousDisplay = ui::LogicalDisplayId::DEFAULT;
- for (const auto& displayInfo : displayInfos) {
- if (displayInfo.displayId == ui::LogicalDisplayId::DEFAULT) {
- continue;
- }
- if (previousDisplay == ui::LogicalDisplayId::DEFAULT) {
- mTopology[previousDisplay].push_back(
- {displayInfo.displayId, DisplayPosition::TOP, 100});
- mTopology[displayInfo.displayId].push_back(
- {previousDisplay, DisplayPosition::BOTTOM, -100});
- } else {
- mTopology[previousDisplay].push_back(
- {displayInfo.displayId, DisplayPosition::RIGHT, 100});
- mTopology[displayInfo.displayId].push_back(
- {previousDisplay, DisplayPosition::LEFT, -100});
- }
- previousDisplay = displayInfo.displayId;
- }
-
- // update default pointer display. In real topology it should be the primary-display
- if (mTopology.find(mDefaultMouseDisplayId) == mTopology.end()) {
- mDefaultMouseDisplayId = ui::LogicalDisplayId::DEFAULT;
- }
-}
-
std::optional<std::pair<const DisplayViewport*, float /*offset*/>>
PointerChoreographer::findDestinationDisplayLocked(const ui::LogicalDisplayId sourceDisplayId,
- const DisplayPosition sourceBoundary,
+ const DisplayTopologyPosition sourceBoundary,
float cursorOffset) const {
- const auto& sourceNode = mTopology.find(sourceDisplayId);
- if (sourceNode == mTopology.end()) {
+ const auto& sourceNode = mTopology.graph.find(sourceDisplayId);
+ if (sourceNode == mTopology.graph.end()) {
// Topology is likely out of sync with viewport info, wait for it to be updated
LOG(WARNING) << "Source display missing from topology " << sourceDisplayId;
return std::nullopt;
}
- for (const AdjacentDisplay& adjacentDisplay : sourceNode->second) {
+ for (const DisplayTopologyAdjacentDisplay& adjacentDisplay : sourceNode->second) {
if (adjacentDisplay.position != sourceBoundary) {
continue;
}
@@ -1064,8 +1013,8 @@
continue;
}
// target position must be within target display boundary
- const int32_t edgeSize =
- sourceBoundary == DisplayPosition::TOP || sourceBoundary == DisplayPosition::BOTTOM
+ const int32_t edgeSize = sourceBoundary == DisplayTopologyPosition::TOP ||
+ sourceBoundary == DisplayTopologyPosition::BOTTOM
? (destinationViewport->logicalRight - destinationViewport->logicalLeft)
: (destinationViewport->logicalBottom - destinationViewport->logicalTop);
if (cursorOffset >= adjacentDisplay.offsetPx &&
@@ -1093,7 +1042,6 @@
mPrivacySensitiveDisplays = std::move(newPrivacySensitiveDisplays);
mPointerChoreographer->onPrivacySensitiveDisplaysChangedLocked(mPrivacySensitiveDisplays);
}
- mPointerChoreographer->populateFakeDisplayTopologyLocked(windowInfosUpdate.displayInfos);
}
void PointerChoreographer::PointerChoreographerDisplayInfoListener::setInitialDisplayInfosLocked(
diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h
index 939529f..c2f5ec0 100644
--- a/services/inputflinger/PointerChoreographer.h
+++ b/services/inputflinger/PointerChoreographer.h
@@ -22,6 +22,7 @@
#include <android-base/thread_annotations.h>
#include <gui/WindowInfosListener.h>
+#include <input/DisplayTopologyGraph.h>
#include <type_traits>
#include <unordered_set>
@@ -80,6 +81,11 @@
*/
virtual void setFocusedDisplay(ui::LogicalDisplayId displayId) = 0;
+ /*
+ * Used by InputManager to notify changes in the DisplayTopology
+ */
+ virtual void setDisplayTopology(const DisplayTopologyGraph& displayTopologyGraph) = 0;
+
/**
* This method may be called on any thread (usually by the input manager on a binder thread).
*/
@@ -103,6 +109,7 @@
ui::LogicalDisplayId displayId, DeviceId deviceId) override;
void setPointerIconVisibility(ui::LogicalDisplayId displayId, bool visible) override;
void setFocusedDisplay(ui::LogicalDisplayId displayId) override;
+ void setDisplayTopology(const DisplayTopologyGraph& displayTopologyGraph);
void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
void notifyKey(const NotifyKeyArgs& args) override;
@@ -113,24 +120,6 @@
void notifyDeviceReset(const NotifyDeviceResetArgs& args) override;
void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override;
- // TODO(b/362719483) remove these when real topology is available
- enum class DisplayPosition {
- RIGHT,
- TOP,
- LEFT,
- BOTTOM,
- ftl_last = BOTTOM,
- };
-
- struct AdjacentDisplay {
- ui::LogicalDisplayId displayId;
- DisplayPosition position;
- float offsetPx;
- };
- void setDisplayTopology(
- const std::unordered_map<ui::LogicalDisplayId, std::vector<AdjacentDisplay>>&
- displayTopology);
-
void dump(std::string& dump) override;
private:
@@ -174,18 +163,16 @@
void handleUnconsumedDeltaLocked(PointerControllerInterface& pc, const vec2& unconsumedDelta)
REQUIRES(getLock());
- void populateFakeDisplayTopologyLocked(const std::vector<gui::DisplayInfo>& displayInfos)
+ std::optional<std::pair<const DisplayViewport*, float /*offset*/>> findDestinationDisplayLocked(
+ const ui::LogicalDisplayId sourceDisplayId,
+ const DisplayTopologyPosition sourceBoundary, float cursorOffset) const
REQUIRES(getLock());
- std::optional<std::pair<const DisplayViewport*, float /*offset*/>> findDestinationDisplayLocked(
- const ui::LogicalDisplayId sourceDisplayId, const DisplayPosition sourceBoundary,
- float cursorOffset) const REQUIRES(getLock());
-
- static vec2 calculateDestinationPosition(const DisplayViewport& destinationViewport,
- float pointerOffset, DisplayPosition sourceBoundary);
-
- std::unordered_map<ui::LogicalDisplayId, std::vector<AdjacentDisplay>> mTopology
- GUARDED_BY(getLock());
+ /* Topology is initialized with default-constructed value, which is an empty topology. Till we
+ * receive setDisplayTopology call.
+ * Meanwhile Choreographer will treat every display as independent disconnected display.
+ */
+ DisplayTopologyGraph mTopology GUARDED_BY(getLock());
/* This listener keeps tracks of visible privacy sensitive displays and updates the
* choreographer if there are any changes.
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index cd4ed5c..4c4182d 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -46,6 +46,7 @@
#include <ctime>
#include <queue>
#include <sstream>
+#include <variant>
#include "../InputDeviceMetricsSource.h"
@@ -417,7 +418,7 @@
if (inputTarget.useDefaultPointerTransform() && !zeroCoords) {
const ui::Transform& transform = inputTarget.getDefaultPointerTransform();
return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, transform,
- inputTarget.displayTransform,
+ inputTarget.rawTransform,
inputTarget.globalScaleFactor, uid, vsyncId,
windowId);
}
@@ -438,7 +439,7 @@
transform =
&inputTarget.getTransformForPointer(firstMarkedBit(inputTarget.getPointerIds()));
const ui::Transform inverseTransform = transform->inverse();
- displayTransform = &inputTarget.displayTransform;
+ displayTransform = &inputTarget.rawTransform;
// Iterate through all pointers in the event to normalize against the first.
for (size_t i = 0; i < motionEntry.getPointerCount(); i++) {
@@ -787,38 +788,14 @@
});
}
-/**
- * In general, touch should be always split between windows. Some exceptions:
- * 1. Don't split touch if all of the below is true:
- * (a) we have an active pointer down *and*
- * (b) a new pointer is going down that's from the same device *and*
- * (c) the window that's receiving the current pointer does not support split touch.
- * 2. Don't split mouse events
- */
-bool shouldSplitTouch(const TouchState& touchState, const MotionEntry& entry) {
- if (isFromSource(entry.source, AINPUT_SOURCE_MOUSE)) {
- // We should never split mouse events
- return false;
- }
- for (const TouchedWindow& touchedWindow : touchState.windows) {
- if (touchedWindow.windowHandle->getInfo()->isSpy()) {
- // Spy windows should not affect whether or not touch is split.
- continue;
- }
- if (touchedWindow.windowHandle->getInfo()->supportsSplitTouch()) {
- continue;
- }
- if (touchedWindow.windowHandle->getInfo()->inputConfig.test(
- gui::WindowInfo::InputConfig::IS_WALLPAPER)) {
- // Wallpaper window should not affect whether or not touch is split
- continue;
- }
-
- if (touchedWindow.hasTouchingPointers(entry.deviceId)) {
- return false;
- }
- }
- return true;
+bool shouldSplitTouch(int32_t source) {
+ // We should never split mouse events. This is because the events that are produced by touchpad
+ // are sent to InputDispatcher as two fingers (for example, pinch zoom), but they need to be
+ // dispatched to the same window. In those cases, the behaviour is also slightly different from
+ // default because the events should be sent to the cursor position rather than the x,y values
+ // of each of the fingers.
+ // The "normal" (uncaptured) events produced by touchpad and by mouse have SOURCE_MOUSE.
+ return !isFromSource(source, AINPUT_SOURCE_MOUSE);
}
/**
@@ -924,6 +901,23 @@
std::forward<InputEventInjectionResult>(e));
}
+InputTarget createInputTarget(const std::shared_ptr<Connection>& connection,
+ const sp<android::gui::WindowInfoHandle>& windowHandle,
+ InputTarget::DispatchMode dispatchMode,
+ ftl::Flags<InputTarget::Flags> targetFlags,
+ const ui::Transform& rawTransform,
+ std::optional<nsecs_t> firstDownTimeInTarget) {
+ LOG_ALWAYS_FATAL_IF(connection == nullptr);
+ InputTarget inputTarget{connection};
+ inputTarget.windowHandle = windowHandle;
+ inputTarget.dispatchMode = dispatchMode;
+ inputTarget.flags = targetFlags;
+ inputTarget.globalScaleFactor = windowHandle->getInfo()->globalScaleFactor;
+ inputTarget.rawTransform = rawTransform;
+ inputTarget.firstDownTimeInTarget = firstDownTimeInTarget;
+ return inputTarget;
+}
+
} // namespace
// --- InputDispatcher ---
@@ -980,7 +974,7 @@
while (!mConnectionsByToken.empty()) {
std::shared_ptr<Connection> connection = mConnectionsByToken.begin()->second;
- removeInputChannelLocked(connection->getToken(), /*notify=*/false);
+ removeInputChannelLocked(connection, /*notify=*/false);
}
}
@@ -1128,7 +1122,7 @@
if (connection->monitor) {
return mMonitorDispatchingTimeout;
}
- const sp<WindowInfoHandle> window = getWindowHandleLocked(connection->getToken());
+ const sp<WindowInfoHandle> window = mWindowInfos.findWindowHandle(connection->getToken());
if (window != nullptr) {
return window->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
}
@@ -1333,7 +1327,7 @@
const bool isStylus = isPointerFromStylus(motionEntry, /*pointerIndex=*/0);
sp<WindowInfoHandle> touchedWindowHandle =
- findTouchedWindowAtLocked(displayId, x, y, isStylus);
+ mWindowInfos.findTouchedWindowAt(displayId, x, y, isStylus);
if (touchedWindowHandle != nullptr &&
touchedWindowHandle->getApplicationToken() !=
mAwaitedFocusedApplication->getApplicationToken()) {
@@ -1346,7 +1340,8 @@
// Alternatively, maybe there's a spy window that could handle this event.
const std::vector<sp<WindowInfoHandle>> touchedSpies =
- findTouchedSpyWindowsAtLocked(displayId, x, y, isStylus, motionEntry.deviceId);
+ mWindowInfos.findTouchedSpyWindowsAt(displayId, x, y, isStylus,
+ motionEntry.deviceId, mTouchStatesByDisplay);
for (const auto& windowHandle : touchedSpies) {
const std::shared_ptr<Connection> connection =
getConnectionLocked(windowHandle->getToken());
@@ -1452,19 +1447,19 @@
}
}
-sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(ui::LogicalDisplayId displayId,
- float x, float y, bool isStylus,
- bool ignoreDragWindow) const {
+sp<WindowInfoHandle> InputDispatcher::DispatcherWindowInfo::findTouchedWindowAt(
+ ui::LogicalDisplayId displayId, float x, float y, bool isStylus,
+ const sp<android::gui::WindowInfoHandle> ignoreWindow) const {
// Traverse windows from front to back to find touched window.
- const auto& windowHandles = getWindowHandlesLocked(displayId);
+ const auto& windowHandles = getWindowHandlesForDisplay(displayId);
for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
- if (ignoreDragWindow && haveSameToken(windowHandle, mDragState->dragWindow)) {
+ if (ignoreWindow && haveSameToken(windowHandle, ignoreWindow)) {
continue;
}
const WindowInfo& info = *windowHandle->getInfo();
if (!info.isSpy() &&
- windowAcceptsTouchAt(info, displayId, x, y, isStylus, getTransformLocked(displayId))) {
+ windowAcceptsTouchAt(info, displayId, x, y, isStylus, getDisplayTransform(displayId))) {
return windowHandle;
}
}
@@ -1479,7 +1474,7 @@
}
// Traverse windows from front to back until we encounter the touched window.
std::vector<InputTarget> outsideTargets;
- const auto& windowHandles = getWindowHandlesLocked(displayId);
+ const auto& windowHandles = mWindowInfos.getWindowHandlesForDisplay(displayId);
for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
if (windowHandle == touchedWindow) {
// Stop iterating once we found a touched window. Any WATCH_OUTSIDE_TOUCH window
@@ -1499,28 +1494,18 @@
return outsideTargets;
}
-std::vector<sp<WindowInfoHandle>> InputDispatcher::findTouchedSpyWindowsAtLocked(
- ui::LogicalDisplayId displayId, float x, float y, bool isStylus, DeviceId deviceId) const {
+std::vector<sp<WindowInfoHandle>> InputDispatcher::DispatcherWindowInfo::findTouchedSpyWindowsAt(
+ ui::LogicalDisplayId displayId, float x, float y, bool isStylus, DeviceId deviceId,
+ const std::unordered_map<ui::LogicalDisplayId, TouchState>& touchStatesByDisplay) const {
// Traverse windows from front to back and gather the touched spy windows.
std::vector<sp<WindowInfoHandle>> spyWindows;
- const auto& windowHandles = getWindowHandlesLocked(displayId);
+ const auto& windowHandles = getWindowHandlesForDisplay(displayId);
for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
const WindowInfo& info = *windowHandle->getInfo();
- if (!windowAcceptsTouchAt(info, displayId, x, y, isStylus, getTransformLocked(displayId))) {
- // Generally, we would skip any pointer that's outside of the window. However, if the
- // spy prevents splitting, and already has some of the pointers from this device, then
- // it should get more pointers from the same device, even if they are outside of that
- // window
- if (info.supportsSplitTouch()) {
- continue;
- }
-
- // We know that split touch is not supported. Skip this window only if it doesn't have
- // any touching pointers for this device already.
- if (!windowHasTouchingPointersLocked(windowHandle, deviceId)) {
- continue;
- }
- // If it already has pointers down for this device, then give it this pointer, too.
+ if (!windowAcceptsTouchAt(info, displayId, x, y, isStylus,
+ getDisplayTransform(displayId))) {
+ // Skip if the pointer is outside of the window.
+ continue;
}
if (!info.isSpy()) {
// The first touched non-spy window was found, so return the spy windows touched so far.
@@ -1828,7 +1813,7 @@
void InputDispatcher::dispatchTouchModeChangeLocked(
nsecs_t currentTime, const std::shared_ptr<const TouchModeEntry>& entry) {
const std::vector<sp<WindowInfoHandle>>& windowHandles =
- getWindowHandlesLocked(entry->displayId);
+ mWindowInfos.getWindowHandlesForDisplay(entry->displayId);
if (windowHandles.empty()) {
return;
}
@@ -2216,7 +2201,7 @@
sp<WindowInfoHandle> windowHandle;
if (!connection->monitor) {
- windowHandle = getWindowHandleLocked(connection->getToken());
+ windowHandle = mWindowInfos.findWindowHandle(connection->getToken());
if (windowHandle == nullptr) {
// The window that is receiving this ANR was removed, so there is no need to generate
// cancellations, because the cancellations would have already been generated when
@@ -2427,7 +2412,7 @@
tempTouchState = *oldState;
}
- bool isSplit = shouldSplitTouch(tempTouchState, entry);
+ const bool isSplit = shouldSplitTouch(entry.source);
const bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE ||
maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
@@ -2440,11 +2425,6 @@
const bool newGesture = isDown || maskedAction == AMOTION_EVENT_ACTION_SCROLL ||
maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE;
- const bool isFromMouse = isFromSource(entry.source, AINPUT_SOURCE_MOUSE);
-
- if (newGesture) {
- isSplit = false;
- }
if (isDown && tempTouchState.hasHoveringPointers(entry.deviceId)) {
// Compatibility behaviour: ACTION_DOWN causes HOVER_EXIT to get generated.
@@ -2472,7 +2452,7 @@
// be a pointer that would generate ACTION_DOWN, *and* touch should not already be down.
const bool isStylus = isPointerFromStylus(entry, pointerIndex);
sp<WindowInfoHandle> newTouchedWindowHandle =
- findTouchedWindowAtLocked(displayId, x, y, isStylus);
+ mWindowInfos.findTouchedWindowAt(displayId, x, y, isStylus);
if (isDown) {
targets += findOutsideTargetsLocked(displayId, newTouchedWindowHandle, pointer.id);
@@ -2480,17 +2460,6 @@
LOG_IF(INFO, newTouchedWindowHandle == nullptr)
<< "No new touched window at (" << std::format("{:.1f}, {:.1f}", x, y)
<< ") in display " << displayId;
- // Handle the case where we did not find a window.
- if (!input_flags::split_all_touches()) {
- // If we are force splitting all touches, then touches outside of the window should
- // be dropped, even if this device already has pointers down in another window.
- if (newTouchedWindowHandle == nullptr) {
- // Try to assign the pointer to the first foreground window we find, if there is
- // one.
- newTouchedWindowHandle =
- tempTouchState.getFirstForegroundWindowHandle(entry.deviceId);
- }
- }
// Verify targeted injection.
if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) {
@@ -2498,27 +2467,9 @@
return injectionError(InputEventInjectionResult::TARGET_MISMATCH);
}
- // Figure out whether splitting will be allowed for this window.
- if (newTouchedWindowHandle != nullptr) {
- if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
- // New window supports splitting, but we should never split mouse events.
- isSplit = !isFromMouse;
- } else if (isSplit) {
- // New window does not support splitting but we have already split events.
- // Ignore the new window.
- LOG(INFO) << "Skipping " << newTouchedWindowHandle->getName()
- << " because it doesn't support split touch";
- newTouchedWindowHandle = nullptr;
- }
- } else {
- // No window is touched, so set split to true. This will allow the next pointer down to
- // be delivered to a new window which supports split touch. Pointers from a mouse device
- // should never be split.
- isSplit = !isFromMouse;
- }
-
std::vector<sp<WindowInfoHandle>> newTouchedWindows =
- findTouchedSpyWindowsAtLocked(displayId, x, y, isStylus, entry.deviceId);
+ mWindowInfos.findTouchedSpyWindowsAt(displayId, x, y, isStylus, entry.deviceId,
+ mTouchStatesByDisplay);
if (newTouchedWindowHandle != nullptr) {
// Process the foreground window first so that it is the first to receive the event.
newTouchedWindows.insert(newTouchedWindows.begin(), newTouchedWindowHandle);
@@ -2655,7 +2606,7 @@
tempTouchState.getFirstForegroundWindowHandle(entry.deviceId);
LOG_ALWAYS_FATAL_IF(oldTouchedWindowHandle == nullptr);
sp<WindowInfoHandle> newTouchedWindowHandle =
- findTouchedWindowAtLocked(displayId, x, y, isStylus);
+ mWindowInfos.findTouchedWindowAt(displayId, x, y, isStylus);
// Verify targeted injection.
if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) {
@@ -2689,9 +2640,6 @@
targets);
// Make a slippery entrance into the new window.
- if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
- isSplit = !isFromMouse;
- }
ftl::Flags<InputTarget::Flags> targetFlags;
if (canReceiveForegroundTouches(*newTouchedWindowHandle->getInfo())) {
@@ -2779,7 +2727,7 @@
for (InputTarget& target : targets) {
if (target.dispatchMode == InputTarget::DispatchMode::OUTSIDE) {
sp<WindowInfoHandle> targetWindow =
- getWindowHandleLocked(target.connection->getToken());
+ mWindowInfos.findWindowHandle(target.connection->getToken());
if (targetWindow->getInfo()->ownerUid != foregroundWindowUid) {
target.flags |= InputTarget::Flags::ZERO_COORDS;
}
@@ -2879,7 +2827,8 @@
constexpr bool isStylus = false;
sp<WindowInfoHandle> dropWindow =
- findTouchedWindowAtLocked(displayId, x, y, isStylus, /*ignoreDragWindow=*/true);
+ mWindowInfos.findTouchedWindowAt(displayId, x, y, isStylus, /*ignoreWindow=*/
+ mDragState->dragWindow);
if (dropWindow) {
vec2 local = dropWindow->getInfo()->transform.transform(x, y);
sendDropWindowCommandLocked(dropWindow->getToken(), local.x, local.y);
@@ -2934,8 +2883,8 @@
constexpr bool isStylus = false;
sp<WindowInfoHandle> hoverWindowHandle =
- findTouchedWindowAtLocked(entry.displayId, x, y, isStylus,
- /*ignoreDragWindow=*/true);
+ mWindowInfos.findTouchedWindowAt(entry.displayId, x, y, isStylus,
+ /*ignoreWindow=*/mDragState->dragWindow);
// enqueue drag exit if needed.
if (hoverWindowHandle != mDragState->dragHoverWindowHandle &&
!haveSameToken(hoverWindowHandle, mDragState->dragHoverWindowHandle)) {
@@ -2970,31 +2919,6 @@
}
}
-std::optional<InputTarget> InputDispatcher::createInputTargetLocked(
- const sp<android::gui::WindowInfoHandle>& windowHandle,
- InputTarget::DispatchMode dispatchMode, ftl::Flags<InputTarget::Flags> targetFlags,
- std::optional<nsecs_t> firstDownTimeInTarget) const {
- std::shared_ptr<Connection> connection = getConnectionLocked(windowHandle->getToken());
- if (connection == nullptr) {
- ALOGW("Not creating InputTarget for %s, no input channel", windowHandle->getName().c_str());
- return {};
- }
- InputTarget inputTarget{connection};
- inputTarget.windowHandle = windowHandle;
- inputTarget.dispatchMode = dispatchMode;
- inputTarget.flags = targetFlags;
- inputTarget.globalScaleFactor = windowHandle->getInfo()->globalScaleFactor;
- inputTarget.firstDownTimeInTarget = firstDownTimeInTarget;
- const auto& displayInfoIt = mDisplayInfos.find(windowHandle->getInfo()->displayId);
- if (displayInfoIt != mDisplayInfos.end()) {
- inputTarget.displayTransform = displayInfoIt->second.transform;
- } else {
- // DisplayInfo not found for this window on display windowHandle->getInfo()->displayId.
- // TODO(b/198444055): Make this an error message after 'setInputWindows' API is removed.
- }
- return inputTarget;
-}
-
void InputDispatcher::addWindowTargetLocked(const sp<WindowInfoHandle>& windowHandle,
InputTarget::DispatchMode dispatchMode,
ftl::Flags<InputTarget::Flags> targetFlags,
@@ -3009,13 +2933,16 @@
const WindowInfo* windowInfo = windowHandle->getInfo();
if (it == inputTargets.end()) {
- std::optional<InputTarget> target =
- createInputTargetLocked(windowHandle, dispatchMode, targetFlags,
- firstDownTimeInTarget);
- if (!target) {
+ std::shared_ptr<Connection> connection = getConnectionLocked(windowHandle->getToken());
+ if (connection == nullptr) {
+ ALOGW("Not creating InputTarget for %s, no input channel",
+ windowHandle->getName().c_str());
return;
}
- inputTargets.push_back(*target);
+ inputTargets.push_back(
+ createInputTarget(connection, windowHandle, dispatchMode, targetFlags,
+ mWindowInfos.getRawTransform(*windowHandle->getInfo()),
+ firstDownTimeInTarget));
it = inputTargets.end() - 1;
}
@@ -3060,13 +2987,16 @@
const WindowInfo* windowInfo = windowHandle->getInfo();
if (it == inputTargets.end()) {
- std::optional<InputTarget> target =
- createInputTargetLocked(windowHandle, dispatchMode, targetFlags,
- firstDownTimeInTarget);
- if (!target) {
+ std::shared_ptr<Connection> connection = getConnectionLocked(windowHandle->getToken());
+ if (connection == nullptr) {
+ ALOGW("Not creating InputTarget for %s, no input channel",
+ windowHandle->getName().c_str());
return;
}
- inputTargets.push_back(*target);
+ inputTargets.push_back(
+ createInputTarget(connection, windowHandle, dispatchMode, targetFlags,
+ mWindowInfos.getRawTransform(*windowHandle->getInfo()),
+ firstDownTimeInTarget));
it = inputTargets.end() - 1;
}
@@ -3098,11 +3028,10 @@
for (const Monitor& monitor : selectResponsiveMonitorsLocked(monitorsIt->second)) {
InputTarget target{monitor.connection};
// target.firstDownTimeInTarget is not set for global monitors. It is only required in split
- // touch and global monitoring works as intended even without setting firstDownTimeInTarget
- if (const auto& it = mDisplayInfos.find(displayId); it != mDisplayInfos.end()) {
- target.displayTransform = it->second.transform;
- }
- target.setDefaultPointerTransform(target.displayTransform);
+ // touch and global monitoring works as intended even without setting firstDownTimeInTarget.
+ // Since global monitors don't have windows, use the display transform as the raw transform.
+ target.rawTransform = mWindowInfos.getDisplayTransform(displayId);
+ target.setDefaultPointerTransform(target.rawTransform);
inputTargets.push_back(target);
}
}
@@ -3164,7 +3093,8 @@
const sp<WindowInfoHandle>& windowHandle, float x, float y) const {
const WindowInfo* windowInfo = windowHandle->getInfo();
ui::LogicalDisplayId displayId = windowInfo->displayId;
- const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);
+ const std::vector<sp<WindowInfoHandle>>& windowHandles =
+ mWindowInfos.getWindowHandlesForDisplay(displayId);
TouchOcclusionInfo info;
info.hasBlockingOcclusion = false;
info.obscuringOpacity = 0;
@@ -3176,7 +3106,8 @@
}
const WindowInfo* otherInfo = otherHandle->getInfo();
if (canBeObscuredBy(windowHandle, otherHandle) &&
- windowOccludesTouchAt(*otherInfo, displayId, x, y, getTransformLocked(displayId)) &&
+ windowOccludesTouchAt(*otherInfo, displayId, x, y,
+ mWindowInfos.getDisplayTransform(displayId)) &&
!haveSameApplicationToken(windowInfo, otherInfo)) {
if (DEBUG_TOUCH_OCCLUSION) {
info.debugInfo.push_back(
@@ -3248,14 +3179,16 @@
bool InputDispatcher::isWindowObscuredAtPointLocked(const sp<WindowInfoHandle>& windowHandle,
float x, float y) const {
ui::LogicalDisplayId displayId = windowHandle->getInfo()->displayId;
- const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);
+ const std::vector<sp<WindowInfoHandle>>& windowHandles =
+ mWindowInfos.getWindowHandlesForDisplay(displayId);
for (const sp<WindowInfoHandle>& otherHandle : windowHandles) {
if (windowHandle == otherHandle) {
break; // All future windows are below us. Exit early.
}
const WindowInfo* otherInfo = otherHandle->getInfo();
if (canBeObscuredBy(windowHandle, otherHandle) &&
- windowOccludesTouchAt(*otherInfo, displayId, x, y, getTransformLocked(displayId))) {
+ windowOccludesTouchAt(*otherInfo, displayId, x, y,
+ mWindowInfos.getDisplayTransform(displayId))) {
return true;
}
}
@@ -3264,7 +3197,8 @@
bool InputDispatcher::isWindowObscuredLocked(const sp<WindowInfoHandle>& windowHandle) const {
ui::LogicalDisplayId displayId = windowHandle->getInfo()->displayId;
- const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);
+ const std::vector<sp<WindowInfoHandle>>& windowHandles =
+ mWindowInfos.getWindowHandlesForDisplay(displayId);
const WindowInfo* windowInfo = windowHandle->getInfo();
for (const sp<WindowInfoHandle>& otherHandle : windowHandles) {
if (windowHandle == otherHandle) {
@@ -3921,7 +3855,7 @@
"event to it, status=%s(%d)",
connection->getInputChannelName().c_str(), statusToString(status).c_str(),
status);
- abortBrokenDispatchCycleLocked(currentTime, connection, /*notify=*/true);
+ abortBrokenDispatchCycleLocked(connection, /*notify=*/true);
} else {
// Pipe is full and we are waiting for the app to finish process some events
// before sending more events to it.
@@ -3936,7 +3870,7 @@
"status=%s(%d)",
connection->getInputChannelName().c_str(), statusToString(status).c_str(),
status);
- abortBrokenDispatchCycleLocked(currentTime, connection, /*notify=*/true);
+ abortBrokenDispatchCycleLocked(connection, /*notify=*/true);
}
return;
}
@@ -4011,8 +3945,7 @@
postCommandLocked(std::move(command));
}
-void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime,
- const std::shared_ptr<Connection>& connection,
+void InputDispatcher::abortBrokenDispatchCycleLocked(const std::shared_ptr<Connection>& connection,
bool notify) {
if (DEBUG_DISPATCH_CYCLE) {
LOG(INFO) << "channel '" << connection->getInputChannelName() << "'~ " << __func__
@@ -4118,7 +4051,8 @@
} else {
// Monitor channels are never explicitly unregistered.
// We do it automatically when the remote endpoint is closed so don't warn about them.
- const bool stillHaveWindowHandle = getWindowHandleLocked(connection->getToken()) != nullptr;
+ const bool stillHaveWindowHandle =
+ mWindowInfos.findWindowHandle(connection->getToken()) != nullptr;
notify = !connection->monitor && stillHaveWindowHandle;
if (notify) {
ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred. events=0x%x",
@@ -4127,7 +4061,7 @@
}
// Remove the channel.
- removeInputChannelLocked(connection->getToken(), notify);
+ removeInputChannelLocked(connection, notify);
return 0; // remove the callback
}
@@ -4153,11 +4087,11 @@
// Follow up by generating cancellations for all windows, because we don't explicitly track
// the windows that have an ongoing focus event stream.
if (cancelNonPointers) {
- for (const auto& [_, handles] : mWindowHandlesByDisplay) {
- for (const auto& windowHandle : handles) {
- synthesizeCancelationEventsForWindowLocked(windowHandle, options);
- }
- }
+ mWindowInfos.forEachWindowHandle(
+ [&](const sp<android::gui::WindowInfoHandle>& windowHandle) {
+ base::ScopedLockAssertion assumeLocked(mLock);
+ synthesizeCancelationEventsForWindowLocked(windowHandle, options);
+ });
}
// Cancel monitors.
@@ -4281,11 +4215,11 @@
motionEntry.downTime, targets);
} else {
targets.emplace_back(fallbackTarget);
- const auto it = mDisplayInfos.find(motionEntry.displayId);
- if (it != mDisplayInfos.end()) {
- targets.back().displayTransform = it->second.transform;
- targets.back().setDefaultPointerTransform(it->second.transform);
- }
+ // Since we don't have a window, use the display transform as the raw transform.
+ const ui::Transform displayTransform =
+ mWindowInfos.getDisplayTransform(motionEntry.displayId);
+ targets.back().rawTransform = displayTransform;
+ targets.back().setDefaultPointerTransform(displayTransform);
}
logOutboundMotionDetails("cancel - ", motionEntry);
break;
@@ -4340,7 +4274,7 @@
}
const auto [_, touchedWindowState, displayId] =
- findTouchStateWindowAndDisplayLocked(connection->getToken());
+ findTouchStateWindowAndDisplay(connection->getToken(), mTouchStatesByDisplay);
if (touchedWindowState == nullptr) {
LOG(FATAL) << __func__ << ": Touch state is out of sync: No touched window for token";
}
@@ -4367,11 +4301,11 @@
targets);
} else {
targets.emplace_back(connection, targetFlags);
- const auto it = mDisplayInfos.find(motionEntry.displayId);
- if (it != mDisplayInfos.end()) {
- targets.back().displayTransform = it->second.transform;
- targets.back().setDefaultPointerTransform(it->second.transform);
- }
+ // Since we don't have a window, use the display transform as the raw transform.
+ const ui::Transform displayTransform =
+ mWindowInfos.getDisplayTransform(motionEntry.displayId);
+ targets.back().rawTransform = displayTransform;
+ targets.back().setDefaultPointerTransform(displayTransform);
}
logOutboundMotionDetails("down - ", motionEntry);
break;
@@ -4642,10 +4576,7 @@
}
if (shouldSendMotionToInputFilterLocked(args)) {
- ui::Transform displayTransform;
- if (const auto it = mDisplayInfos.find(args.displayId); it != mDisplayInfos.end()) {
- displayTransform = it->second.transform;
- }
+ ui::Transform displayTransform = mWindowInfos.getDisplayTransform(args.displayId);
mLock.unlock();
@@ -4939,6 +4870,19 @@
return InputEventInjectionResult::FAILED;
}
+ if (!(policyFlags & POLICY_FLAG_PASS_TO_USER)) {
+ // Set the flag anyway if we already have an ongoing motion gesture. That
+ // would allow us to complete the processing of the current stroke.
+ const auto touchStateIt = mTouchStatesByDisplay.find(displayId);
+ if (touchStateIt != mTouchStatesByDisplay.end()) {
+ const TouchState& touchState = touchStateIt->second;
+ if (touchState.hasTouchingPointers(resolvedDeviceId) ||
+ touchState.hasHoveringPointers(resolvedDeviceId)) {
+ policyFlags |= POLICY_FLAG_PASS_TO_USER;
+ }
+ }
+ }
+
const nsecs_t* sampleEventTimes = motionEvent.getSampleEventTimes();
const size_t pointerCount = motionEvent.getPointerCount();
const std::vector<PointerProperties>
@@ -5149,9 +5093,8 @@
MotionEntry& entry, const ui::Transform& injectedTransform) const {
// Input injection works in the logical display coordinate space, but the input pipeline works
// display space, so we need to transform the injected events accordingly.
- const auto it = mDisplayInfos.find(entry.displayId);
- if (it == mDisplayInfos.end()) return;
- const auto& transformToDisplay = it->second.transform.inverse() * injectedTransform;
+ const ui::Transform displayTransform = mWindowInfos.getDisplayTransform(entry.displayId);
+ const auto& transformToDisplay = displayTransform.inverse() * injectedTransform;
if (entry.xCursorPosition != AMOTION_EVENT_INVALID_CURSOR_POSITION &&
entry.yCursorPosition != AMOTION_EVENT_INVALID_CURSOR_POSITION) {
@@ -5184,14 +5127,7 @@
}
}
-const std::vector<sp<WindowInfoHandle>>& InputDispatcher::getWindowHandlesLocked(
- ui::LogicalDisplayId displayId) const {
- static const std::vector<sp<WindowInfoHandle>> EMPTY_WINDOW_HANDLES;
- auto it = mWindowHandlesByDisplay.find(displayId);
- return it != mWindowHandlesByDisplay.end() ? it->second : EMPTY_WINDOW_HANDLES;
-}
-
-sp<WindowInfoHandle> InputDispatcher::getWindowHandleLocked(
+sp<WindowInfoHandle> InputDispatcher::DispatcherWindowInfo::findWindowHandle(
const sp<IBinder>& windowHandleToken, std::optional<ui::LogicalDisplayId> displayId) const {
if (windowHandleToken == nullptr) {
return nullptr;
@@ -5210,7 +5146,7 @@
}
// Only look through the requested display.
- for (const sp<WindowInfoHandle>& windowHandle : getWindowHandlesLocked(*displayId)) {
+ for (const sp<WindowInfoHandle>& windowHandle : getWindowHandlesForDisplay(*displayId)) {
if (windowHandle->getToken() == windowHandleToken) {
return windowHandle;
}
@@ -5218,7 +5154,7 @@
return nullptr;
}
-sp<WindowInfoHandle> InputDispatcher::getWindowHandleLocked(
+bool InputDispatcher::DispatcherWindowInfo::isWindowPresent(
const sp<WindowInfoHandle>& windowHandle) const {
for (const auto& [displayId, windowHandles] : mWindowHandlesByDisplay) {
for (const sp<WindowInfoHandle>& handle : windowHandles) {
@@ -5230,25 +5166,108 @@
windowHandle->getName().c_str(), displayId.toString().c_str(),
windowHandle->getInfo()->displayId.toString().c_str());
}
- return handle;
+ return true;
}
}
}
- return nullptr;
+ return false;
}
sp<WindowInfoHandle> InputDispatcher::getFocusedWindowHandleLocked(
ui::LogicalDisplayId displayId) const {
sp<IBinder> focusedToken = mFocusResolver.getFocusedWindowToken(displayId);
- return getWindowHandleLocked(focusedToken, displayId);
+ return mWindowInfos.findWindowHandle(focusedToken, displayId);
}
-ui::Transform InputDispatcher::getTransformLocked(ui::LogicalDisplayId displayId) const {
+void InputDispatcher::DispatcherWindowInfo::setWindowHandlesForDisplay(
+ ui::LogicalDisplayId displayId, std::vector<sp<WindowInfoHandle>>&& windowHandles) {
+ // Insert or replace
+ mWindowHandlesByDisplay[displayId] = std::move(windowHandles);
+}
+
+void InputDispatcher::DispatcherWindowInfo::setDisplayInfos(
+ const std::vector<android::gui::DisplayInfo>& displayInfos) {
+ mDisplayInfos.clear();
+ for (const auto& displayInfo : displayInfos) {
+ mDisplayInfos.emplace(displayInfo.displayId, displayInfo);
+ }
+}
+
+void InputDispatcher::DispatcherWindowInfo::removeDisplay(ui::LogicalDisplayId displayId) {
+ mWindowHandlesByDisplay.erase(displayId);
+}
+
+const std::vector<sp<android::gui::WindowInfoHandle>>&
+InputDispatcher::DispatcherWindowInfo::getWindowHandlesForDisplay(
+ ui::LogicalDisplayId displayId) const {
+ static const std::vector<sp<WindowInfoHandle>> EMPTY_WINDOW_HANDLES;
+ const auto it = mWindowHandlesByDisplay.find(displayId);
+ return it != mWindowHandlesByDisplay.end() ? it->second : EMPTY_WINDOW_HANDLES;
+}
+
+void InputDispatcher::DispatcherWindowInfo::forEachWindowHandle(
+ std::function<void(const sp<android::gui::WindowInfoHandle>&)> f) const {
+ for (const auto& [_, windowHandles] : mWindowHandlesByDisplay) {
+ for (const auto& windowHandle : windowHandles) {
+ f(windowHandle);
+ }
+ }
+}
+
+void InputDispatcher::DispatcherWindowInfo::forEachDisplayId(
+ std::function<void(ui::LogicalDisplayId)> f) const {
+ for (const auto& [displayId, _] : mWindowHandlesByDisplay) {
+ f(displayId);
+ }
+}
+
+ui::Transform InputDispatcher::DispatcherWindowInfo::getDisplayTransform(
+ ui::LogicalDisplayId displayId) const {
auto displayInfoIt = mDisplayInfos.find(displayId);
return displayInfoIt != mDisplayInfos.end() ? displayInfoIt->second.transform
: kIdentityTransform;
}
+ui::Transform InputDispatcher::DispatcherWindowInfo::getRawTransform(
+ const android::gui::WindowInfo& windowInfo) const {
+ // If the window has a cloneLayerStackTransform, always use it as the transform for the "getRaw"
+ // APIs. If not, fall back to using the DisplayInfo transform of the window's display.
+ return (input_flags::use_cloned_screen_coordinates_as_raw() &&
+ windowInfo.cloneLayerStackTransform)
+ ? *windowInfo.cloneLayerStackTransform
+ : getDisplayTransform(windowInfo.displayId);
+}
+
+std::string InputDispatcher::DispatcherWindowInfo::dumpDisplayAndWindowInfo() const {
+ std::string dump;
+ if (!mWindowHandlesByDisplay.empty()) {
+ for (const auto& [displayId, windowHandles] : mWindowHandlesByDisplay) {
+ dump += StringPrintf("Display: %s\n", displayId.toString().c_str());
+ if (const auto& it = mDisplayInfos.find(displayId); it != mDisplayInfos.end()) {
+ const auto& displayInfo = it->second;
+ dump += StringPrintf(INDENT "logicalSize=%dx%d\n", displayInfo.logicalWidth,
+ displayInfo.logicalHeight);
+ displayInfo.transform.dump(dump, "transform", INDENT3);
+ } else {
+ dump += INDENT "No DisplayInfo found!\n";
+ }
+
+ if (!windowHandles.empty()) {
+ dump += INDENT "Windows:\n";
+ for (size_t i = 0; i < windowHandles.size(); i++) {
+ dump += StringPrintf(INDENT2 "%zu: %s", i,
+ streamableToString(*windowHandles[i]).c_str());
+ }
+ } else {
+ dump += INDENT "Windows: <none>\n";
+ }
+ }
+ } else {
+ dump += "Displays: <none>\n";
+ }
+ return dump;
+}
+
bool InputDispatcher::canWindowReceiveMotionLocked(const sp<WindowInfoHandle>& window,
const MotionEntry& motionEntry) const {
const WindowInfo& info = *window->getInfo();
@@ -5316,13 +5335,14 @@
ui::LogicalDisplayId displayId) {
if (windowInfoHandles.empty()) {
// Remove all handles on a display if there are no windows left.
- mWindowHandlesByDisplay.erase(displayId);
+ mWindowInfos.removeDisplay(displayId);
return;
}
// Since we compare the pointer of input window handles across window updates, we need
// to make sure the handle object for the same window stays unchanged across updates.
- const std::vector<sp<WindowInfoHandle>>& oldHandles = getWindowHandlesLocked(displayId);
+ const std::vector<sp<WindowInfoHandle>>& oldHandles =
+ mWindowInfos.getWindowHandlesForDisplay(displayId);
std::unordered_map<int32_t /*id*/, sp<WindowInfoHandle>> oldHandlesById;
for (const sp<WindowInfoHandle>& handle : oldHandles) {
oldHandlesById[handle->getId()] = handle;
@@ -5361,8 +5381,7 @@
}
}
- // Insert or replace
- mWindowHandlesByDisplay[displayId] = newHandles;
+ mWindowInfos.setWindowHandlesForDisplay(displayId, std::move(newHandles));
}
/**
@@ -5412,12 +5431,14 @@
}
// Copy old handles for release if they are no longer present.
- const std::vector<sp<WindowInfoHandle>> oldWindowHandles = getWindowHandlesLocked(displayId);
+ const std::vector<sp<WindowInfoHandle>> oldWindowHandles =
+ mWindowInfos.getWindowHandlesForDisplay(displayId);
const sp<WindowInfoHandle> removedFocusedWindowHandle = getFocusedWindowHandleLocked(displayId);
updateWindowHandlesForDisplayLocked(windowInfoHandles, displayId);
- const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);
+ const std::vector<sp<WindowInfoHandle>>& windowHandles =
+ mWindowInfos.getWindowHandlesForDisplay(displayId);
std::optional<FocusResolver::FocusChanges> changes =
mFocusResolver.setInputWindows(displayId, windowHandles);
@@ -5429,7 +5450,7 @@
TouchState& state = it->second;
for (size_t i = 0; i < state.windows.size();) {
TouchedWindow& touchedWindow = state.windows[i];
- if (getWindowHandleLocked(touchedWindow.windowHandle) != nullptr) {
+ if (mWindowInfos.isWindowPresent(touchedWindow.windowHandle)) {
i++;
continue;
}
@@ -5473,7 +5494,8 @@
[this, displayId, &touchedWindow](const PointerProperties& properties, float x,
float y) REQUIRES(mLock) {
const bool isStylus = properties.toolType == ToolType::STYLUS;
- const ui::Transform displayTransform = getTransformLocked(displayId);
+ const ui::Transform displayTransform =
+ mWindowInfos.getDisplayTransform(displayId);
const bool stillAcceptsTouch =
windowAcceptsTouchAt(*touchedWindow.windowHandle->getInfo(),
displayId, x, y, isStylus, displayTransform);
@@ -5495,7 +5517,7 @@
// Otherwise, they might stick around until the window handle is destroyed
// which might not happen until the next GC.
for (const sp<WindowInfoHandle>& oldWindowHandle : oldWindowHandles) {
- if (getWindowHandleLocked(oldWindowHandle) == nullptr) {
+ if (!mWindowInfos.isWindowPresent(oldWindowHandle)) {
if (DEBUG_FOCUS) {
ALOGD("Window went away: %s", oldWindowHandle->getName().c_str());
}
@@ -5572,7 +5594,7 @@
mFocusResolver.getFocusedWindowToken(mFocusedDisplayId);
if (oldFocusedWindowToken != nullptr) {
const auto windowHandle =
- getWindowHandleLocked(oldFocusedWindowToken, mFocusedDisplayId);
+ mWindowInfos.findWindowHandle(oldFocusedWindowToken, mFocusedDisplayId);
if (windowHandle == nullptr) {
LOG(FATAL) << __func__ << ": Previously focused token did not have a window";
}
@@ -5711,7 +5733,7 @@
if (focusedToken == nullptr) {
return false;
}
- sp<WindowInfoHandle> windowHandle = getWindowHandleLocked(focusedToken);
+ sp<WindowInfoHandle> windowHandle = mWindowInfos.findWindowHandle(focusedToken);
return isWindowOwnedBy(windowHandle, pid, uid);
}
@@ -5719,7 +5741,7 @@
return std::find_if(mInteractionConnectionTokens.begin(), mInteractionConnectionTokens.end(),
[&](const sp<IBinder>& connectionToken) REQUIRES(mLock) {
const sp<WindowInfoHandle> windowHandle =
- getWindowHandleLocked(connectionToken);
+ mWindowInfos.findWindowHandle(connectionToken);
return isWindowOwnedBy(windowHandle, pid, uid);
}) != mInteractionConnectionTokens.end();
}
@@ -5734,10 +5756,12 @@
mMaximumObscuringOpacityForTouch = opacity;
}
-std::tuple<TouchState*, TouchedWindow*, ui::LogicalDisplayId>
-InputDispatcher::findTouchStateWindowAndDisplayLocked(const sp<IBinder>& token) {
- for (auto& [displayId, state] : mTouchStatesByDisplay) {
- for (TouchedWindow& w : state.windows) {
+std::tuple<const TouchState*, const TouchedWindow*, ui::LogicalDisplayId>
+InputDispatcher::findTouchStateWindowAndDisplay(
+ const sp<IBinder>& token,
+ const std::unordered_map<ui::LogicalDisplayId, TouchState>& touchStatesByDisplay) {
+ for (auto& [displayId, state] : touchStatesByDisplay) {
+ for (const TouchedWindow& w : state.windows) {
if (w.windowHandle->getToken() == token) {
return std::make_tuple(&state, &w, displayId);
}
@@ -5746,20 +5770,18 @@
return std::make_tuple(nullptr, nullptr, ui::LogicalDisplayId::DEFAULT);
}
-std::tuple<const TouchState*, const TouchedWindow*, ui::LogicalDisplayId>
-InputDispatcher::findTouchStateWindowAndDisplayLocked(const sp<IBinder>& token) const {
- return const_cast<InputDispatcher*>(this)->findTouchStateWindowAndDisplayLocked(token);
-}
+std::tuple<TouchState*, TouchedWindow*, ui::LogicalDisplayId>
+InputDispatcher::findTouchStateWindowAndDisplay(
+ const sp<IBinder>& token,
+ std::unordered_map<ui::LogicalDisplayId, TouchState>& touchStatesByDisplay) {
+ auto [constTouchState, constTouchedWindow, displayId] = InputDispatcher::
+ findTouchStateWindowAndDisplay(token,
+ const_cast<const std::unordered_map<ui::LogicalDisplayId,
+ TouchState>&>(
+ touchStatesByDisplay));
-bool InputDispatcher::windowHasTouchingPointersLocked(const sp<WindowInfoHandle>& windowHandle,
- DeviceId deviceId) const {
- const auto& [touchState, touchedWindow, _] =
- findTouchStateWindowAndDisplayLocked(windowHandle->getToken());
- if (touchState == nullptr) {
- // No touching pointers at all
- return false;
- }
- return touchState->hasTouchingPointers(deviceId);
+ return std::make_tuple(const_cast<TouchState*>(constTouchState),
+ const_cast<TouchedWindow*>(constTouchedWindow), displayId);
}
bool InputDispatcher::transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
@@ -5775,7 +5797,8 @@
std::scoped_lock _l(mLock);
// Find the target touch state and touched window by fromToken.
- auto [state, touchedWindow, displayId] = findTouchStateWindowAndDisplayLocked(fromToken);
+ auto [state, touchedWindow, displayId] =
+ findTouchStateWindowAndDisplay(fromToken, mTouchStatesByDisplay);
if (state == nullptr || touchedWindow == nullptr) {
ALOGD("Touch transfer failed because from window is not being touched.");
@@ -5790,7 +5813,8 @@
const DeviceId deviceId = *deviceIds.begin();
const sp<WindowInfoHandle> fromWindowHandle = touchedWindow->windowHandle;
- const sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(toToken, displayId);
+ const sp<WindowInfoHandle> toWindowHandle =
+ mWindowInfos.findWindowHandle(toToken, displayId);
if (!toWindowHandle) {
ALOGW("Cannot transfer touch because the transfer target window was not found.");
return false;
@@ -5866,10 +5890,11 @@
* Return null if there are no windows touched on that display, or if more than one foreground
* window is being touched.
*/
-sp<WindowInfoHandle> InputDispatcher::findTouchedForegroundWindowLocked(
- ui::LogicalDisplayId displayId) const {
- auto stateIt = mTouchStatesByDisplay.find(displayId);
- if (stateIt == mTouchStatesByDisplay.end()) {
+sp<WindowInfoHandle> InputDispatcher::findTouchedForegroundWindow(
+ const std::unordered_map<ui::LogicalDisplayId, TouchState>& touchStatesByDisplay,
+ ui::LogicalDisplayId displayId) {
+ const auto stateIt = touchStatesByDisplay.find(displayId);
+ if (stateIt == touchStatesByDisplay.end()) {
ALOGI("No touch state on display %s", displayId.toString().c_str());
return nullptr;
}
@@ -5897,14 +5922,15 @@
sp<IBinder> fromToken;
{ // acquire lock
std::scoped_lock _l(mLock);
- sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(destChannelToken, displayId);
+ sp<WindowInfoHandle> toWindowHandle =
+ mWindowInfos.findWindowHandle(destChannelToken, displayId);
if (toWindowHandle == nullptr) {
ALOGW("Could not find window associated with token=%p on display %s",
destChannelToken.get(), displayId.toString().c_str());
return false;
}
- sp<WindowInfoHandle> from = findTouchedForegroundWindowLocked(displayId);
+ sp<WindowInfoHandle> from = findTouchedForegroundWindow(mTouchStatesByDisplay, displayId);
if (from == nullptr) {
ALOGE("Could not find a source window in %s for %p", __func__, destChannelToken.get());
return false;
@@ -5956,7 +5982,7 @@
std::string windowName = "None";
if (mWindowTokenWithPointerCapture) {
const sp<WindowInfoHandle> captureWindowHandle =
- getWindowHandleLocked(mWindowTokenWithPointerCapture);
+ mWindowInfos.findWindowHandle(mWindowTokenWithPointerCapture);
windowName = captureWindowHandle ? captureWindowHandle->getName().c_str()
: "token has capture without window";
}
@@ -6005,31 +6031,7 @@
mDragState->dump(dump, INDENT2);
}
- if (!mWindowHandlesByDisplay.empty()) {
- for (const auto& [displayId, windowHandles] : mWindowHandlesByDisplay) {
- dump += StringPrintf(INDENT "Display: %s\n", displayId.toString().c_str());
- if (const auto& it = mDisplayInfos.find(displayId); it != mDisplayInfos.end()) {
- const auto& displayInfo = it->second;
- dump += StringPrintf(INDENT2 "logicalSize=%dx%d\n", displayInfo.logicalWidth,
- displayInfo.logicalHeight);
- displayInfo.transform.dump(dump, "transform", INDENT4);
- } else {
- dump += INDENT2 "No DisplayInfo found!\n";
- }
-
- if (!windowHandles.empty()) {
- dump += INDENT2 "Windows:\n";
- for (size_t i = 0; i < windowHandles.size(); i++) {
- dump += StringPrintf(INDENT3 "%zu: %s", i,
- streamableToString(*windowHandles[i]).c_str());
- }
- } else {
- dump += INDENT2 "Windows: <none>\n";
- }
- }
- } else {
- dump += INDENT "Displays: <none>\n";
- }
+ dump += addLinePrefix(mWindowInfos.dumpDisplayAndWindowInfo(), INDENT);
if (!mGlobalMonitorsByDisplay.empty()) {
for (const auto& [displayId, monitors] : mGlobalMonitorsByDisplay) {
@@ -6237,8 +6239,14 @@
status_t InputDispatcher::removeInputChannel(const sp<IBinder>& connectionToken) {
{ // acquire lock
std::scoped_lock _l(mLock);
+ std::shared_ptr<Connection> connection = getConnectionLocked(connectionToken);
+ if (connection == nullptr) {
+ // Connection can be removed via socket hang up or an explicit call to
+ // 'removeInputChannel'
+ return BAD_VALUE;
+ }
- status_t status = removeInputChannelLocked(connectionToken, /*notify=*/false);
+ status_t status = removeInputChannelLocked(connection, /*notify=*/false);
if (status) {
return status;
}
@@ -6250,25 +6258,18 @@
return OK;
}
-status_t InputDispatcher::removeInputChannelLocked(const sp<IBinder>& connectionToken,
+status_t InputDispatcher::removeInputChannelLocked(const std::shared_ptr<Connection>& connection,
bool notify) {
- std::shared_ptr<Connection> connection = getConnectionLocked(connectionToken);
- if (connection == nullptr) {
- // Connection can be removed via socket hang up or an explicit call to 'removeInputChannel'
- return BAD_VALUE;
- }
-
+ LOG_ALWAYS_FATAL_IF(connection == nullptr);
+ abortBrokenDispatchCycleLocked(connection, notify);
removeConnectionLocked(connection);
if (connection->monitor) {
- removeMonitorChannelLocked(connectionToken);
+ removeMonitorChannelLocked(connection->getToken());
}
mLooper->removeFd(connection->inputPublisher.getChannel().getFd());
- nsecs_t currentTime = now();
- abortBrokenDispatchCycleLocked(currentTime, connection, notify);
-
connection->status = Connection::Status::ZOMBIE;
return OK;
}
@@ -6301,7 +6302,8 @@
return BAD_VALUE;
}
- auto [statePtr, windowPtr, displayId] = findTouchStateWindowAndDisplayLocked(token);
+ auto [statePtr, windowPtr, displayId] =
+ findTouchStateWindowAndDisplay(token, mTouchStatesByDisplay);
if (statePtr == nullptr || windowPtr == nullptr) {
LOG(WARNING)
<< "Attempted to pilfer points from a channel without any on-going pointer streams."
@@ -6353,7 +6355,7 @@
{ // acquire lock
std::scoped_lock _l(mLock);
if (DEBUG_FOCUS) {
- const sp<WindowInfoHandle> windowHandle = getWindowHandleLocked(windowToken);
+ const sp<WindowInfoHandle> windowHandle = mWindowInfos.findWindowHandle(windowToken);
ALOGI("Request to %s Pointer Capture from: %s.", enabled ? "enable" : "disable",
windowHandle != nullptr ? windowHandle->getName().c_str()
: "token without window");
@@ -6493,17 +6495,17 @@
}
traceWaitQueueLength(*connection);
if (fallbackKeyEntry && connection->status == Connection::Status::NORMAL) {
- const auto windowHandle = getWindowHandleLocked(connection->getToken());
+ const auto windowHandle = mWindowInfos.findWindowHandle(connection->getToken());
// Only dispatch fallbacks if there is a window for the connection.
if (windowHandle != nullptr) {
- const auto inputTarget =
- createInputTargetLocked(windowHandle, InputTarget::DispatchMode::AS_IS,
- dispatchEntry->targetFlags,
- fallbackKeyEntry->downTime);
- if (inputTarget.has_value()) {
- enqueueDispatchEntryLocked(connection, std::move(fallbackKeyEntry),
- *inputTarget);
- }
+ nsecs_t downTime = fallbackKeyEntry->downTime;
+ enqueueDispatchEntryLocked(connection, std::move(fallbackKeyEntry),
+ createInputTarget(connection, windowHandle,
+ InputTarget::DispatchMode::AS_IS,
+ dispatchEntry->targetFlags,
+ mWindowInfos.getRawTransform(
+ *windowHandle->getInfo()),
+ downTime));
}
}
releaseDispatchEntry(std::move(dispatchEntry));
@@ -6557,7 +6559,7 @@
ns2ms(currentWait),
oldestEntry.eventEntry->getDescription().c_str());
sp<IBinder> connectionToken = connection->getToken();
- updateLastAnrStateLocked(getWindowHandleLocked(connectionToken), reason);
+ updateLastAnrStateLocked(mWindowInfos.findWindowHandle(connectionToken), reason);
processConnectionUnresponsiveLocked(*connection, std::move(reason));
@@ -6608,24 +6610,27 @@
void InputDispatcher::doInterceptKeyBeforeDispatchingCommand(const sp<IBinder>& focusedWindowToken,
const KeyEntry& entry) {
const KeyEvent event = createKeyEvent(entry);
+ std::variant<nsecs_t, KeyEntry::InterceptKeyResult> interceptResult;
nsecs_t delay = 0;
{ // release lock
scoped_unlock unlock(mLock);
android::base::Timer t;
- delay = mPolicy.interceptKeyBeforeDispatching(focusedWindowToken, event, entry.policyFlags);
+ interceptResult =
+ mPolicy.interceptKeyBeforeDispatching(focusedWindowToken, event, entry.policyFlags);
if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
ALOGW("Excessive delay in interceptKeyBeforeDispatching; took %s ms",
std::to_string(t.duration().count()).c_str());
}
} // acquire lock
- if (delay < 0) {
- entry.interceptKeyResult = KeyEntry::InterceptKeyResult::SKIP;
- } else if (delay == 0) {
- entry.interceptKeyResult = KeyEntry::InterceptKeyResult::CONTINUE;
- } else {
+ if (std::holds_alternative<KeyEntry::InterceptKeyResult>(interceptResult)) {
+ entry.interceptKeyResult = std::get<KeyEntry::InterceptKeyResult>(interceptResult);
+ return;
+ }
+
+ if (std::holds_alternative<nsecs_t>(interceptResult)) {
entry.interceptKeyResult = KeyEntry::InterceptKeyResult::TRY_AGAIN_LATER;
- entry.interceptKeyWakeupTime = now() + delay;
+ entry.interceptKeyWakeupTime = now() + std::get<nsecs_t>(interceptResult);
}
}
@@ -6665,7 +6670,7 @@
// The connection is a window
ALOGW("Window %s is unresponsive: %s", connection.getInputChannelName().c_str(),
reason.c_str());
- const sp<WindowInfoHandle> handle = getWindowHandleLocked(connectionToken);
+ const sp<WindowInfoHandle> handle = mWindowInfos.findWindowHandle(connectionToken);
if (handle != nullptr) {
pid = handle->getInfo()->ownerPid;
}
@@ -6683,7 +6688,7 @@
pid = findMonitorPidByTokenLocked(connectionToken);
} else {
// The connection is a window
- const sp<WindowInfoHandle> handle = getWindowHandleLocked(connectionToken);
+ const sp<WindowInfoHandle> handle = mWindowInfos.findWindowHandle(connectionToken);
if (handle != nullptr) {
pid = handle->getInfo()->ownerPid;
}
@@ -6748,7 +6753,7 @@
// Cancel the fallback key, but only if we still have a window for the channel.
// It could have been removed during the policy call.
if (*fallbackKeyCode != AKEYCODE_UNKNOWN) {
- const auto windowHandle = getWindowHandleLocked(connection->getToken());
+ const auto windowHandle = mWindowInfos.findWindowHandle(connection->getToken());
if (windowHandle != nullptr) {
CancelationOptions options(CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS,
"application handled the original non-fallback key "
@@ -6834,7 +6839,7 @@
}
}
- const auto windowHandle = getWindowHandleLocked(connection->getToken());
+ const auto windowHandle = mWindowInfos.findWindowHandle(connection->getToken());
if (windowHandle != nullptr) {
CancelationOptions options(CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS,
"canceling fallback, policy no longer desires it",
@@ -6976,7 +6981,7 @@
std::scoped_lock _l(mLock);
std::optional<FocusResolver::FocusChanges> changes =
mFocusResolver.setFocusedWindow(request,
- getWindowHandlesLocked(
+ mWindowInfos.getWindowHandlesForDisplay(
ui::LogicalDisplayId{request.displayId}));
ScopedSyntheticEventTracer traceContext(mTracer);
if (changes) {
@@ -6994,7 +6999,7 @@
if (changes.oldFocus) {
const auto resolvedWindow = removedFocusedWindowHandle != nullptr
? removedFocusedWindowHandle
- : getWindowHandleLocked(changes.oldFocus, changes.displayId);
+ : mWindowInfos.findWindowHandle(changes.oldFocus, changes.displayId);
if (resolvedWindow == nullptr) {
LOG(FATAL) << __func__ << ": Previously focused token did not have a window";
}
@@ -7098,13 +7103,6 @@
for (const auto& info : update.windowInfos) {
handlesPerDisplay.emplace(info.displayId, std::vector<sp<WindowInfoHandle>>());
handlesPerDisplay[info.displayId].push_back(sp<WindowInfoHandle>::make(info));
- if (input_flags::split_all_touches()) {
- handlesPerDisplay[info.displayId]
- .back()
- ->editInfo()
- ->setInputConfig(android::gui::WindowInfo::InputConfig::PREVENT_SPLITTING,
- false);
- }
}
{ // acquire lock
@@ -7112,14 +7110,10 @@
// Ensure that we have an entry created for all existing displays so that if a displayId has
// no windows, we can tell that the windows were removed from the display.
- for (const auto& [displayId, _] : mWindowHandlesByDisplay) {
- handlesPerDisplay[displayId];
- }
+ mWindowInfos.forEachDisplayId(
+ [&](ui::LogicalDisplayId displayId) { handlesPerDisplay[displayId]; });
- mDisplayInfos.clear();
- for (const auto& displayInfo : update.displayInfos) {
- mDisplayInfos.emplace(displayInfo.displayId, displayInfo);
- }
+ mWindowInfos.setDisplayInfos(update.displayInfos);
for (const auto& [displayId, handles] : handlesPerDisplay) {
setInputWindowsLocked(handles, displayId);
@@ -7266,7 +7260,7 @@
sp<WindowInfoHandle> InputDispatcher::findWallpaperWindowBelow(
const sp<WindowInfoHandle>& windowHandle) const {
const std::vector<sp<WindowInfoHandle>>& windowHandles =
- getWindowHandlesLocked(windowHandle->getInfo()->displayId);
+ mWindowInfos.getWindowHandlesForDisplay(windowHandle->getInfo()->displayId);
bool foundWindow = false;
for (const sp<WindowInfoHandle>& otherHandle : windowHandles) {
if (!foundWindow && otherHandle != windowHandle) {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index fade853..bca1c67 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -252,19 +252,13 @@
// to transfer focus to a new application.
std::shared_ptr<const EventEntry> mNextUnblockedEvent GUARDED_BY(mLock);
- sp<android::gui::WindowInfoHandle> findTouchedWindowAtLocked(
- ui::LogicalDisplayId displayId, float x, float y, bool isStylus = false,
- bool ignoreDragWindow = false) const REQUIRES(mLock);
std::vector<InputTarget> findOutsideTargetsLocked(
ui::LogicalDisplayId displayId, const sp<android::gui::WindowInfoHandle>& touchedWindow,
int32_t pointerId) const REQUIRES(mLock);
- std::vector<sp<android::gui::WindowInfoHandle>> findTouchedSpyWindowsAtLocked(
- ui::LogicalDisplayId displayId, float x, float y, bool isStylus,
- DeviceId deviceId) const REQUIRES(mLock);
-
- sp<android::gui::WindowInfoHandle> findTouchedForegroundWindowLocked(
- ui::LogicalDisplayId displayId) const REQUIRES(mLock);
+ static sp<android::gui::WindowInfoHandle> findTouchedForegroundWindow(
+ const std::unordered_map<ui::LogicalDisplayId, TouchState>& touchStatesByDisplay,
+ ui::LogicalDisplayId displayId);
std::shared_ptr<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) const
REQUIRES(mLock);
@@ -368,24 +362,65 @@
};
sp<gui::WindowInfosListener> mWindowInfoListener;
- std::unordered_map<ui::LogicalDisplayId /*displayId*/,
- std::vector<sp<android::gui::WindowInfoHandle>>>
- mWindowHandlesByDisplay GUARDED_BY(mLock);
- std::unordered_map<ui::LogicalDisplayId /*displayId*/, android::gui::DisplayInfo> mDisplayInfos
- GUARDED_BY(mLock);
+ class DispatcherWindowInfo {
+ public:
+ void setWindowHandlesForDisplay(
+ ui::LogicalDisplayId displayId,
+ std::vector<sp<android::gui::WindowInfoHandle>>&& windowHandles);
+
+ void setDisplayInfos(const std::vector<android::gui::DisplayInfo>& displayInfos);
+
+ void removeDisplay(ui::LogicalDisplayId displayId);
+
+ // Get a reference to window handles by display, return an empty vector if not found.
+ const std::vector<sp<android::gui::WindowInfoHandle>>& getWindowHandlesForDisplay(
+ ui::LogicalDisplayId displayId) const;
+
+ void forEachWindowHandle(
+ std::function<void(const sp<android::gui::WindowInfoHandle>&)> f) const;
+
+ void forEachDisplayId(std::function<void(ui::LogicalDisplayId)> f) const;
+
+ // Get the transform for display, returns Identity-transform if display is missing.
+ ui::Transform getDisplayTransform(ui::LogicalDisplayId displayId) const;
+
+ // Get the raw transform to use for motion events going to the given window.
+ ui::Transform getRawTransform(const android::gui::WindowInfo&) const;
+
+ // Lookup for WindowInfoHandle from token and optionally a display-id. In cases where
+ // display-id is not provided lookup is done for all displays.
+ sp<android::gui::WindowInfoHandle> findWindowHandle(
+ const sp<IBinder>& windowHandleToken,
+ std::optional<ui::LogicalDisplayId> displayId = {}) const;
+
+ bool isWindowPresent(const sp<android::gui::WindowInfoHandle>& windowHandle) const;
+
+ // Returns the touched window at the given location, excluding the ignoreWindow if provided.
+ sp<android::gui::WindowInfoHandle> findTouchedWindowAt(
+ ui::LogicalDisplayId displayId, float x, float y, bool isStylus = false,
+ const sp<android::gui::WindowInfoHandle> ignoreWindow = nullptr) const;
+
+ std::vector<sp<android::gui::WindowInfoHandle>> findTouchedSpyWindowsAt(
+ ui::LogicalDisplayId displayId, float x, float y, bool isStylus, DeviceId deviceId,
+ const std::unordered_map<ui::LogicalDisplayId, TouchState>& touchStatesByDisplay)
+ const;
+
+ std::string dumpDisplayAndWindowInfo() const;
+
+ private:
+ std::unordered_map<ui::LogicalDisplayId /*displayId*/,
+ std::vector<sp<android::gui::WindowInfoHandle>>>
+ mWindowHandlesByDisplay;
+ std::unordered_map<ui::LogicalDisplayId /*displayId*/, android::gui::DisplayInfo>
+ mDisplayInfos;
+ };
+
+ DispatcherWindowInfo mWindowInfos GUARDED_BY(mLock);
+
void setInputWindowsLocked(
const std::vector<sp<android::gui::WindowInfoHandle>>& inputWindowHandles,
ui::LogicalDisplayId displayId) REQUIRES(mLock);
- // Get a reference to window handles by display, return an empty vector if not found.
- const std::vector<sp<android::gui::WindowInfoHandle>>& getWindowHandlesLocked(
- ui::LogicalDisplayId displayId) const REQUIRES(mLock);
- ui::Transform getTransformLocked(ui::LogicalDisplayId displayId) const REQUIRES(mLock);
- sp<android::gui::WindowInfoHandle> getWindowHandleLocked(
- const sp<IBinder>& windowHandleToken,
- std::optional<ui::LogicalDisplayId> displayId = {}) const REQUIRES(mLock);
- sp<android::gui::WindowInfoHandle> getWindowHandleLocked(
- const sp<android::gui::WindowInfoHandle>& windowHandle) const REQUIRES(mLock);
sp<android::gui::WindowInfoHandle> getFocusedWindowHandleLocked(
ui::LogicalDisplayId displayId) const REQUIRES(mLock);
bool canWindowReceiveMotionLocked(const sp<android::gui::WindowInfoHandle>& window,
@@ -550,10 +585,6 @@
std::vector<Monitor> selectResponsiveMonitorsLocked(
const std::vector<Monitor>& gestureMonitors) const REQUIRES(mLock);
- std::optional<InputTarget> createInputTargetLocked(
- const sp<android::gui::WindowInfoHandle>& windowHandle,
- InputTarget::DispatchMode dispatchMode, ftl::Flags<InputTarget::Flags> targetFlags,
- std::optional<nsecs_t> firstDownTimeInTarget) const REQUIRES(mLock);
void addWindowTargetLocked(const sp<android::gui::WindowInfoHandle>& windowHandle,
InputTarget::DispatchMode dispatchMode,
ftl::Flags<InputTarget::Flags> targetFlags,
@@ -618,8 +649,7 @@
void finishDispatchCycleLocked(nsecs_t currentTime,
const std::shared_ptr<Connection>& connection, uint32_t seq,
bool handled, nsecs_t consumeTime) REQUIRES(mLock);
- void abortBrokenDispatchCycleLocked(nsecs_t currentTime,
- const std::shared_ptr<Connection>& connection, bool notify)
+ void abortBrokenDispatchCycleLocked(const std::shared_ptr<Connection>& connection, bool notify)
REQUIRES(mLock);
void drainDispatchQueue(std::deque<std::unique_ptr<DispatchEntry>>& queue);
void releaseDispatchEntry(std::unique_ptr<DispatchEntry> dispatchEntry);
@@ -665,7 +695,7 @@
// Registration.
void removeMonitorChannelLocked(const sp<IBinder>& connectionToken) REQUIRES(mLock);
- status_t removeInputChannelLocked(const sp<IBinder>& connectionToken, bool notify)
+ status_t removeInputChannelLocked(const std::shared_ptr<Connection>& connection, bool notify)
REQUIRES(mLock);
// Interesting events that we might like to log or tell the framework about.
@@ -696,13 +726,15 @@
bool handled) REQUIRES(mLock);
// Find touched state and touched window by token.
- std::tuple<TouchState*, TouchedWindow*, ui::LogicalDisplayId>
- findTouchStateWindowAndDisplayLocked(const sp<IBinder>& token) REQUIRES(mLock);
+ static std::tuple<TouchState*, TouchedWindow*, ui::LogicalDisplayId>
+ findTouchStateWindowAndDisplay(
+ const sp<IBinder>& token,
+ std::unordered_map<ui::LogicalDisplayId, TouchState>& touchStatesByDisplay);
- std::tuple<const TouchState*, const TouchedWindow*, ui::LogicalDisplayId>
- findTouchStateWindowAndDisplayLocked(const sp<IBinder>& token) const REQUIRES(mLock);
- bool windowHasTouchingPointersLocked(const sp<android::gui::WindowInfoHandle>& windowHandle,
- DeviceId deviceId) const REQUIRES(mLock);
+ static std::tuple<const TouchState*, const TouchedWindow*, ui::LogicalDisplayId>
+ findTouchStateWindowAndDisplay(
+ const sp<IBinder>& token,
+ const std::unordered_map<ui::LogicalDisplayId, TouchState>& touchStatesByDisplay);
// Statistics gathering.
nsecs_t mLastStatisticPushTime = 0;
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index 90374f1..76f3fe0 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -77,8 +77,8 @@
// (ignored for KeyEvents)
float globalScaleFactor = 1.0f;
- // Current display transform. Used for compatibility for raw coordinates.
- ui::Transform displayTransform;
+ // The raw coordinate transform that's used for compatibility for MotionEvent's getRaw APIs.
+ ui::Transform rawTransform;
// Event time for the first motion event (ACTION_DOWN) dispatched to this input target if
// FLAG_SPLIT is set.
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index b885ba1..5dcd984 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -20,12 +20,14 @@
#include <android-base/properties.h>
#include <binder/IBinder.h>
+#include <dispatcher/Entry.h>
#include <gui/InputApplication.h>
#include <gui/PidUid.h>
#include <input/Input.h>
#include <input/InputDevice.h>
#include <utils/RefBase.h>
#include <set>
+#include <variant>
namespace android {
@@ -106,9 +108,9 @@
uint32_t& policyFlags) = 0;
/* Allows the policy a chance to intercept a key before dispatching. */
- virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>& token,
- const KeyEvent& keyEvent,
- uint32_t policyFlags) = 0;
+ virtual std::variant<nsecs_t, inputdispatcher::KeyEntry::InterceptKeyResult>
+ interceptKeyBeforeDispatching(const sp<IBinder>& token, const KeyEvent& keyEvent,
+ uint32_t policyFlags) = 0;
/* Allows the policy a chance to perform default processing for an unhandled key.
* Returns an alternate key event to redispatch as a fallback, if needed. */
diff --git a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp
index 0b17507..cc04684 100644
--- a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp
+++ b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp
@@ -115,13 +115,17 @@
for (size_t i = 0; i < motion->pointerProperties.size(); i++) {
auto* pointerProto = outProto.add_dispatched_pointer();
pointerProto->set_pointer_id(motion->pointerProperties[i].id);
+ const auto& coords = motion->pointerCoords[i];
const auto rawXY =
MotionEvent::calculateTransformedXY(motion->source, args.rawTransform,
- motion->pointerCoords[i].getXYValue());
- pointerProto->set_x_in_display(rawXY.x);
- pointerProto->set_y_in_display(rawXY.y);
+ coords.getXYValue());
+ if (coords.getXYValue() != rawXY) {
+ // These values are only traced if they were modified by the raw transform
+ // to save space. Trace consumers should be aware of this optimization.
+ pointerProto->set_x_in_display(rawXY.x);
+ pointerProto->set_y_in_display(rawXY.y);
+ }
- const auto& coords = motion->pointerCoords[i];
const auto coordsInWindow =
MotionEvent::calculateTransformedCoords(motion->source, motion->flags,
args.transform, coords);
@@ -129,6 +133,7 @@
for (int32_t axisIndex = 0; !bits.isEmpty(); axisIndex++) {
const uint32_t axis = bits.clearFirstMarkedBit();
const float axisValueInWindow = coordsInWindow.values[axisIndex];
+ // Only values that are modified by the window transform are traced.
if (coords.values[axisIndex] != axisValueInWindow) {
auto* axisEntry = pointerProto->add_axis_value_in_window();
axisEntry->set_axis(axis);
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 4d6b6c7..f54b76b 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -139,8 +139,16 @@
// The mouse pointer speed, as a number from -7 (slowest) to 7 (fastest).
int32_t mousePointerSpeed;
- // Displays on which an acceleration curve shouldn't be applied for pointer movements from mice.
- std::set<ui::LogicalDisplayId> displaysWithMousePointerAccelerationDisabled;
+ // Displays on which all pointer scaling, including linear scaling based on the
+ // user's pointer speed setting, should be disabled for mice. This differs from
+ // disabling acceleration via the 'mousePointerAccelerationEnabled' setting, where
+ // the pointer speed setting still influences the scaling factor.
+ std::set<ui::LogicalDisplayId> displaysWithMouseScalingDisabled;
+
+ // True if the connected mouse should exhibit pointer acceleration. If false,
+ // a flat acceleration curve (linear scaling) is used, but the user's pointer
+ // speed setting still affects the scaling factor.
+ bool mousePointerAccelerationEnabled;
// Velocity control parameters for touchpad pointer movements on the old touchpad stack (based
// on TouchInputMapper).
@@ -274,12 +282,16 @@
: virtualKeyQuietTime(0),
defaultPointerDisplayId(ui::LogicalDisplayId::DEFAULT),
mousePointerSpeed(0),
- displaysWithMousePointerAccelerationDisabled(),
+ displaysWithMouseScalingDisabled(),
+ mousePointerAccelerationEnabled(true),
pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f,
static_cast<float>(
android::os::IInputConstants::
DEFAULT_POINTER_ACCELERATION)),
- wheelVelocityControlParameters(1.0f, 15.0f, 50.0f, 4.0f),
+ wheelVelocityControlParameters(1.0f, 15.0f, 50.0f,
+ static_cast<float>(
+ android::os::IInputConstants::
+ DEFAULT_MOUSE_WHEEL_ACCELERATION)),
pointerGesturesEnabled(true),
pointerGestureQuietInterval(100 * 1000000LL), // 100 ms
pointerGestureDragMinSwitchSpeed(50), // 50 pixels per second
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index b3cd35c..3934e78 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -79,25 +79,25 @@
srcs: [":libinputreader_sources"],
shared_libs: [
"android.companion.virtualdevice.flags-aconfig-cc",
+ "libPlatformProperties",
"libbase",
"libcap",
"libcrypto",
"libcutils",
- "libjsoncpp",
"libinput",
+ "libjsoncpp",
"liblog",
- "libPlatformProperties",
"libstatslog",
"libstatspull",
- "libutils",
"libstatssocket",
+ "libutils",
],
static_libs: [
"libchrome-gestures",
- "libui-types",
"libexpresslog",
- "libtextclassifier_hash_static",
"libstatslog_express",
+ "libtextclassifier_hash_static",
+ "libui-types",
],
header_libs: [
"libbatteryservice_headers",
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index b33659c..9f584a0 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -481,15 +481,21 @@
mPointerVelocityControl.setAccelerationEnabled(false);
mWheelXVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS);
mWheelYVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS);
- } else {
- mPointerVelocityControl.setAccelerationEnabled(
- config.displaysWithMousePointerAccelerationDisabled.count(
- mDisplayId.value_or(ui::LogicalDisplayId::INVALID)) == 0);
- mPointerVelocityControl.setCurve(
- createAccelerationCurveForPointerSensitivity(config.mousePointerSpeed));
- mWheelXVelocityControl.setParameters(config.wheelVelocityControlParameters);
- mWheelYVelocityControl.setParameters(config.wheelVelocityControlParameters);
+ return;
}
+
+ bool disableAllScaling = config.displaysWithMouseScalingDisabled.count(
+ mDisplayId.value_or(ui::LogicalDisplayId::INVALID)) != 0;
+
+ mPointerVelocityControl.setAccelerationEnabled(!disableAllScaling);
+
+ mPointerVelocityControl.setCurve(
+ config.mousePointerAccelerationEnabled
+ ? createAccelerationCurveForPointerSensitivity(config.mousePointerSpeed)
+ : createFlatAccelerationCurve(config.mousePointerSpeed));
+
+ mWheelXVelocityControl.setParameters(config.wheelVelocityControlParameters);
+ mWheelYVelocityControl.setParameters(config.wheelVelocityControlParameters);
}
void CursorInputMapper::configureOnChangeDisplayInfo(const InputReaderConfiguration& config) {
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 5c90cbb..6efaeca 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -30,6 +30,7 @@
#include <android-base/stringprintf.h>
#include <android/input.h>
+#include <com_android_input_flags.h>
#include <ftl/enum.h>
#include <input/PrintTools.h>
#include <input/PropertyMap.h>
@@ -47,6 +48,8 @@
namespace android {
+namespace input_flags = com::android::input::flags;
+
// --- Constants ---
// Artificial latency on synthetic events created from stylus data without corresponding touch
@@ -1575,7 +1578,8 @@
mLastCookedState.buttonState, mCurrentCookedState.buttonState);
// Dispatch the touches either directly or by translation through a pointer on screen.
- if (mDeviceMode == DeviceMode::POINTER) {
+ if (!input_flags::disable_touch_input_mapper_pointer_usage() &&
+ mDeviceMode == DeviceMode::POINTER) {
for (BitSet32 idBits(mCurrentRawState.rawPointerData.touchingIdBits); !idBits.isEmpty();) {
uint32_t id = idBits.clearFirstMarkedBit();
const RawPointerData::Pointer& pointer =
@@ -1613,7 +1617,9 @@
}
out += dispatchPointerUsage(when, readTime, policyFlags, pointerUsage);
- } else {
+ }
+ if (input_flags::disable_touch_input_mapper_pointer_usage() ||
+ mDeviceMode != DeviceMode::POINTER) {
if (!mCurrentMotionAborted) {
out += dispatchButtonRelease(when, readTime, policyFlags);
out += dispatchHoverExit(when, readTime, policyFlags);
@@ -2251,6 +2257,23 @@
for (uint32_t i = 0; i < currentPointerCount; i++) {
const RawPointerData::Pointer& in = mCurrentRawState.rawPointerData.pointers[i];
+ bool isHovering = in.isHovering;
+
+ // A tool MOUSE pointer is only down/touching when a mouse button is pressed.
+ if (input_flags::disable_touch_input_mapper_pointer_usage() &&
+ in.toolType == ToolType::MOUSE &&
+ !mCurrentRawState.rawPointerData.canceledIdBits.hasBit(in.id)) {
+ if (isPointerDown(mCurrentRawState.buttonState)) {
+ isHovering = false;
+ mCurrentCookedState.cookedPointerData.touchingIdBits.markBit(in.id);
+ mCurrentCookedState.cookedPointerData.hoveringIdBits.clearBit(in.id);
+ } else {
+ isHovering = true;
+ mCurrentCookedState.cookedPointerData.touchingIdBits.clearBit(in.id);
+ mCurrentCookedState.cookedPointerData.hoveringIdBits.markBit(in.id);
+ }
+ }
+
// Size
float touchMajor, touchMinor, toolMajor, toolMinor, size;
switch (mCalibration.sizeCalibration) {
@@ -2340,7 +2363,7 @@
pressure = in.pressure * mPressureScale;
break;
default:
- pressure = in.isHovering ? 0 : 1;
+ pressure = isHovering ? 0 : 1;
break;
}
@@ -3476,7 +3499,7 @@
}
return dispatchPointerSimple(when, readTime, policyFlags, down, hovering,
- ui::LogicalDisplayId::INVALID);
+ getAssociatedDisplayId().value_or(ui::LogicalDisplayId::INVALID));
}
std::list<NotifyArgs> TouchInputMapper::abortPointerMouse(nsecs_t when, nsecs_t readTime,
@@ -3697,7 +3720,10 @@
float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
if (mDeviceMode == DeviceMode::POINTER) {
- xCursorPosition = yCursorPosition = 0.f;
+ ALOGW_IF(pointerCount != 1,
+ "Only single pointer events are fully supported in POINTER mode");
+ xCursorPosition = pointerCoords[0].getX();
+ yCursorPosition = pointerCoords[0].getY();
}
const DeviceId deviceId = getDeviceId();
std::vector<TouchVideoFrame> frames = getDeviceContext().getVideoFrames();
@@ -3967,14 +3993,8 @@
}
std::optional<ui::LogicalDisplayId> TouchInputMapper::getAssociatedDisplayId() {
- if (mParameters.hasAssociatedDisplay) {
- if (mDeviceMode == DeviceMode::POINTER) {
- return ui::LogicalDisplayId::INVALID;
- } else {
- return std::make_optional(mViewport.displayId);
- }
- }
- return std::nullopt;
+ return mParameters.hasAssociatedDisplay ? std::make_optional(mViewport.displayId)
+ : std::nullopt;
}
} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index ef0e02f..eb4326f 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -215,7 +215,7 @@
DISABLED, // input is disabled
DIRECT, // direct mapping (touchscreen)
NAVIGATION, // unscaled mapping with assist gesture (touch navigation)
- POINTER, // pointer mapping (e.g. uncaptured touchpad, drawing tablet)
+ POINTER, // pointer mapping (e.g. absolute mouse, drawing tablet)
ftl_last = POINTER
};
@@ -234,6 +234,9 @@
ftl_last = POINTER
};
+ // TouchInputMapper will configure devices with INPUT_PROP_DIRECT as
+ // DeviceType::TOUCH_SCREEN, and will otherwise use DeviceType::POINTER by default.
+ // This can be overridden by IDC files, using the `touch.deviceType` config.
DeviceType deviceType;
bool hasAssociatedDisplay;
bool associatedDisplayIsExternal;
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index 6bd949a..827076a 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -81,7 +81,6 @@
const InputDeviceContext& deviceContext, int32_t deviceId)
: mDeviceId(deviceId),
mReaderContext(readerContext),
- mEnableFlingStop(input_flags::enable_touchpad_fling_stop()),
mEnableNoFocusChange(input_flags::enable_touchpad_no_focus_change()),
// We can safely assume that ABS_MT_POSITION_X and _Y axes will be available, as EventHub
// won't classify a device as a touchpad if they're not present.
@@ -406,7 +405,7 @@
break;
case GESTURES_FLING_TAP_DOWN:
if (mCurrentClassification == MotionClassification::NONE) {
- if (mEnableFlingStop && mFlingMayBeInProgress) {
+ if (mFlingMayBeInProgress) {
// The user has just touched the pad again after ending a two-finger scroll
// motion, which might have started a fling. We want to stop the fling, but
// unfortunately there's currently no API for doing so. Instead, send and
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
index 8d92ead..be76b61 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
@@ -104,7 +104,6 @@
const int32_t mDeviceId;
InputReaderContext& mReaderContext;
- const bool mEnableFlingStop;
const bool mEnableNoFocusChange;
bool mEnableSystemGestures{true};
diff --git a/services/inputflinger/tests/CursorInputMapper_test.cpp b/services/inputflinger/tests/CursorInputMapper_test.cpp
index d4e8fdf..18e0b30 100644
--- a/services/inputflinger/tests/CursorInputMapper_test.cpp
+++ b/services/inputflinger/tests/CursorInputMapper_test.cpp
@@ -27,6 +27,7 @@
#include <com_android_input_flags.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <input/AccelerationCurve.h>
#include <input/DisplayViewport.h>
#include <input/InputEventLabels.h>
#include <linux/input-event-codes.h>
@@ -1028,6 +1029,34 @@
WithCoords(0.0f, 0.0f)))));
}
+TEST_F(CursorInputMapperUnitTest, PointerAccelerationDisabled) {
+ mReaderConfiguration.mousePointerAccelerationEnabled = false;
+ mReaderConfiguration.mousePointerSpeed = 3;
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ createMapper();
+
+ std::list<NotifyArgs> reconfigureArgs;
+
+ reconfigureArgs += mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
+ InputReaderConfiguration::Change::POINTER_SPEED);
+
+ std::vector<AccelerationCurveSegment> curve =
+ createFlatAccelerationCurve(mReaderConfiguration.mousePointerSpeed);
+ double baseGain = curve[0].baseGain;
+
+ std::list<NotifyArgs> motionArgs;
+ motionArgs += process(ARBITRARY_TIME, EV_REL, REL_X, 10);
+ motionArgs += process(ARBITRARY_TIME, EV_REL, REL_Y, 20);
+ motionArgs += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+ const float expectedRelX = 10 * baseGain;
+ const float expectedRelY = 20 * baseGain;
+ ASSERT_THAT(motionArgs,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(HOVER_MOVE),
+ WithRelativeMotion(expectedRelX, expectedRelY)))));
+}
+
TEST_F(CursorInputMapperUnitTest, ConfigureAccelerationWithAssociatedViewport) {
mPropertyMap.addProperty("cursor.mode", "pointer");
DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation0);
@@ -1049,7 +1078,7 @@
ASSERT_GT(coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y), 20.f);
// Disable acceleration for the display, and verify that acceleration is no longer applied.
- mReaderConfiguration.displaysWithMousePointerAccelerationDisabled.emplace(DISPLAY_ID);
+ mReaderConfiguration.displaysWithMouseScalingDisabled.emplace(DISPLAY_ID);
args += mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
InputReaderConfiguration::Change::POINTER_SPEED);
args.clear();
@@ -1068,7 +1097,7 @@
DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation0);
mReaderConfiguration.setDisplayViewports({primaryViewport});
// Disable acceleration for the display.
- mReaderConfiguration.displaysWithMousePointerAccelerationDisabled.emplace(DISPLAY_ID);
+ mReaderConfiguration.displaysWithMouseScalingDisabled.emplace(DISPLAY_ID);
// Don't associate the device with the display yet.
EXPECT_CALL((*mDevice), getAssociatedViewport).WillRepeatedly(Return(std::nullopt));
diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp b/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp
index db68d8a..c4257a8 100644
--- a/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp
@@ -16,6 +16,8 @@
#include "FakeInputDispatcherPolicy.h"
+#include <variant>
+
#include <gtest/gtest.h>
namespace android {
@@ -409,12 +411,18 @@
void FakeInputDispatcherPolicy::interceptMotionBeforeQueueing(ui::LogicalDisplayId, uint32_t,
int32_t, nsecs_t, uint32_t&) {}
-nsecs_t FakeInputDispatcherPolicy::interceptKeyBeforeDispatching(const sp<IBinder>&,
- const KeyEvent&, uint32_t) {
+std::variant<nsecs_t, inputdispatcher::KeyEntry::InterceptKeyResult>
+FakeInputDispatcherPolicy::interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent&,
+ uint32_t) {
if (mConsumeKeyBeforeDispatching) {
- return -1;
+ return inputdispatcher::KeyEntry::InterceptKeyResult::SKIP;
}
+
nsecs_t delay = std::chrono::nanoseconds(mInterceptKeyTimeout).count();
+ if (delay == 0) {
+ return inputdispatcher::KeyEntry::InterceptKeyResult::CONTINUE;
+ }
+
// Clear intercept state so we could dispatch the event in next wake.
mInterceptKeyTimeout = 0ms;
return delay;
diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.h b/services/inputflinger/tests/FakeInputDispatcherPolicy.h
index a9e39d1..c387eac 100644
--- a/services/inputflinger/tests/FakeInputDispatcherPolicy.h
+++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.h
@@ -28,11 +28,13 @@
#include <optional>
#include <queue>
#include <string>
+#include <variant>
#include <vector>
#include <android-base/logging.h>
#include <android-base/thread_annotations.h>
#include <binder/IBinder.h>
+#include <dispatcher/Entry.h>
#include <gui/PidUid.h>
#include <gui/WindowInfo.h>
#include <input/BlockingQueue.h>
@@ -189,7 +191,8 @@
void interceptKeyBeforeQueueing(const KeyEvent& inputEvent, uint32_t&) override;
void interceptMotionBeforeQueueing(ui::LogicalDisplayId, uint32_t, int32_t, nsecs_t,
uint32_t&) override;
- nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent&, uint32_t) override;
+ std::variant<nsecs_t, inputdispatcher::KeyEntry::InterceptKeyResult>
+ interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent&, uint32_t) override;
std::optional<KeyEvent> dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent& event,
uint32_t) override;
void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
diff --git a/services/inputflinger/tests/FakeWindows.h b/services/inputflinger/tests/FakeWindows.h
index 3a3238a..54dc25a 100644
--- a/services/inputflinger/tests/FakeWindows.h
+++ b/services/inputflinger/tests/FakeWindows.h
@@ -144,10 +144,6 @@
mInfo.setInputConfig(InputConfig::PAUSE_DISPATCHING, paused);
}
- inline void setPreventSplitting(bool preventSplitting) {
- mInfo.setInputConfig(InputConfig::PREVENT_SPLITTING, preventSplitting);
- }
-
inline void setSlippery(bool slippery) {
mInfo.setInputConfig(InputConfig::SLIPPERY, slippery);
}
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index fe40a5e..8fa439d 100644
--- a/services/inputflinger/tests/GestureConverter_test.cpp
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -1297,7 +1297,6 @@
TEST_F(GestureConverterTest, FlingTapDownAfterScrollStopsFling) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- input_flags::enable_touchpad_fling_stop(true);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
converter.setDisplayId(ui::LogicalDisplayId::DEFAULT);
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 3413caa..b6e27a8 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -19,6 +19,7 @@
#include "FakeInputDispatcherPolicy.h"
#include "FakeInputTracingBackend.h"
#include "FakeWindows.h"
+#include "ScopedFlagOverride.h"
#include "TestEventMatchers.h"
#include <NotifyArgsBuilders.h>
@@ -117,8 +118,12 @@
// An arbitrary pid of the gesture monitor window
static constexpr gui::Pid MONITOR_PID{2001};
+static constexpr int32_t FLAG_WINDOW_IS_OBSCURED = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
+static constexpr int32_t FLAG_WINDOW_IS_PARTIALLY_OBSCURED =
+ AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+
static constexpr int EXPECTED_WALLPAPER_FLAGS =
- AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+ FLAG_WINDOW_IS_OBSCURED | FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
using ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID;
@@ -134,40 +139,6 @@
return event;
}
-/**
- * Provide a local override for a flag value. The value is restored when the object of this class
- * goes out of scope.
- * This class is not intended to be used directly, because its usage is cumbersome.
- * Instead, a wrapper macro SCOPED_FLAG_OVERRIDE is provided.
- */
-class ScopedFlagOverride {
-public:
- ScopedFlagOverride(std::function<bool()> read, std::function<void(bool)> write, bool value)
- : mInitialValue(read()), mWriteValue(write) {
- mWriteValue(value);
- }
- ~ScopedFlagOverride() { mWriteValue(mInitialValue); }
-
-private:
- const bool mInitialValue;
- std::function<void(bool)> mWriteValue;
-};
-
-typedef bool (*readFlagValueFunction)();
-typedef void (*writeFlagValueFunction)(bool);
-
-/**
- * Use this macro to locally override a flag value.
- * Example usage:
- * SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false);
- * Note: this works by creating a local variable in your current scope. Don't call this twice for
- * the same flag, because the variable names will clash!
- */
-#define SCOPED_FLAG_OVERRIDE(NAME, VALUE) \
- readFlagValueFunction read##NAME = com::android::input::flags::NAME; \
- writeFlagValueFunction write##NAME = com::android::input::flags::NAME; \
- ScopedFlagOverride override##NAME(read##NAME, write##NAME, (VALUE))
-
} // namespace
// --- InputDispatcherTest ---
@@ -1211,22 +1182,17 @@
WithFlags(EXPECTED_WALLPAPER_FLAGS | AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE)));
}
-class ShouldSplitTouchFixture : public InputDispatcherTest,
- public ::testing::WithParamInterface<bool> {};
-INSTANTIATE_TEST_SUITE_P(InputDispatcherTest, ShouldSplitTouchFixture,
- ::testing::Values(true, false));
/**
* A single window that receives touch (on top), and a wallpaper window underneath it.
* The top window gets a multitouch gesture.
* Ensure that wallpaper gets the same gesture.
*/
-TEST_P(ShouldSplitTouchFixture, WallpaperWindowReceivesMultiTouch) {
+TEST_F(InputDispatcherTest, WallpaperWindowReceivesMultiTouch) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> foregroundWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Foreground",
ui::LogicalDisplayId::DEFAULT);
foregroundWindow->setDupTouchToWallpaper(true);
- foregroundWindow->setPreventSplitting(GetParam());
sp<FakeWindowHandle> wallpaperWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper",
@@ -1571,6 +1537,60 @@
window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
}
+// Still send inject motion events to window which already be touched.
+TEST_F(InputDispatcherTest, AlwaysDispatchInjectMotionEventWhenAlreadyDownForWindow) {
+ std::shared_ptr<FakeApplicationHandle> application1 = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window1 =
+ sp<FakeWindowHandle>::make(application1, mDispatcher, "window1",
+ ui::LogicalDisplayId::DEFAULT);
+ window1->setFrame(Rect(0, 0, 100, 100));
+ window1->setWatchOutsideTouch(false);
+
+ std::shared_ptr<FakeApplicationHandle> application2 = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window2 =
+ sp<FakeWindowHandle>::make(application2, mDispatcher, "window2",
+ ui::LogicalDisplayId::DEFAULT);
+ window2->setFrame(Rect(50, 50, 100, 100));
+ window2->setWatchOutsideTouch(true);
+ mDispatcher->onWindowInfosChanged({{*window2->getInfo(), *window1->getInfo()}, {}, 0, 0});
+
+ std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT;
+ InputEventInjectionSync injectionMode = InputEventInjectionSync::WAIT_FOR_RESULT;
+ std::optional<gui::Uid> targetUid = {};
+ uint32_t policyFlags = DEFAULT_POLICY_FLAGS;
+
+ const MotionEvent eventDown1 = MotionEventBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(60).y(60)).deviceId(-1)
+ .build();
+ injectMotionEvent(*mDispatcher, eventDown1, injectionTimeout, injectionMode, targetUid,
+ policyFlags);
+ window2->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ const MotionEvent eventUp1 = MotionEventBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(60).y(60)).deviceId(-1)
+ .downTime(eventDown1.getDownTime()).build();
+ // Inject UP event, without the POLICY_FLAG_PASS_TO_USER (to simulate policy behaviour
+ // when screen is off).
+ injectMotionEvent(*mDispatcher, eventUp1, injectionTimeout, injectionMode, targetUid,
+ /*policyFlags=*/0);
+ window2->consumeMotionEvent(WithMotionAction(ACTION_UP));
+ const MotionEvent eventDown2 = MotionEventBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(40)).deviceId(-1)
+ .build();
+ injectMotionEvent(*mDispatcher, eventDown2, injectionTimeout, injectionMode, targetUid,
+ policyFlags);
+ window1->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+ window2->consumeMotionEvent(WithMotionAction(ACTION_OUTSIDE));
+
+ const MotionEvent eventUp2 = MotionEventBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(60).y(60)).deviceId(-1)
+ .downTime(eventDown2.getDownTime()).build();
+ injectMotionEvent(*mDispatcher, eventUp2, injectionTimeout, injectionMode, targetUid,
+ /*policyFlags=*/0);
+ window1->consumeMotionEvent(WithMotionAction(ACTION_UP));
+ window2->assertNoEvents();
+}
+
/**
* Two windows: a window on the left and a window on the right.
* Mouse is hovered from the right window into the left window.
@@ -4256,17 +4276,15 @@
}
/**
- * When events are not split, the downTime should be adjusted such that the downTime corresponds
+ * When events are split, the downTime should be adjusted such that the downTime corresponds
* to the event time of the first ACTION_DOWN. If a new window appears, it should not affect
- * the event routing because the first window prevents splitting.
+ * the event routing unless pointers are delivered to the new window.
*/
TEST_F(InputDispatcherTest, SplitTouchesSendCorrectActionDownTimeForNewWindow) {
- SCOPED_FLAG_OVERRIDE(split_all_touches, false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window1 =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window1", DISPLAY_ID);
window1->setTouchableRegion(Region{{0, 0, 100, 100}});
- window1->setPreventSplitting(true);
sp<FakeWindowHandle> window2 =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window2", DISPLAY_ID);
@@ -4286,13 +4304,18 @@
// Second window is added
mDispatcher->onWindowInfosChanged({{*window1->getInfo(), *window2->getInfo()}, {}, 0, 0});
- // Now touch down on the window with another pointer
- mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
- .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
- .pointer(PointerBuilder(1, ToolType::FINGER).x(150).y(50))
- .downTime(downArgs.downTime)
- .build());
- window1->consumeMotionPointerDown(1, AllOf(WithDownTime(downArgs.downTime)));
+ // Now touch down on the new window with another pointer
+ NotifyMotionArgs pointerDownArgs =
+ MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(150).y(50))
+ .downTime(downArgs.downTime)
+ .build();
+ mDispatcher->notifyMotion(pointerDownArgs);
+ window1->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithPointerCount(1),
+ WithDownTime(downArgs.downTime)));
+ window2->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDownTime(pointerDownArgs.eventTime)));
// Finish the gesture
mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN)
@@ -4300,11 +4323,16 @@
.pointer(PointerBuilder(1, ToolType::FINGER).x(150).y(50))
.downTime(downArgs.downTime)
.build());
+ window1->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_MOVE), WithDownTime(downArgs.downTime)));
+ window2->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_UP), WithDownTime(pointerDownArgs.eventTime)));
+
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
.pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
.downTime(downArgs.downTime)
.build());
- window1->consumeMotionPointerUp(1, AllOf(WithDownTime(downArgs.downTime)));
+
window1->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_UP), WithDownTime(downArgs.downTime)));
window2->assertNoEvents();
@@ -4313,13 +4341,12 @@
/**
* When splitting touch events, the downTime should be adjusted such that the downTime corresponds
* to the event time of the first ACTION_DOWN sent to the new window.
- * If a new window that does not support split appears on the screen and gets touched with the
- * second finger, it should not get any events because it doesn't want split touches. At the same
- * time, the first window should not get the pointer_down event because it supports split touches
- * (and the touch occurred outside of the bounds of window1).
+ * If a new window appears on the screen and gets touched with the
+ * second finger, it should get the new event. At the same
+ * time, the first window should not get the pointer_down event because
+ * the touch occurred outside of its bounds.
*/
-TEST_F(InputDispatcherTest, SplitTouchesDropsEventForNonSplittableSecondWindow) {
- SCOPED_FLAG_OVERRIDE(split_all_touches, false);
+TEST_F(InputDispatcherTest, SplitTouchesWhenWindowIsAdded) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window1 =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window1", DISPLAY_ID);
@@ -4341,16 +4368,16 @@
AllOf(WithMotionAction(ACTION_DOWN), WithDownTime(downArgs.downTime)));
// Second window is added
- window2->setPreventSplitting(true);
mDispatcher->onWindowInfosChanged({{*window1->getInfo(), *window2->getInfo()}, {}, 0, 0});
- // Now touch down on the window with another pointer
+ // Now touch down on the new window with another pointer
mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
.pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
.pointer(PointerBuilder(1, ToolType::FINGER).x(150).y(50))
.downTime(downArgs.downTime)
.build());
- // Event is dropped because window2 doesn't support split touch, and window1 does.
+ window1->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithPointerCount(1)));
+ window2->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
// Complete the gesture
mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN)
@@ -4361,6 +4388,8 @@
// A redundant MOVE event is generated that doesn't carry any new information
window1->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_MOVE), WithDownTime(downArgs.downTime)));
+ window2->consumeMotionEvent(WithMotionAction(ACTION_UP));
+
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
.pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
.downTime(downArgs.downTime)
@@ -4580,22 +4609,20 @@
* A spy window sits above a window with NO_INPUT_CHANNEL. Ensure that the spy receives events even
* though the window underneath should not get any events.
*/
-TEST_F(InputDispatcherTest, NonSplittableSpyAboveNoInputChannelWindowSinglePointer) {
+TEST_F(InputDispatcherTest, SpyAboveNoInputChannelWindowSinglePointer) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> spyWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Spy",
ui::LogicalDisplayId::DEFAULT);
spyWindow->setFrame(Rect(0, 0, 100, 100));
spyWindow->setTrustedOverlay(true);
- spyWindow->setPreventSplitting(true);
spyWindow->setSpy(true);
- // Another window below spy that has both NO_INPUT_CHANNEL and PREVENT_SPLITTING
+ // Another window below spy that has NO_INPUT_CHANNEL
sp<FakeWindowHandle> inputSinkWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Input sink below spy",
ui::LogicalDisplayId::DEFAULT);
inputSinkWindow->setFrame(Rect(0, 0, 100, 100));
inputSinkWindow->setTrustedOverlay(true);
- inputSinkWindow->setPreventSplitting(true);
inputSinkWindow->setNoInputChannel(true);
mDispatcher->onWindowInfosChanged(
@@ -4620,22 +4647,20 @@
* though the window underneath should not get any events.
* Same test as above, but with two pointers touching instead of one.
*/
-TEST_F(InputDispatcherTest, NonSplittableSpyAboveNoInputChannelWindowTwoPointers) {
+TEST_F(InputDispatcherTest, SpyAboveNoInputChannelWindowTwoPointers) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> spyWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Spy",
ui::LogicalDisplayId::DEFAULT);
spyWindow->setFrame(Rect(0, 0, 100, 100));
spyWindow->setTrustedOverlay(true);
- spyWindow->setPreventSplitting(true);
spyWindow->setSpy(true);
- // Another window below spy that would have both NO_INPUT_CHANNEL and PREVENT_SPLITTING
+ // Another window below spy that would have NO_INPUT_CHANNEL
sp<FakeWindowHandle> inputSinkWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Input sink below spy",
ui::LogicalDisplayId::DEFAULT);
inputSinkWindow->setFrame(Rect(0, 0, 100, 100));
inputSinkWindow->setTrustedOverlay(true);
- inputSinkWindow->setPreventSplitting(true);
inputSinkWindow->setNoInputChannel(true);
mDispatcher->onWindowInfosChanged(
@@ -4667,15 +4692,10 @@
inputSinkWindow->assertNoEvents();
}
-/** Check the behaviour for cases where input sink prevents or doesn't prevent splitting. */
-class SpyThatPreventsSplittingWithApplicationFixture : public InputDispatcherTest,
- public ::testing::WithParamInterface<bool> {
-};
-
/**
* Three windows:
* - An application window (app window)
- * - A spy window that does not overlap the app window. Has PREVENT_SPLITTING flag
+ * - A spy window that does not overlap the app window.
* - A window below the spy that has NO_INPUT_CHANNEL (call it 'inputSink')
*
* The spy window is side-by-side with the app window. The inputSink is below the spy.
@@ -4683,34 +4703,31 @@
* Only the SPY window should get the DOWN event.
* The spy pilfers after receiving the first DOWN event.
* Next, we touch the app window.
- * The spy should receive POINTER_DOWN(1) (since spy is preventing splits).
- * Also, since the spy is already pilfering the first pointer, it will be sent the remaining new
- * pointers automatically, as well.
+ * The spy should not receive POINTER_DOWN(1) (since the pointer is outside of the spy).
* Next, the first pointer (from the spy) is lifted.
- * Spy should get POINTER_UP(0).
+ * Spy should get ACTION_UP.
* This event should not go to the app because the app never received this pointer to begin with.
- * Now, lift the remaining pointer and check that the spy receives UP event.
+ * However, due to the current implementation, this would still cause an ACTION_MOVE event in the
+ * app.
+ * Now, lift the remaining pointer and check that the app receives UP event.
*
- * Finally, send a new ACTION_DOWN event to the spy and check that it's received.
+ * Finally, send a new ACTION_DOWN event to the spy and check that it gets received.
* This test attempts to reproduce a crash in the dispatcher.
*/
-TEST_P(SpyThatPreventsSplittingWithApplicationFixture, SpyThatPreventsSplittingWithApplication) {
- SCOPED_FLAG_OVERRIDE(split_all_touches, false);
+TEST_F(InputDispatcherTest, SpyThatPilfersAfterFirstPointerWithTwoOtherWindows) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> spyWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Spy",
ui::LogicalDisplayId::DEFAULT);
spyWindow->setFrame(Rect(100, 100, 200, 200));
spyWindow->setTrustedOverlay(true);
- spyWindow->setPreventSplitting(true);
spyWindow->setSpy(true);
- // Another window below spy that has both NO_INPUT_CHANNEL and PREVENT_SPLITTING
+ // Another window below spy that has NO_INPUT_CHANNEL
sp<FakeWindowHandle> inputSinkWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Input sink below spy",
ui::LogicalDisplayId::DEFAULT);
inputSinkWindow->setFrame(Rect(100, 100, 200, 200)); // directly below the spy
inputSinkWindow->setTrustedOverlay(true);
- inputSinkWindow->setPreventSplitting(GetParam());
inputSinkWindow->setNoInputChannel(true);
sp<FakeWindowHandle> appWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "App",
@@ -4732,16 +4749,15 @@
mDispatcher->pilferPointers(spyWindow->getToken());
- // Second finger lands in the app, and goes to the spy window. It doesn't go to the app because
- // the spy is already pilfering the first pointer, and this automatically grants the remaining
- // new pointers to the spy, as well.
+ // Second finger lands in the app. It goes to the app as ACTION_DOWN.
mDispatcher->notifyMotion(
MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
.pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(150).y(150))
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
.build());
- spyWindow->consumeMotionPointerDown(1, WithPointerCount(2));
+ spyWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithPointerCount(1)));
+ appWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
// Now lift up the first pointer
mDispatcher->notifyMotion(
@@ -4749,14 +4765,15 @@
.pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(150).y(150))
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
.build());
- spyWindow->consumeMotionPointerUp(0, WithPointerCount(2));
+ spyWindow->consumeMotionEvent(WithMotionAction(ACTION_UP));
+ appWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithPointerCount(1)));
// And lift the remaining pointer!
mDispatcher->notifyMotion(
MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
.build());
- spyWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP), WithPointerCount(1)));
+ appWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP), WithPointerCount(1)));
// Now send a new DOWN, which should again go to spy.
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
@@ -4768,10 +4785,6 @@
appWindow->assertNoEvents();
}
-// Behaviour should be the same regardless of whether inputSink supports splitting.
-INSTANTIATE_TEST_SUITE_P(SpyThatPreventsSplittingWithApplication,
- SpyThatPreventsSplittingWithApplicationFixture, testing::Bool());
-
TEST_F(InputDispatcherTest, HoverWithSpyWindows) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
@@ -5725,14 +5738,12 @@
window->assertNoEvents();
}
-TEST_F(InputDispatcherTest, NonSplitTouchableWindowReceivesMultiTouch) {
- SCOPED_FLAG_OVERRIDE(split_all_touches, false);
+TEST_F(InputDispatcherTest, WindowDoesNotReceiveSecondPointerOutsideOfItsBounds) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
ui::LogicalDisplayId::DEFAULT);
- // Ensure window is non-split and have some transform.
- window->setPreventSplitting(true);
+ // Ensure window has a non-trivial transform.
window->setWindowOffset(20, 40);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
@@ -5740,7 +5751,10 @@
injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
ui::LogicalDisplayId::DEFAULT, {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- window->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN),
+ WithCoords(70, // 50 + 20
+ 90 // 50 + 40
+ )));
const MotionEvent secondFingerDownEvent =
MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
@@ -5749,45 +5763,32 @@
.pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(50))
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(-30).y(-50))
.build();
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
- << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
-
- std::unique_ptr<MotionEvent> event = window->consumeMotionEvent();
- ASSERT_NE(nullptr, event);
- EXPECT_EQ(POINTER_1_DOWN, event->getAction());
- EXPECT_EQ(70, event->getX(0)); // 50 + 20
- EXPECT_EQ(90, event->getY(0)); // 50 + 40
- EXPECT_EQ(-10, event->getX(1)); // -30 + 20
- EXPECT_EQ(-10, event->getY(1)); // -50 + 40
+ << "Injection should fail because the second finger is outside of any window on the "
+ "screen.";
}
/**
- * Two windows: a splittable and a non-splittable.
- * The non-splittable window shouldn't receive any "incomplete" gestures.
- * Send the first pointer to the splittable window, and then touch the non-splittable window.
- * The second pointer should be dropped because the initial window is splittable, so it won't get
- * any pointers outside of it, and the second window is non-splittable, so it shouldn't get any
- * "incomplete" gestures.
+ * Two windows.
+ * Send the first pointer to the left window, and then touch the right window.
+ * The second pointer should generate an ACTION_DOWN in the right window.
*/
-TEST_F(InputDispatcherTest, SplittableAndNonSplittableWindows) {
- SCOPED_FLAG_OVERRIDE(split_all_touches, false);
+TEST_F(InputDispatcherTest, TwoWindowsTwoPointers) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> leftWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Left splittable Window",
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left Window",
ui::LogicalDisplayId::DEFAULT);
- leftWindow->setPreventSplitting(false);
leftWindow->setFrame(Rect(0, 0, 100, 100));
sp<FakeWindowHandle> rightWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Right non-splittable Window",
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right Window",
ui::LogicalDisplayId::DEFAULT);
- rightWindow->setPreventSplitting(true);
rightWindow->setFrame(Rect(100, 100, 200, 200));
mDispatcher->onWindowInfosChanged(
{{*leftWindow->getInfo(), *rightWindow->getInfo()}, {}, 0, 0});
- // Touch down on left, splittable window
+ // Touch down on left window
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
.pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
.build());
@@ -5798,8 +5799,8 @@
.pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(50))
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(150).y(150))
.build());
- leftWindow->assertNoEvents();
- rightWindow->assertNoEvents();
+ leftWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
+ rightWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
}
/**
@@ -5822,7 +5823,6 @@
sp<FakeWindowHandle>::make(application1, mDispatcher, "wallpaper",
ui::LogicalDisplayId::DEFAULT);
wallpaper->setIsWallpaper(true);
- wallpaper->setPreventSplitting(true);
wallpaper->setTouchable(false);
sp<FakeWindowHandle> leftWindow = sp<FakeWindowHandle>::make(application2, mDispatcher, "Left",
@@ -5956,7 +5956,6 @@
sp<FakeWindowHandle>::make(application1, mDispatcher, "wallpaper",
ui::LogicalDisplayId::DEFAULT);
wallpaper->setIsWallpaper(true);
- wallpaper->setPreventSplitting(true);
wallpaper->setTouchable(false);
sp<FakeWindowHandle> leftWindow = sp<FakeWindowHandle>::make(application2, mDispatcher, "Left",
@@ -6070,20 +6069,18 @@
}
/**
- * Two windows: left and right. The left window has PREVENT_SPLITTING input config. Device A sends a
- * down event to the right window. Device B sends a down event to the left window, and then a
- * POINTER_DOWN event to the right window. However, since the left window prevents splitting, the
- * POINTER_DOWN event should only go to the left window, and not to the right window.
+ * Two windows: left and right. Device A sends a DOWN event to the right window. Device B sends a
+ * DOWN event to the left window, and then a POINTER_DOWN event outside of all windows.
+ * The POINTER_DOWN event should be dropped.
* This test attempts to reproduce a crash.
*/
-TEST_F(InputDispatcherTest, MultiDeviceTwoWindowsPreventSplitting) {
- SCOPED_FLAG_OVERRIDE(split_all_touches, false);
+TEST_F(InputDispatcherTest, MultiDeviceTwoWindowsTwoPointers) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, true);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> leftWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Left window (prevent splitting)",
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left window",
ui::LogicalDisplayId::DEFAULT);
leftWindow->setFrame(Rect(0, 0, 100, 100));
- leftWindow->setPreventSplitting(true);
sp<FakeWindowHandle> rightWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Right window",
@@ -6107,14 +6104,14 @@
.deviceId(deviceB)
.build());
leftWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceB)));
- // Send a second pointer from device B to the right window. It shouldn't go to the right window
- // because the left window prevents splitting.
+
+ // Send a second pointer from device B to an area outside of all windows.
mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(deviceB)
.pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
.pointer(PointerBuilder(1, ToolType::FINGER).x(120).y(120))
.build());
- leftWindow->consumeMotionPointerDown(1, WithDeviceId(deviceB));
+ // This is dropped because there's no touchable window at the location (120, 120)
// Finish the gesture for both devices
mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN)
@@ -6122,7 +6119,8 @@
.pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
.pointer(PointerBuilder(1, ToolType::FINGER).x(120).y(120))
.build());
- leftWindow->consumeMotionPointerUp(1, WithDeviceId(deviceB));
+ leftWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithPointerId(0, 0)));
+
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
.pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
.deviceId(deviceB)
@@ -6501,19 +6499,47 @@
ui::LogicalDisplayId::DEFAULT, {PointF{150, 220}}));
firstWindow->assertNoEvents();
- std::unique_ptr<MotionEvent> event = secondWindow->consumeMotionEvent();
- ASSERT_NE(nullptr, event);
- EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, event->getAction());
+ secondWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN),
+ // Ensure that the events from the "getRaw" API are in logical display
+ // coordinates, which has an x-scale of 2 and y-scale of 4.
+ WithRawCoords(300, 880),
+ // Ensure that the x and y values are in the window's coordinate space.
+ // The left-top of the second window is at (100, 200) in display space, which is
+ // (200, 800) in the logical display space. This will be the origin of the window
+ // space.
+ WithCoords(100, 80)));
+}
- // Ensure that the events from the "getRaw" API are in logical display coordinates.
- EXPECT_EQ(300, event->getRawX(0));
- EXPECT_EQ(880, event->getRawY(0));
+TEST_F(InputDispatcherDisplayProjectionTest, UseCloneLayerStackTransformForRawCoordinates) {
+ SCOPED_FLAG_OVERRIDE(use_cloned_screen_coordinates_as_raw, true);
- // Ensure that the x and y values are in the window's coordinate space.
- // The left-top of the second window is at (100, 200) in display space, which is (200, 800) in
- // the logical display space. This will be the origin of the window space.
- EXPECT_EQ(100, event->getX(0));
- EXPECT_EQ(80, event->getY(0));
+ auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
+
+ const std::array<float, 9> matrix = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 0.0, 0.0, 1.0};
+ ui::Transform secondDisplayTransform;
+ secondDisplayTransform.set(matrix);
+ addDisplayInfo(SECOND_DISPLAY_ID, secondDisplayTransform);
+
+ // When a clone layer stack transform is provided for a window, we should use that as the
+ // "display transform" for input going to that window.
+ sp<FakeWindowHandle> secondWindowClone = secondWindow->clone(SECOND_DISPLAY_ID);
+ secondWindowClone->editInfo()->cloneLayerStackTransform = ui::Transform();
+ secondWindowClone->editInfo()->cloneLayerStackTransform->set(0.5, 0, 0, 0.25);
+ addWindow(secondWindowClone);
+
+ // Touch down on the clone window, and ensure its raw coordinates use
+ // the clone layer stack transform.
+ mDispatcher->notifyMotion(generateMotionArgs(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ SECOND_DISPLAY_ID, {PointF{150, 220}}));
+ secondWindowClone->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN),
+ // Ensure the x and y coordinates are in the window's coordinate space.
+ // See previous test case for calculation.
+ WithCoords(100, 80),
+ // Ensure the "getRaw" API uses the clone layer stack transform when it is
+ // provided for the window. It has an x-scale of 0.5 and y-scale of 0.25.
+ WithRawCoords(75, 55)));
}
TEST_F(InputDispatcherDisplayProjectionTest, CancelMotionWithCorrectCoordinates) {
@@ -6949,7 +6975,7 @@
AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
}
-TEST_P(TransferTouchFixture, TransferTouch_TwoPointersNonSplitTouch) {
+TEST_P(TransferTouchFixture, TransferTouch_TwoPointers) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
PointF touchPoint = {10, 10};
@@ -6958,11 +6984,9 @@
sp<FakeWindowHandle> firstWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "First Window",
ui::LogicalDisplayId::DEFAULT);
- firstWindow->setPreventSplitting(true);
sp<FakeWindowHandle> secondWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Second Window",
ui::LogicalDisplayId::DEFAULT);
- secondWindow->setPreventSplitting(true);
// Add the windows to the dispatcher
mDispatcher->onWindowInfosChanged(
@@ -8819,12 +8843,10 @@
}
/**
- * When a device reports a DOWN event, which lands in a window that supports splits, and then the
- * device then reports a POINTER_DOWN, which lands in the location of a non-existing window, then
- * the previous window should receive this event and not be dropped.
+ * First finger lands into a window, and then the second finger lands in the location of a
+ * non-existent window. The second finger should be dropped.
*/
TEST_F(InputDispatcherMultiDeviceTest, SingleDevicePointerDownEventRetentionWithoutWindowTarget) {
- SCOPED_FLAG_OVERRIDE(split_all_touches, false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
ui::LogicalDisplayId::DEFAULT);
@@ -8842,13 +8864,13 @@
.pointer(PointerBuilder(1, ToolType::FINGER).x(200).y(200))
.build());
- window->consumeMotionEvent(AllOf(WithMotionAction(POINTER_1_DOWN)));
+ window->assertNoEvents();
}
/**
* When deviceA reports a DOWN event, which lands in a window that supports splits, and then deviceB
* also reports a DOWN event, which lands in the location of a non-existing window, then the
- * previous window should receive deviceB's event and it should be dropped.
+ * previous window should not receive deviceB's event and it should be dropped.
*/
TEST_F(InputDispatcherMultiDeviceTest, SecondDeviceDownEventDroppedWithoutWindowTarget) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
@@ -12649,9 +12671,6 @@
}
TEST_F(InputDispatcherDragTests, NoDragAndDropWhenMultiFingers) {
- // Ensure window could track pointerIds if it didn't support split touch.
- mWindow->setPreventSplitting(true);
-
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
ui::LogicalDisplayId::DEFAULT, {50, 50}))
@@ -13551,14 +13570,10 @@
}
/**
- * The spy window should not be able to affect whether or not touches are split. Only the foreground
- * windows should be allowed to control split touch.
+ * The spy window should not be able to affect whether or not touches are split.
*/
TEST_F(InputDispatcherSpyWindowTest, SplitIfNoForegroundWindowTouched) {
- // This spy window prevents touch splitting. However, we still expect to split touches
- // because a foreground window has not disabled splitting.
auto spy = createSpy();
- spy->setPreventSplitting(true);
auto window = createForeground();
window->setFrame(Rect(0, 0, 100, 100));
@@ -14684,4 +14699,219 @@
mFakePolicy->assertFocusedDisplayNotified(SECOND_DISPLAY_ID);
}
+class InputDispatcherObscuredFlagTest : public InputDispatcherTest {
+protected:
+ std::shared_ptr<FakeApplicationHandle> mApplication;
+ std::shared_ptr<FakeApplicationHandle> mOcclusionApplication;
+ sp<FakeWindowHandle> mWindow;
+ sp<FakeWindowHandle> mOcclusionWindow;
+
+ void SetUp() override {
+ InputDispatcherTest::SetUp();
+ mDispatcher->setMaximumObscuringOpacityForTouch(0.8f);
+ mApplication = std::make_shared<FakeApplicationHandle>();
+ mOcclusionApplication = std::make_shared<FakeApplicationHandle>();
+ mWindow = sp<FakeWindowHandle>::make(mApplication, mDispatcher, "Window", DISPLAY_ID);
+ mWindow->setOwnerInfo(WINDOW_PID, WINDOW_UID);
+
+ mOcclusionWindow = sp<FakeWindowHandle>::make(mOcclusionApplication, mDispatcher,
+ "Occlusion Window", DISPLAY_ID);
+
+ mOcclusionWindow->setTouchable(false);
+ mOcclusionWindow->setTouchOcclusionMode(TouchOcclusionMode::USE_OPACITY);
+ mOcclusionWindow->setAlpha(0.7f);
+ mOcclusionWindow->setOwnerInfo(SECONDARY_WINDOW_PID, SECONDARY_WINDOW_UID);
+ }
+};
+
+/**
+ * Two windows. An untouchable window partially occludes a touchable region below it.
+ * Use a finger to touch the bottom window.
+ * When the finger touches down in the obscured area, the motion event should always have the
+ * FLAG_WINDOW_IS_OBSCURED flag, regardless of where it is moved to. If it starts from a
+ * non-obscured area, the motion event should always with a FLAG_WINDOW_IS_PARTIALLY_OBSCURED flag,
+ * regardless of where it is moved to.
+ */
+TEST_F(InputDispatcherObscuredFlagTest, TouchObscuredTest) {
+ mWindow->setFrame({0, 0, 100, 100});
+ mOcclusionWindow->setFrame({0, 0, 100, 50});
+
+ mDispatcher->onWindowInfosChanged(
+ {{*mOcclusionWindow->getInfo(), *mWindow->getInfo()}, {}, 0, 0});
+
+ // If the finger touch goes down in the region that is obscured.
+ // Expect the entire stream to use FLAG_WINDOW_IS_OBSCURED.
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(10))
+ .build());
+
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN),
+ WithFlags(FLAG_WINDOW_IS_OBSCURED),
+ WithDisplayId(DISPLAY_ID)));
+
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(60))
+ .build());
+
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE),
+ WithFlags(FLAG_WINDOW_IS_OBSCURED),
+ WithDisplayId(DISPLAY_ID)));
+
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(110))
+ .build());
+
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP),
+ WithFlags(FLAG_WINDOW_IS_OBSCURED),
+ WithDisplayId(DISPLAY_ID)));
+}
+
+/**
+ * Two windows. An untouchable window partially occludes a touchable region below it.
+ * Use a finger to touch the bottom window.
+ * When the finger starts from a non-obscured area, the motion event should always have the
+ * FLAG_WINDOW_IS_PARTIALLY_OBSCURED flag, regardless of where it is moved to.
+ */
+TEST_F(InputDispatcherObscuredFlagTest, TouchPartiallyObscuredTest) {
+ mWindow->setFrame({0, 0, 100, 100});
+ mOcclusionWindow->setFrame({0, 0, 100, 50});
+
+ mDispatcher->onWindowInfosChanged(
+ {{*mOcclusionWindow->getInfo(), *mWindow->getInfo()}, {}, 0, 0});
+
+ // If the finger touch goes down in the region that is not directly obscured by the overlay.
+ // Expect the entire stream to use FLAG_WINDOW_IS_PARTIALLY_OBSCURED.
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(60))
+ .build());
+
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN),
+ WithFlags(FLAG_WINDOW_IS_PARTIALLY_OBSCURED),
+ WithDisplayId(DISPLAY_ID)));
+
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(80))
+ .build());
+
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE),
+ WithFlags(FLAG_WINDOW_IS_PARTIALLY_OBSCURED),
+ WithDisplayId(DISPLAY_ID)));
+
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(110))
+ .build());
+
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP),
+ WithFlags(FLAG_WINDOW_IS_PARTIALLY_OBSCURED),
+ WithDisplayId(DISPLAY_ID)));
+}
+
+/**
+ * Two windows. An untouchable window partially occludes a touchable region below it.
+ * Use the mouse to hover over the bottom window.
+ * When the hover happens over the occluded area, the window below should receive a motion
+ * event with the FLAG_WINDOW_IS_OBSCURED flag. When the hover event moves to the non-occluded area,
+ * the window below should receive a motion event with the FLAG_WINDOW_IS_PARTIALLY_OBSCURED flag.
+ */
+TEST_F(InputDispatcherObscuredFlagTest, MouseHoverObscuredTest) {
+ mWindow->setFrame({0, 0, 100, 100});
+ mOcclusionWindow->setFrame({0, 0, 100, 40});
+
+ mDispatcher->onWindowInfosChanged(
+ {{*mOcclusionWindow->getInfo(), *mWindow->getInfo()}, {}, 0, 0});
+
+ // TODO(b/328160937): The window should receive a motion event with the FLAG_WINDOW_IS_OBSCURED
+ // flag.
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::MOUSE).x(50).y(20))
+ .build());
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithFlags(0)));
+
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::MOUSE).x(50).y(30))
+ .build());
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithFlags(0)));
+
+ // TODO(b/328160937): The window should receive a motion event with the
+ // FLAG_WINDOW_IS_PARTIALLY_OBSCURED flag.
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::MOUSE).x(50).y(50))
+ .build());
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithFlags(0)));
+
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::MOUSE).x(50).y(60))
+ .build());
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithFlags(0)));
+
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::MOUSE).x(50).y(110))
+ .build());
+
+ // TODO(b/328160937): The window should receive a HOVER_EXIT with the
+ // FLAG_WINDOW_IS_PARTIALLY_OBSCURED flag. The cause of the current issue is that we moved the
+ // mouse to a location where there are no windows, so the HOVER_EXIT event cannot be generated.
+ mWindow->assertNoEvents();
+}
+
+/**
+ * Two windows. An untouchable window partially occludes a touchable region below it.
+ * Use the stylus to hover over the bottom window.
+ * When the hover happens over the occluded area, the window below should receive a motion
+ * event with the FLAG_WINDOW_IS_OBSCURED flag. When the hover event moves to the non-occluded area,
+ * the window below should receive a motion event with the FLAG_WINDOW_IS_PARTIALLY_OBSCURED flag.
+ */
+TEST_F(InputDispatcherObscuredFlagTest, StylusHoverObscuredTest) {
+ mWindow->setFrame({0, 0, 100, 100});
+ mOcclusionWindow->setFrame({0, 0, 100, 40});
+
+ mDispatcher->onWindowInfosChanged(
+ {{*mOcclusionWindow->getInfo(), *mWindow->getInfo()}, {}, 0, 0});
+
+ // TODO(b/328160937): The window should receive a motion event with the FLAG_WINDOW_IS_OBSCURED
+ // flag.
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS).x(50).y(20))
+ .build());
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithFlags(0)));
+
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS).x(50).y(30))
+ .build());
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithFlags(0)));
+
+ // TODO(b/328160937): The window should receive a motion event with the
+ // FLAG_WINDOW_IS_PARTIALLY_OBSCURED flag.
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS).x(50).y(50))
+ .build());
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithFlags(0)));
+
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS).x(50).y(60))
+ .build());
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithFlags(0)));
+
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS).x(50).y(70))
+ .build());
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithFlags(0)));
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 9d2256f..470e65b 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -28,6 +28,7 @@
#include <MultiTouchInputMapper.h>
#include <NotifyArgsBuilders.h>
#include <PeripheralController.h>
+#include <ScopedFlagOverride.h>
#include <SingleTouchInputMapper.h>
#include <TestEventMatchers.h>
#include <TestInputListener.h>
@@ -2474,10 +2475,10 @@
const auto syncTime = std::chrono::system_clock::now();
// After 72 ms, the event *will* be generated. If we wait the full 72 ms to check that NO event
// is generated in that period, there will be a race condition between the event being generated
- // and the test's wait timeout expiring. Thus, we wait for a shorter duration in the test, which
- // will reduce the liklihood of the race condition occurring.
- const auto waitUntilTimeForNoEvent =
- syncTime + std::chrono::milliseconds(ns2ms(EXTERNAL_STYLUS_DATA_TIMEOUT / 2));
+ // and the test's wait timeout expiring. Thus, we wait for a shorter duration in the test to
+ // ensure the event is not immediately generated, which should reduce the liklihood of the race
+ // condition occurring.
+ const auto waitUntilTimeForNoEvent = syncTime + std::chrono::milliseconds(1);
mDevice->sendSync();
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled(waitUntilTimeForNoEvent));
@@ -4526,6 +4527,10 @@
NotifyMotionArgs motionArgs;
+ // Hold down the mouse button for the duration of the test, since the mouse tools require
+ // the button to be pressed to make sure they are not hovering.
+ processKey(mapper, BTN_MOUSE, 1);
+
// default tool type is finger
processDown(mapper, 100, 200);
processSync(mapper);
@@ -4533,6 +4538,9 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS)));
+
// eraser
processKey(mapper, BTN_TOOL_RUBBER, 1);
processSync(mapper);
@@ -7175,6 +7183,10 @@
NotifyMotionArgs motionArgs;
+ // Hold down the mouse button for the duration of the test, since the mouse tools require
+ // the button to be pressed to make sure they are not hovering.
+ processKey(mapper, BTN_MOUSE, 1);
+
// default tool type is finger
processId(mapper, 1);
processPosition(mapper, 100, 200);
@@ -7183,6 +7195,9 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS)));
+
// eraser
processKey(mapper, BTN_TOOL_RUBBER, 1);
processSync(mapper);
@@ -7520,6 +7535,7 @@
}
TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShouldHandleDisplayId) {
+ SCOPED_FLAG_OVERRIDE(disable_touch_input_mapper_pointer_usage, true);
prepareSecondaryDisplay(ViewportType::EXTERNAL);
prepareDisplay(ui::ROTATION_0);
@@ -7532,9 +7548,9 @@
processPosition(mapper, 100, 100);
processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_EQ(ui::LogicalDisplayId::INVALID, motionArgs.displayId);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithDisplayId(DISPLAY_ID),
+ WithSource(AINPUT_SOURCE_MOUSE), WithToolType(ToolType::FINGER))));
}
/**
@@ -8604,6 +8620,8 @@
* fingers start to move downwards, the gesture should be swipe.
*/
TEST_F(MultiTouchPointerModeTest, PointerGestureMaxSwipeWidthSwipe) {
+ SCOPED_FLAG_OVERRIDE(disable_touch_input_mapper_pointer_usage, false);
+
// The min freeform gesture width is 25units/mm x 30mm = 750
// which is greater than fraction of the diagnal length of the touchpad (349).
// Thus, MaxSwipWidth is 750.
@@ -8664,6 +8682,8 @@
* the gesture should be swipe.
*/
TEST_F(MultiTouchPointerModeTest, PointerGestureMaxSwipeWidthLowResolutionSwipe) {
+ SCOPED_FLAG_OVERRIDE(disable_touch_input_mapper_pointer_usage, false);
+
// The min freeform gesture width is 5units/mm x 30mm = 150
// which is greater than fraction of the diagnal length of the touchpad (349).
// Thus, MaxSwipWidth is the fraction of the diagnal length, 349.
@@ -8723,6 +8743,8 @@
* freeform gestures after two fingers start to move downwards.
*/
TEST_F(MultiTouchPointerModeTest, PointerGestureMaxSwipeWidthFreeform) {
+ SCOPED_FLAG_OVERRIDE(disable_touch_input_mapper_pointer_usage, false);
+
preparePointerMode(/*xResolution=*/25, /*yResolution=*/25);
MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
@@ -8818,6 +8840,8 @@
}
TEST_F(MultiTouchPointerModeTest, TwoFingerSwipeOffsets) {
+ SCOPED_FLAG_OVERRIDE(disable_touch_input_mapper_pointer_usage, false);
+
preparePointerMode(/*xResolution=*/25, /*yResolution=*/25);
MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
NotifyMotionArgs motionArgs;
@@ -8864,6 +8888,8 @@
}
TEST_F(MultiTouchPointerModeTest, WhenViewportActiveStatusChanged_PointerGestureIsReset) {
+ SCOPED_FLAG_OVERRIDE(disable_touch_input_mapper_pointer_usage, false);
+
preparePointerMode(/*xResolution=*/25, /*yResolution=*/25);
mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOOL_PEN, 0, AKEYCODE_UNKNOWN, 0);
MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
diff --git a/services/inputflinger/tests/MultiTouchInputMapper_test.cpp b/services/inputflinger/tests/MultiTouchInputMapper_test.cpp
index 9a6b266..d15048d 100644
--- a/services/inputflinger/tests/MultiTouchInputMapper_test.cpp
+++ b/services/inputflinger/tests/MultiTouchInputMapper_test.cpp
@@ -23,6 +23,7 @@
#include "InputMapperTest.h"
#include "InterfaceMocks.h"
+#include "ScopedFlagOverride.h"
#include "TestEventMatchers.h"
#define TAG "MultiTouchpadInputMapperUnit_test"
@@ -30,6 +31,7 @@
namespace android {
using testing::_;
+using testing::AllOf;
using testing::IsEmpty;
using testing::Return;
using testing::SetArgPointee;
@@ -266,4 +268,94 @@
VariantWith<NotifyMotionArgs>(WithMotionAction(AMOTION_EVENT_ACTION_UP))));
}
+class MultiTouchInputMapperPointerModeUnitTest : public MultiTouchInputMapperUnitTest {
+protected:
+ void SetUp() override {
+ MultiTouchInputMapperUnitTest::SetUp();
+
+ // TouchInputMapper goes into POINTER mode whenever INPUT_PROP_DIRECT is not set.
+ EXPECT_CALL(mMockEventHub, hasInputProperty(EVENTHUB_ID, INPUT_PROP_DIRECT))
+ .WillRepeatedly(Return(false));
+
+ mMapper = createInputMapper<MultiTouchInputMapper>(*mDeviceContext,
+ mFakePolicy->getReaderConfiguration());
+ }
+};
+
+TEST_F(MultiTouchInputMapperPointerModeUnitTest, MouseToolOnlyDownWhenMouseButtonsAreDown) {
+ SCOPED_FLAG_OVERRIDE(disable_touch_input_mapper_pointer_usage, true);
+
+ std::list<NotifyArgs> args;
+
+ // Set the tool type to mouse.
+ args += processKey(BTN_TOOL_MOUSE, 1);
+
+ args += processPosition(100, 100);
+ args += processId(1);
+ ASSERT_THAT(args, IsEmpty());
+
+ args = processSync();
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithToolType(ToolType::MOUSE))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithToolType(ToolType::MOUSE)))));
+
+ // Setting BTN_TOUCH does not make a mouse pointer go down.
+ args = processKey(BTN_TOUCH, 1);
+ args += processSync();
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))));
+
+ // The mouse button is pressed, so the mouse goes down.
+ args = processKey(BTN_MOUSE, 1);
+ args += processSync();
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT),
+ WithToolType(ToolType::MOUSE))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithToolType(ToolType::MOUSE),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithToolType(ToolType::MOUSE),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY)))));
+
+ // The mouse button is released, so the mouse starts hovering.
+ args = processKey(BTN_MOUSE, 0);
+ args += processSync();
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithButtonState(0), WithToolType(ToolType::MOUSE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithToolType(ToolType::MOUSE), WithButtonState(0))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithToolType(ToolType::MOUSE))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithToolType(ToolType::MOUSE)))));
+
+ // Change the tool type so that it is no longer a mouse.
+ // The default tool type is finger, and the finger is already down.
+ args = processKey(BTN_TOOL_MOUSE, 0);
+ args += processSync();
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT),
+ WithToolType(ToolType::MOUSE))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithToolType(ToolType::FINGER)))));
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp
index 27da3d3..1ca2998 100644
--- a/services/inputflinger/tests/PointerChoreographer_test.cpp
+++ b/services/inputflinger/tests/PointerChoreographer_test.cpp
@@ -2633,16 +2633,14 @@
ui::ROTATION_0),
};
- std::unordered_map<ui::LogicalDisplayId, std::vector<PointerChoreographer::AdjacentDisplay>>
- mTopology{
- {DISPLAY_CENTER_ID,
- {{DISPLAY_TOP_ID, PointerChoreographer::DisplayPosition::TOP, 10.0f},
- {DISPLAY_RIGHT_ID, PointerChoreographer::DisplayPosition::RIGHT, 10.0f},
- {DISPLAY_BOTTOM_ID, PointerChoreographer::DisplayPosition::BOTTOM, 10.0f},
- {DISPLAY_LEFT_ID, PointerChoreographer::DisplayPosition::LEFT, 10.0f},
- {DISPLAY_TOP_RIGHT_CORNER_ID, PointerChoreographer::DisplayPosition::RIGHT,
- -90.0f}}},
- };
+ DisplayTopologyGraph mTopology{DISPLAY_CENTER_ID,
+ {{DISPLAY_CENTER_ID,
+ {{DISPLAY_TOP_ID, DisplayTopologyPosition::TOP, 10.0f},
+ {DISPLAY_RIGHT_ID, DisplayTopologyPosition::RIGHT, 10.0f},
+ {DISPLAY_BOTTOM_ID, DisplayTopologyPosition::BOTTOM, 10.0f},
+ {DISPLAY_LEFT_ID, DisplayTopologyPosition::LEFT, 10.0f},
+ {DISPLAY_TOP_RIGHT_CORNER_ID, DisplayTopologyPosition::RIGHT,
+ -90.0f}}}}};
private:
DisplayViewport createViewport(ui::LogicalDisplayId displayId, int32_t width, int32_t height,
@@ -2706,6 +2704,11 @@
testing::Values(
// Note: Upon viewport transition cursor will be positioned at the boundary of the
// destination, as we drop any unconsumed delta.
+ std::make_tuple("PrimaryDisplayIsDefault", AINPUT_SOURCE_MOUSE,
+ ControllerType::MOUSE, ToolType::MOUSE,
+ vec2(50, 50) /* initial x/y */, vec2(0, 0) /* delta x/y */,
+ PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
+ vec2(50, 50) /* destination x/y */),
std::make_tuple("UnchangedDisplay", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE,
ToolType::MOUSE, vec2(50, 50) /* initial x/y */,
vec2(25, 25) /* delta x/y */,
diff --git a/services/inputflinger/tests/ScopedFlagOverride.h b/services/inputflinger/tests/ScopedFlagOverride.h
new file mode 100644
index 0000000..883673c
--- /dev/null
+++ b/services/inputflinger/tests/ScopedFlagOverride.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2025 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 <com_android_input_flags.h>
+#include <functional>
+
+namespace android {
+
+/**
+ * Provide a local override for a flag value. The value is restored when the object of this class
+ * goes out of scope.
+ * This class is not intended to be used directly, because its usage is cumbersome.
+ * Instead, a wrapper macro SCOPED_FLAG_OVERRIDE is provided.
+ */
+class ScopedFlagOverride {
+public:
+ ScopedFlagOverride(std::function<bool()> read, std::function<void(bool)> write, bool value)
+ : mInitialValue(read()), mWriteValue(write) {
+ mWriteValue(value);
+ }
+ ~ScopedFlagOverride() { mWriteValue(mInitialValue); }
+
+private:
+ const bool mInitialValue;
+ std::function<void(bool)> mWriteValue;
+};
+
+typedef bool (*ReadFlagValueFunction)();
+typedef void (*WriteFlagValueFunction)(bool);
+
+/**
+ * Use this macro to locally override a flag value.
+ * Example usage:
+ * SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false);
+ * Note: this works by creating a local variable in your current scope. Don't call this twice for
+ * the same flag, because the variable names will clash!
+ */
+#define SCOPED_FLAG_OVERRIDE(NAME, VALUE) \
+ ReadFlagValueFunction read##NAME = com::android::input::flags::NAME; \
+ WriteFlagValueFunction write##NAME = com::android::input::flags::NAME; \
+ ScopedFlagOverride override##NAME(read##NAME, write##NAME, (VALUE))
+
+} // namespace android
diff --git a/services/inputflinger/tests/TestEventMatchers.h b/services/inputflinger/tests/TestEventMatchers.h
index 7078e49..7fb8895 100644
--- a/services/inputflinger/tests/TestEventMatchers.h
+++ b/services/inputflinger/tests/TestEventMatchers.h
@@ -32,6 +32,17 @@
namespace android {
+namespace {
+
+template <typename T>
+static bool valuesMatch(T value1, T value2) {
+ if constexpr (std::is_floating_point_v<T>) {
+ return std::abs(value1 - value2) < EPSILON;
+ } else {
+ return value1 == value2;
+ }
+}
+
struct PointF {
float x;
float y;
@@ -42,6 +53,8 @@
return std::string("(") + std::to_string(p.x) + ", " + std::to_string(p.y) + ")";
}
+} // namespace
+
/// Source
class WithSourceMatcher {
public:
@@ -706,8 +719,8 @@
}
const PointerCoords& coords = event.pointerCoords[mPointerIndex];
- bool matches = mRelX == coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X) &&
- mRelY == coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
+ bool matches = valuesMatch(mRelX, coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X)) &&
+ valuesMatch(mRelY, coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y));
if (!matches) {
*os << "expected relative motion (" << mRelX << ", " << mRelY << ") at pointer index "
<< mPointerIndex << ", but got ("
diff --git a/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp
index 79a5ff6..31db2fe 100644
--- a/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp
@@ -76,7 +76,6 @@
window.setDupTouchToWallpaper(fdp.ConsumeBool());
window.setIsWallpaper(fdp.ConsumeBool());
window.setVisible(fdp.ConsumeBool());
- window.setPreventSplitting(fdp.ConsumeBool());
const bool isTrustedOverlay = fdp.ConsumeBool();
window.setTrustedOverlay(isTrustedOverlay);
if (isTrustedOverlay) {
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index abeb2a9..77bf145 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -53,7 +53,6 @@
const sp<IBinder>& parent, const gui::LayerMetadata& metadata,
gui::CreateSurfaceResult* outResult) {
// We rely on createLayer to check permissions.
- sp<IBinder> handle;
LayerCreationArgs args(mFlinger.get(), sp<Client>::fromExisting(this), name.c_str(),
static_cast<uint32_t>(flags), std::move(metadata));
args.parentHandle = parent;
@@ -101,7 +100,6 @@
binder::Status Client::mirrorSurface(const sp<IBinder>& mirrorFromHandle,
gui::CreateSurfaceResult* outResult) {
- sp<IBinder> handle;
LayerCreationArgs args(mFlinger.get(), sp<Client>::fromExisting(this), "MirrorRoot",
0 /* flags */, gui::LayerMetadata());
status_t status = mFlinger->mirrorLayer(args, mirrorFromHandle, *outResult);
@@ -109,7 +107,6 @@
}
binder::Status Client::mirrorDisplay(int64_t displayId, gui::CreateSurfaceResult* outResult) {
- sp<IBinder> handle;
LayerCreationArgs args(mFlinger.get(), sp<Client>::fromExisting(this),
"MirrorRoot-" + std::to_string(displayId), 0 /* flags */,
gui::LayerMetadata());
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
index e32cc02..fd58191 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
@@ -53,7 +53,7 @@
createLayerFECompositionState() = 0;
virtual HWComposer& getHwComposer() const = 0;
- virtual void setHwComposer(std::unique_ptr<HWComposer>) = 0;
+ virtual void setHwComposer(HWComposer*) = 0;
virtual renderengine::RenderEngine& getRenderEngine() const = 0;
virtual void setRenderEngine(renderengine::RenderEngine*) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index cda4edc..e876693 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -36,6 +36,10 @@
#include <utils/RefBase.h>
#include <utils/Timers.h>
+namespace aidl::android::hardware::graphics::composer3 {
+enum class Composition;
+}
+
namespace android {
class Fence;
@@ -121,6 +125,8 @@
// True if layers with 170M dataspace should be overridden to sRGB.
const bool treat170mAsSrgb;
+
+ std::shared_ptr<gui::DisplayLuts> luts;
};
// A superset of LayerSettings required by RenderEngine to compose a layer
@@ -131,6 +137,9 @@
// Currently latched frame number, 0 if invalid.
uint64_t frameNumber = 0;
+
+ // layer serial number, -1 if invalid.
+ int32_t sequence = -1;
};
// Describes the states of the release fence. Checking the states allows checks
@@ -173,6 +182,11 @@
// Whether the layer should be rendered with rounded corners.
virtual bool hasRoundedCorners() const = 0;
virtual void setWasClientComposed(const sp<Fence>&) {}
+ virtual void setHwcCompositionType(
+ aidl::android::hardware::graphics::composer3::Composition) = 0;
+ virtual aidl::android::hardware::graphics::composer3::Composition getHwcCompositionType()
+ const = 0;
+
virtual const gui::LayerMetadata* getMetadata() const = 0;
virtual const gui::LayerMetadata* getRelativeMetadata() const = 0;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
index 45208dd..2992b6d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
@@ -31,7 +31,7 @@
override;
HWComposer& getHwComposer() const override;
- void setHwComposer(std::unique_ptr<HWComposer>) override;
+ void setHwComposer(HWComposer*) override;
renderengine::RenderEngine& getRenderEngine() const override;
void setRenderEngine(renderengine::RenderEngine*) override;
@@ -59,7 +59,7 @@
void setNeedsAnotherUpdateForTest(bool);
private:
- std::unique_ptr<HWComposer> mHwComposer;
+ HWComposer* mHwComposer;
renderengine::RenderEngine* mRenderEngine;
std::shared_ptr<TimeStats> mTimeStats;
bool mNeedsAnotherUpdate = false;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
index a1b7282..bb1a222 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
@@ -37,7 +37,7 @@
std::unique_ptr<compositionengine::LayerFECompositionState>());
MOCK_CONST_METHOD0(getHwComposer, HWComposer&());
- MOCK_METHOD1(setHwComposer, void(std::unique_ptr<HWComposer>));
+ MOCK_METHOD1(setHwComposer, void(HWComposer*));
MOCK_CONST_METHOD0(getRenderEngine, renderengine::RenderEngine&());
MOCK_METHOD1(setRenderEngine, void(renderengine::RenderEngine*));
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index 272fa3e..7744b8b 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -59,6 +59,10 @@
MOCK_CONST_METHOD0(getMetadata, gui::LayerMetadata*());
MOCK_CONST_METHOD0(getRelativeMetadata, gui::LayerMetadata*());
MOCK_METHOD0(onPictureProfileCommitted, void());
+ MOCK_METHOD(void, setHwcCompositionType,
+ (aidl::android::hardware::graphics::composer3::Composition), (override));
+ MOCK_METHOD(aidl::android::hardware::graphics::composer3::Composition, getHwcCompositionType,
+ (), (const, override));
};
} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index cfcce47..989f8e3 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -58,11 +58,11 @@
}
HWComposer& CompositionEngine::getHwComposer() const {
- return *mHwComposer.get();
+ return *mHwComposer;
}
-void CompositionEngine::setHwComposer(std::unique_ptr<HWComposer> hwComposer) {
- mHwComposer = std::move(hwComposer);
+void CompositionEngine::setHwComposer(HWComposer* hwComposer) {
+ mHwComposer = hwComposer;
}
renderengine::RenderEngine& CompositionEngine::getRenderEngine() const {
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 734d764..de1d13a 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -1564,7 +1564,9 @@
.clearContent = !clientComposition,
.blurSetting = blurSetting,
.whitePointNits = layerState.whitePointNits,
- .treat170mAsSrgb = outputState.treat170mAsSrgb};
+ .treat170mAsSrgb = outputState.treat170mAsSrgb,
+ .luts = layer->getState().hwc ? layer->getState().hwc->luts
+ : nullptr};
if (auto clientCompositionSettings =
layerFE.prepareClientComposition(targetSettings)) {
clientCompositionLayers.push_back(std::move(*clientCompositionSettings));
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 96b86d5..9b66f01 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -867,6 +867,7 @@
if (outputDependentState.hwc->hwcCompositionType != requestedCompositionType ||
(outputDependentState.hwc->layerSkipped && !skipLayer)) {
outputDependentState.hwc->hwcCompositionType = requestedCompositionType;
+ getLayerFE().setHwcCompositionType(requestedCompositionType);
if (auto error = hwcLayer->setCompositionType(requestedCompositionType);
error != hal::Error::NONE) {
@@ -964,6 +965,7 @@
}
hwcState.hwcCompositionType = compositionType;
+ getLayerFE().setHwcCompositionType(compositionType);
}
void OutputLayer::prepareForDeviceLayerRequests() {
diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
index 3e0c390..ad65c44 100644
--- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -61,7 +61,7 @@
TEST_F(CompositionEngineTest, canSetHWComposer) {
android::mock::HWComposer* hwc = new StrictMock<android::mock::HWComposer>();
- mEngine.setHwComposer(std::unique_ptr<android::HWComposer>(hwc));
+ mEngine.setHwComposer(static_cast<android::HWComposer*>(hwc));
EXPECT_EQ(hwc, &mEngine.getHwComposer());
}
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 8529c72..366d3f4 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -686,6 +686,36 @@
return error;
}
+Error AidlComposer::getLayerPresentFences(Display display, std::vector<Layer>* outLayers,
+ std::vector<int>* outFences,
+ std::vector<int64_t>* outLatenciesNanos) {
+ Error error = Error::NONE;
+ std::vector<PresentFence::LayerPresentFence> fences;
+ {
+ mMutex.lock_shared();
+ if (auto reader = getReader(display)) {
+ fences = reader->get().takeLayerPresentFences(translate<int64_t>(display));
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
+ }
+
+ outLayers->reserve(fences.size());
+ outFences->reserve(fences.size());
+ outLatenciesNanos->reserve(fences.size());
+
+ for (auto& fence : fences) {
+ outLayers->emplace_back(translate<Layer>(fence.layer));
+ // take ownership
+ const int fenceOwner = fence.bufferFence.get();
+ *fence.bufferFence.getR() = -1;
+ outFences->emplace_back(fenceOwner);
+ outLatenciesNanos->emplace_back(fence.bufferLatencyNanos);
+ }
+ return error;
+}
+
Error AidlComposer::presentDisplay(Display display, int* outPresentFence) {
const auto displayId = translate<int64_t>(display);
SFTRACE_FORMAT("HwcPresentDisplay %" PRId64, displayId);
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index 82006f4..79e3349 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -106,6 +106,10 @@
Error getReleaseFences(Display display, std::vector<Layer>* outLayers,
std::vector<int>* outReleaseFences) override;
+ Error getLayerPresentFences(Display display, std::vector<Layer>* outLayers,
+ std::vector<int>* outFences,
+ std::vector<int64_t>* outLatenciesNanos) override;
+
Error presentDisplay(Display display, int* outPresentFence) override;
Error setActiveConfig(Display display, Config config) override;
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 6e431bb..018ee6e 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -157,6 +157,10 @@
virtual Error getReleaseFences(Display display, std::vector<Layer>* outLayers,
std::vector<int>* outReleaseFences) = 0;
+ virtual Error getLayerPresentFences(Display display, std::vector<Layer>* outLayers,
+ std::vector<int>* outFences,
+ std::vector<int64_t>* outLatenciesNanos) = 0;
+
virtual Error presentDisplay(Display display, int* outPresentFence) = 0;
virtual Error setActiveConfig(Display display, Config config) = 0;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 99a67e4..252c6b6 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -629,7 +629,7 @@
auto layer = getLayerById(layerIds[i]);
if (layer) {
auto& layerLut = tmpLuts[i];
- if (layerLut.luts.pfd.get() > 0 && layerLut.luts.offsets.has_value()) {
+ if (layerLut.luts.pfd.get() >= 0 && layerLut.luts.offsets.has_value()) {
const auto& offsets = layerLut.luts.offsets.value();
std::vector<std::pair<int32_t, LutProperties>> lutOffsetsAndProperties;
lutOffsetsAndProperties.reserve(offsets.size());
@@ -640,7 +640,15 @@
lutFileDescriptorMapper.emplace_or_replace(layer.get(),
ndk::ScopedFileDescriptor(
layerLut.luts.pfd.release()));
+ } else {
+ ALOGE("getRequestedLuts: invalid luts on layer %" PRIu64 " found"
+ " on display %" PRIu64 ". pfd.get()=%d, offsets.has_value()=%d",
+ layerIds[i], mId, layerLut.luts.pfd.get(), layerLut.luts.offsets.has_value());
}
+ } else {
+ ALOGE("getRequestedLuts: invalid layer %" PRIu64 " found"
+ " on display %" PRIu64,
+ layerIds[i], mId);
}
}
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index ec15539..fc317f3 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -590,6 +590,11 @@
return Error::NONE;
}
+Error HidlComposer::getLayerPresentFences(Display, std::vector<Layer>*, std::vector<int>*,
+ std::vector<int64_t>*) {
+ return Error::UNSUPPORTED;
+}
+
Error HidlComposer::presentDisplay(Display display, int* outPresentFence) {
SFTRACE_NAME("HwcPresentDisplay");
mWriter.selectDisplay(display);
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index cacdb8c..86ca4b1 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -214,6 +214,10 @@
Error getReleaseFences(Display display, std::vector<Layer>* outLayers,
std::vector<int>* outReleaseFences) override;
+ Error getLayerPresentFences(Display display, std::vector<Layer>* outLayers,
+ std::vector<int>* outFences,
+ std::vector<int64_t>* outLatenciesNanos) override;
+
Error presentDisplay(Display display, int* outPresentFence) override;
Error setActiveConfig(Display display, Config config) override;
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
index 47d0041..4fdbae1 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.h
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
@@ -102,6 +102,8 @@
// Returns true if the node is a clone.
bool isClone() const { return !mirrorRootIds.empty(); }
+ TraversalPath getClonedFrom() const { return {.id = id, .variant = variant}; }
+
bool operator==(const TraversalPath& other) const {
return id == other.id && mirrorRootIds == other.mirrorRootIds;
}
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
index f1091a6..d369403 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
@@ -182,8 +182,8 @@
}
}
-void LayerLifecycleManager::applyTransactions(const std::vector<TransactionState>& transactions,
- bool ignoreUnknownLayers) {
+void LayerLifecycleManager::applyTransactions(
+ const std::vector<QueuedTransactionState>& transactions, bool ignoreUnknownLayers) {
for (const auto& transaction : transactions) {
for (const auto& resolvedComposerState : transaction.states) {
const auto& clientState = resolvedComposerState.state;
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
index 330da9a..072be35 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
@@ -16,8 +16,8 @@
#pragma once
+#include "QueuedTransactionState.h"
#include "RequestedLayerState.h"
-#include "TransactionState.h"
namespace android::surfaceflinger::frontend {
@@ -43,7 +43,8 @@
// the layers it is unreachable. When using the LayerLifecycleManager for layer trace
// generation we may encounter layers which are known because we don't have an explicit
// lifecycle. Ignore these errors while we have to interop with legacy.
- void applyTransactions(const std::vector<TransactionState>&, bool ignoreUnknownLayers = false);
+ void applyTransactions(const std::vector<QueuedTransactionState>&,
+ bool ignoreUnknownLayers = false);
// Ignore unknown handles when iteroping with legacy front end. In the old world, we
// would create child layers which are not necessary with the new front end. This means
// we will get notified for handle changes that don't exist in the new front end.
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index 367132c..523ef7b 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -18,12 +18,17 @@
#undef LOG_TAG
#define LOG_TAG "SurfaceFlinger"
-#include "LayerSnapshot.h"
+#include <PowerAdvisor/Workload.h>
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+#include <gui/LayerState.h>
+
#include "Layer.h"
+#include "LayerSnapshot.h"
namespace android::surfaceflinger::frontend {
using namespace ftl::flag_operators;
+using namespace aidl::android::hardware::graphics::composer3;
namespace {
@@ -398,9 +403,13 @@
if (forceUpdate || requested.what & layer_state_t::eSidebandStreamChanged) {
sidebandStream = requested.sidebandStream;
}
- if (forceUpdate || requested.what & layer_state_t::eShadowRadiusChanged) {
- shadowSettings.length = requested.shadowRadius;
+ if (forceUpdate || requested.what & layer_state_t::eShadowRadiusChanged ||
+ requested.what & layer_state_t::eClientDrawnShadowsChanged) {
+ shadowSettings.length =
+ requested.clientDrawnShadowRadius > 0 ? 0.f : requested.shadowRadius;
+ shadowSettings.clientDrawnLength = requested.clientDrawnShadowRadius;
}
+
if (forceUpdate || requested.what & layer_state_t::eFrameRateSelectionPriority) {
frameRateSelectionPriority = requested.frameRateSelectionPriority;
}
@@ -528,4 +537,49 @@
}
}
+char LayerSnapshot::classifyCompositionForDebug(Composition compositionType) const {
+ if (!isVisible) {
+ return '.';
+ }
+
+ switch (compositionType) {
+ case Composition::INVALID:
+ return 'i';
+ case Composition::SOLID_COLOR:
+ return 'c';
+ case Composition::CURSOR:
+ return 'u';
+ case Composition::SIDEBAND:
+ return 'd';
+ case Composition::DISPLAY_DECORATION:
+ return 'a';
+ case Composition::REFRESH_RATE_INDICATOR:
+ return 'r';
+ case Composition::CLIENT:
+ case Composition::DEVICE:
+ break;
+ }
+
+ char code = '.'; // Default to invisible
+ if (hasBufferOrSidebandStream()) {
+ code = 'b';
+ } else if (fillsColor()) {
+ code = 'c'; // Solid color
+ } else if (hasBlur()) {
+ code = 'l'; // Blur
+ } else if (hasProtectedContent) {
+ code = 'p'; // Protected content
+ } else if (drawShadows()) {
+ code = 's'; // Shadow
+ } else if (roundedCorner.hasRoundedCorners()) {
+ code = 'r'; // Rounded corners
+ }
+
+ if (compositionType == Composition::CLIENT) {
+ return static_cast<char>(std::toupper(code));
+ } else {
+ return code;
+ }
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
index b8df3ed..04b9f3b 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -16,6 +16,7 @@
#pragma once
+#include <PowerAdvisor/Workload.h>
#include <compositionengine/LayerFECompositionState.h>
#include <renderengine/LayerSettings.h>
#include "DisplayHardware/ComposerHal.h"
@@ -28,16 +29,23 @@
struct RoundedCornerState {
RoundedCornerState() = default;
- RoundedCornerState(const FloatRect& cropRect, const vec2& radius)
- : cropRect(cropRect), radius(radius) {}
// Rounded rectangle in local layer coordinate space.
FloatRect cropRect = FloatRect();
- // Radius of the rounded rectangle.
+ // Radius of the rounded rectangle for composition
vec2 radius;
+ // Requested radius of the rounded rectangle
+ vec2 requestedRadius;
+ // Radius drawn by client for the rounded rectangle
+ vec2 clientDrawnRadius;
+ bool hasClientDrawnRadius() const {
+ return clientDrawnRadius.x > 0.0f && clientDrawnRadius.y > 0.0f;
+ }
+ bool hasRequestedRadius() const { return requestedRadius.x > 0.0f && requestedRadius.y > 0.0f; }
bool hasRoundedCorners() const { return radius.x > 0.0f && radius.y > 0.0f; }
bool operator==(RoundedCornerState const& rhs) const {
- return cropRect == rhs.cropRect && radius == rhs.radius;
+ return cropRect == rhs.cropRect && radius == rhs.radius &&
+ clientDrawnRadius == rhs.clientDrawnRadius;
}
};
@@ -152,6 +160,10 @@
friend std::ostream& operator<<(std::ostream& os, const LayerSnapshot& obj);
void merge(const RequestedLayerState& requested, bool forceUpdate, bool displayChanges,
bool forceFullDamage, uint32_t displayRotationFlags);
+ // Returns a char summarizing the composition request
+ // This function tries to maintain parity with planner::Plan chars.
+ char classifyCompositionForDebug(
+ aidl::android::hardware::graphics::composer3::Composition compositionType) const;
};
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 022588d..7289e2f 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -16,6 +16,8 @@
// #define LOG_NDEBUG 0
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include "FrontEnd/LayerSnapshot.h"
+#include "ui/Transform.h"
#undef LOG_TAG
#define LOG_TAG "SurfaceFlinger"
@@ -25,6 +27,7 @@
#include <common/FlagManager.h>
#include <common/trace.h>
#include <ftl/small_map.h>
+#include <math/vec2.h>
#include <ui/DisplayMap.h>
#include <ui/FloatRect.h>
@@ -932,7 +935,8 @@
if (forceUpdate || snapshot.clientChanges & layer_state_t::eCornerRadiusChanged ||
snapshot.changes.any(RequestedLayerState::Changes::Geometry |
- RequestedLayerState::Changes::BufferUsageFlags)) {
+ RequestedLayerState::Changes::BufferUsageFlags) ||
+ snapshot.clientChanges & layer_state_t::eClientDrawnCornerRadiusChanged) {
updateRoundedCorner(snapshot, requested, parentSnapshot, args);
}
@@ -972,19 +976,27 @@
}
snapshot.roundedCorner = RoundedCornerState();
RoundedCornerState parentRoundedCorner;
- if (parentSnapshot.roundedCorner.hasRoundedCorners()) {
+ if (parentSnapshot.roundedCorner.hasRequestedRadius()) {
parentRoundedCorner = parentSnapshot.roundedCorner;
ui::Transform t = snapshot.localTransform.inverse();
parentRoundedCorner.cropRect = t.transform(parentRoundedCorner.cropRect);
parentRoundedCorner.radius.x *= t.getScaleX();
parentRoundedCorner.radius.y *= t.getScaleY();
+ parentRoundedCorner.requestedRadius.x *= t.getScaleX();
+ parentRoundedCorner.requestedRadius.y *= t.getScaleY();
}
FloatRect layerCropRect = snapshot.croppedBufferSize;
- const vec2 radius(requested.cornerRadius, requested.cornerRadius);
- RoundedCornerState layerSettings(layerCropRect, radius);
- const bool layerSettingsValid = layerSettings.hasRoundedCorners() && !layerCropRect.isEmpty();
- const bool parentRoundedCornerValid = parentRoundedCorner.hasRoundedCorners();
+ const vec2 requestedRadius(requested.cornerRadius, requested.cornerRadius);
+ const vec2 clientDrawnRadius(requested.clientDrawnCornerRadius,
+ requested.clientDrawnCornerRadius);
+ RoundedCornerState layerSettings;
+ layerSettings.cropRect = layerCropRect;
+ layerSettings.requestedRadius = requestedRadius;
+ layerSettings.clientDrawnRadius = clientDrawnRadius;
+
+ const bool layerSettingsValid = layerSettings.hasRequestedRadius() && !layerCropRect.isEmpty();
+ const bool parentRoundedCornerValid = parentRoundedCorner.hasRequestedRadius();
if (layerSettingsValid && parentRoundedCornerValid) {
// If the parent and the layer have rounded corner settings, use the parent settings if
// the parent crop is entirely inside the layer crop. This has limitations and cause
@@ -1002,6 +1014,14 @@
} else if (parentRoundedCornerValid) {
snapshot.roundedCorner = parentRoundedCorner;
}
+
+ if (snapshot.roundedCorner.requestedRadius.x == requested.clientDrawnCornerRadius) {
+ // If the client drawn radius matches the requested radius, then surfaceflinger
+ // does not need to draw rounded corners for this layer
+ snapshot.roundedCorner.radius = vec2(0.f, 0.f);
+ } else {
+ snapshot.roundedCorner.radius = snapshot.roundedCorner.requestedRadius;
+ }
}
/**
@@ -1205,13 +1225,27 @@
snapshot.inputInfo.contentSize = {snapshot.croppedBufferSize.getHeight(),
snapshot.croppedBufferSize.getWidth()};
- // If the layer is a clone, we need to crop the input region to cloned root to prevent
- // touches from going outside the cloned area.
+ snapshot.inputInfo.cloneLayerStackTransform.reset();
+
if (path.isClone()) {
snapshot.inputInfo.inputConfig |= InputConfig::CLONE;
// Cloned layers shouldn't handle watch outside since their z order is not determined by
// WM or the client.
snapshot.inputInfo.inputConfig.clear(InputConfig::WATCH_OUTSIDE_TOUCH);
+
+ // Compute the transform that maps the clone's display to the layer stack space of the
+ // cloned window.
+ const LayerSnapshot* clonedSnapshot = getSnapshot(path.getClonedFrom());
+ if (clonedSnapshot != nullptr) {
+ const auto& [clonedInputBounds, s] =
+ getInputBounds(*clonedSnapshot, /*fillParentBounds=*/false);
+ ui::Transform inputToLayer;
+ inputToLayer.set(clonedInputBounds.left, clonedInputBounds.top);
+ const ui::Transform& layerToLayerStack = getInputTransform(*clonedSnapshot);
+ const auto& displayToInput = snapshot.inputInfo.transform;
+ snapshot.inputInfo.cloneLayerStackTransform =
+ layerToLayerStack * inputToLayer * displayToInput;
+ }
}
}
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index ee9302b..591ebb2 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -107,6 +107,8 @@
hdrMetadata.validTypes = 0;
surfaceDamageRegion = Region::INVALID_REGION;
cornerRadius = 0.0f;
+ clientDrawnCornerRadius = 0.0f;
+ clientDrawnShadowRadius = 0.0f;
backgroundBlurRadius = 0;
api = -1;
hasColorTransform = false;
@@ -348,6 +350,16 @@
requestedFrameRate.category = category;
changes |= RequestedLayerState::Changes::FrameRate;
}
+
+ if (clientState.what & layer_state_t::eClientDrawnCornerRadiusChanged) {
+ clientDrawnCornerRadius = clientState.clientDrawnCornerRadius;
+ changes |= RequestedLayerState::Changes::Geometry;
+ }
+
+ if (clientState.what & layer_state_t::eClientDrawnShadowsChanged) {
+ clientDrawnShadowRadius = clientState.clientDrawnShadowRadius;
+ changes |= RequestedLayerState::Changes::Geometry;
+ }
}
ui::Size RequestedLayerState::getUnrotatedBufferSize(uint32_t displayRotationFlags) const {
@@ -624,6 +636,8 @@
const uint64_t deniedChanges = layer_state_t::ePositionChanged | layer_state_t::eAlphaChanged |
layer_state_t::eColorTransformChanged | layer_state_t::eBackgroundColorChanged |
layer_state_t::eMatrixChanged | layer_state_t::eCornerRadiusChanged |
+ layer_state_t::eClientDrawnCornerRadiusChanged |
+ layer_state_t::eClientDrawnShadowsChanged |
layer_state_t::eBackgroundBlurRadiusChanged | layer_state_t::eBufferTransformChanged |
layer_state_t::eTransformToDisplayInverseChanged | layer_state_t::eCropChanged |
layer_state_t::eDataspaceChanged | layer_state_t::eHdrMetadataChanged |
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
index 7ddd7ba..dd861a7 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -23,7 +23,7 @@
#include "Scheduler/LayerInfo.h"
#include "LayerCreationArgs.h"
-#include "TransactionState.h"
+#include "QueuedTransactionState.h"
namespace android::surfaceflinger::frontend {
using namespace ftl::flag_operators;
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
index a1e8213..5bf86e5 100644
--- a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
@@ -28,7 +28,7 @@
namespace android::surfaceflinger::frontend {
-void TransactionHandler::queueTransaction(TransactionState&& state) {
+void TransactionHandler::queueTransaction(QueuedTransactionState&& state) {
mLocklessTransactionQueue.push(std::move(state));
mPendingTransactionCount.fetch_add(1);
SFTRACE_INT("TransactionQueue", static_cast<int>(mPendingTransactionCount.load()));
@@ -45,9 +45,9 @@
}
}
-std::vector<TransactionState> TransactionHandler::flushTransactions() {
+std::vector<QueuedTransactionState> TransactionHandler::flushTransactions() {
// Collect transaction that are ready to be applied.
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
TransactionFlushState flushState;
flushState.queueProcessTime = systemTime();
// Transactions with a buffer pending on a barrier may be on a different applyToken
@@ -76,7 +76,7 @@
}
void TransactionHandler::applyUnsignaledBufferTransaction(
- std::vector<TransactionState>& transactions, TransactionFlushState& flushState) {
+ std::vector<QueuedTransactionState>& transactions, TransactionFlushState& flushState) {
if (!flushState.queueWithUnsignaledBuffer) {
return;
}
@@ -98,9 +98,9 @@
}
}
-void TransactionHandler::popTransactionFromPending(std::vector<TransactionState>& transactions,
- TransactionFlushState& flushState,
- std::queue<TransactionState>& queue) {
+void TransactionHandler::popTransactionFromPending(
+ std::vector<QueuedTransactionState>& transactions, TransactionFlushState& flushState,
+ std::queue<QueuedTransactionState>& queue) {
auto& transaction = queue.front();
// Transaction is ready move it from the pending queue.
flushState.firstTransaction = false;
@@ -146,8 +146,8 @@
return ready;
}
-int TransactionHandler::flushPendingTransactionQueues(std::vector<TransactionState>& transactions,
- TransactionFlushState& flushState) {
+int TransactionHandler::flushPendingTransactionQueues(
+ std::vector<QueuedTransactionState>& transactions, TransactionFlushState& flushState) {
int transactionsPendingBarrier = 0;
auto it = mPendingTransactionQueues.begin();
while (it != mPendingTransactionQueues.end()) {
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.h b/services/surfaceflinger/FrontEnd/TransactionHandler.h
index 00f6bce..e78dd88 100644
--- a/services/surfaceflinger/FrontEnd/TransactionHandler.h
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.h
@@ -22,7 +22,7 @@
#include <vector>
#include <LocklessQueue.h>
-#include <TransactionState.h>
+#include <QueuedTransactionState.h>
#include <android-base/thread_annotations.h>
#include <ftl/small_map.h>
#include <ftl/small_vector.h>
@@ -35,7 +35,7 @@
class TransactionHandler {
public:
struct TransactionFlushState {
- TransactionState* transaction;
+ QueuedTransactionState* transaction;
bool firstTransaction = true;
nsecs_t queueProcessTime = 0;
// Layer handles that have transactions with buffers that are ready to be applied.
@@ -61,9 +61,9 @@
bool hasPendingTransactions();
// Moves transactions from the lockless queue.
void collectTransactions();
- std::vector<TransactionState> flushTransactions();
+ std::vector<QueuedTransactionState> flushTransactions();
void addTransactionReadyFilter(TransactionFilter&&);
- void queueTransaction(TransactionState&&);
+ void queueTransaction(QueuedTransactionState&&);
struct StalledTransactionInfo {
pid_t pid;
@@ -81,14 +81,15 @@
// For unit tests
friend class ::android::TestableSurfaceFlinger;
- int flushPendingTransactionQueues(std::vector<TransactionState>&, TransactionFlushState&);
- void applyUnsignaledBufferTransaction(std::vector<TransactionState>&, TransactionFlushState&);
- void popTransactionFromPending(std::vector<TransactionState>&, TransactionFlushState&,
- std::queue<TransactionState>&);
+ int flushPendingTransactionQueues(std::vector<QueuedTransactionState>&, TransactionFlushState&);
+ void applyUnsignaledBufferTransaction(std::vector<QueuedTransactionState>&,
+ TransactionFlushState&);
+ void popTransactionFromPending(std::vector<QueuedTransactionState>&, TransactionFlushState&,
+ std::queue<QueuedTransactionState>&);
TransactionReadiness applyFilters(TransactionFlushState&);
- std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash>
+ std::unordered_map<sp<IBinder>, std::queue<QueuedTransactionState>, IListenerHash>
mPendingTransactionQueues;
- LocklessQueue<TransactionState> mLocklessTransactionQueue;
+ LocklessQueue<QueuedTransactionState> mLocklessTransactionQueue;
std::atomic<size_t> mPendingTransactionCount = 0;
ftl::SmallVector<TransactionFilter, 2> mTransactionReadyFilters;
diff --git a/services/surfaceflinger/FrontEnd/Update.h b/services/surfaceflinger/FrontEnd/Update.h
index 4af27ab..f7dfeb8 100644
--- a/services/surfaceflinger/FrontEnd/Update.h
+++ b/services/surfaceflinger/FrontEnd/Update.h
@@ -19,15 +19,15 @@
#include <gui/DisplayInfo.h>
#include "FrontEnd/LayerCreationArgs.h"
+#include "QueuedTransactionState.h"
#include "RequestedLayerState.h"
-#include "TransactionState.h"
namespace android::surfaceflinger::frontend {
// Atomic set of changes affecting layer state. These changes are queued in binder threads and
// applied every vsync.
struct Update {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
std::vector<sp<Layer>> legacyLayers;
std::vector<std::unique_ptr<frontend::RequestedLayerState>> newLayers;
std::vector<LayerCreationArgs> layerCreationArgs;
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index c234a75..6af0f59 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -516,11 +516,6 @@
bool mGetHandleCalled = false;
- // The inherited shadow radius after taking into account the layer hierarchy. This is the
- // final shadow radius for this layer. If a shadow is specified for a layer, then effective
- // shadow radius is the set shadow radius, otherwise its the parent's shadow radius.
- float mEffectiveShadowRadius = 0.f;
-
// Game mode for the layer. Set by WindowManagerShell and recorded by SurfaceFlingerStats.
gui::GameMode mGameMode = gui::GameMode::Unsupported;
diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp
index fea7671..725a782 100644
--- a/services/surfaceflinger/LayerFE.cpp
+++ b/services/surfaceflinger/LayerFE.cpp
@@ -173,7 +173,7 @@
layerSettings.edgeExtensionEffect = mSnapshot->edgeExtensionEffect;
// Record the name of the layer for debugging further down the stack.
layerSettings.name = mSnapshot->name;
- layerSettings.luts = mSnapshot->luts;
+ layerSettings.luts = mSnapshot->luts ? mSnapshot->luts : targetSettings.luts;
if (hasEffect() && !hasBufferOrSidebandStream()) {
prepareEffectsClientComposition(layerSettings, targetSettings);
@@ -191,6 +191,7 @@
layerSettings.disableBlending = true;
layerSettings.bufferId = 0;
layerSettings.frameNumber = 0;
+ layerSettings.sequence = -1;
// If layer is blacked out, force alpha to 1 so that we draw a black color layer.
layerSettings.alpha = blackout ? 1.0f : 0.0f;
@@ -262,6 +263,7 @@
layerSettings.source.buffer.maxLuminanceNits = maxLuminance;
layerSettings.frameNumber = mSnapshot->frameNumber;
layerSettings.bufferId = mSnapshot->externalTexture->getId();
+ layerSettings.sequence = mSnapshot->sequence;
const bool useFiltering = targetSettings.needsFiltering ||
mSnapshot->geomLayerTransform.needsBilinearFiltering();
@@ -425,4 +427,14 @@
LayerFE::ReleaseFencePromiseStatus LayerFE::getReleaseFencePromiseStatus() {
return mReleaseFencePromiseStatus;
}
+
+void LayerFE::setHwcCompositionType(
+ aidl::android::hardware::graphics::composer3::Composition type) {
+ mLastHwcCompositionType = type;
+}
+
+aidl::android::hardware::graphics::composer3::Composition LayerFE::getHwcCompositionType() const {
+ return mLastHwcCompositionType;
+}
+
} // namespace android
diff --git a/services/surfaceflinger/LayerFE.h b/services/surfaceflinger/LayerFE.h
index 9483aeb..64ec278 100644
--- a/services/surfaceflinger/LayerFE.h
+++ b/services/surfaceflinger/LayerFE.h
@@ -59,6 +59,9 @@
void setReleaseFence(const FenceResult& releaseFence) override;
LayerFE::ReleaseFencePromiseStatus getReleaseFencePromiseStatus() override;
void onPictureProfileCommitted() override;
+ void setHwcCompositionType(aidl::android::hardware::graphics::composer3::Composition) override;
+ aidl::android::hardware::graphics::composer3::Composition getHwcCompositionType()
+ const override;
std::unique_ptr<surfaceflinger::frontend::LayerSnapshot> mSnapshot;
@@ -90,6 +93,8 @@
std::string mName;
std::promise<FenceResult> mReleaseFence;
ReleaseFencePromiseStatus mReleaseFencePromiseStatus = ReleaseFencePromiseStatus::UNINITIALIZED;
+ aidl::android::hardware::graphics::composer3::Composition mLastHwcCompositionType =
+ aidl::android::hardware::graphics::composer3::Composition::INVALID;
};
} // namespace android
diff --git a/services/surfaceflinger/PowerAdvisor/PowerAdvisor.cpp b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.cpp
index ff45272..cd7210c 100644
--- a/services/surfaceflinger/PowerAdvisor/PowerAdvisor.cpp
+++ b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.cpp
@@ -29,7 +29,9 @@
#include <android-base/properties.h>
#include <android/binder_libbinder.h>
+#include <common/WorkloadTracer.h>
#include <common/trace.h>
+#include <ftl/concat.h>
#include <utils/Log.h>
#include <utils/Mutex.h>
@@ -44,6 +46,7 @@
namespace android::adpf::impl {
+using namespace android::ftl::flag_operators;
using aidl::android::hardware::common::fmq::SynchronizedReadWrite;
using android::hardware::EventFlag;
@@ -62,6 +65,8 @@
}
}
+static constexpr ftl::Flags<Workload> TRIGGER_LOAD_CHANGE_HINTS = Workload::EFFECTS |
+ Workload::VISIBLE_REGION | Workload::DISPLAY_CHANGES | Workload::SCREENSHOT;
} // namespace
PowerAdvisor::PowerAdvisor(std::function<void()>&& sfDisableExpensiveFn,
@@ -756,4 +761,58 @@
return *mPowerHal;
}
+void PowerAdvisor::setQueuedWorkload(ftl::Flags<Workload> queued) {
+ queued &= TRIGGER_LOAD_CHANGE_HINTS;
+ if (!(queued).get()) return;
+ uint32_t previousQueuedWorkload = mQueuedWorkload.fetch_or(queued.get());
+
+ uint32_t newHints = (previousQueuedWorkload ^ queued.get()) & queued.get();
+ if (newHints) {
+ SFTRACE_INSTANT_FOR_TRACK(WorkloadTracer::TRACK_NAME,
+ ftl::Concat("QueuedWorkload: ",
+ ftl::truncated<20>(ftl::Flags<Workload>(newHints)
+ .string()
+ .c_str()))
+ .c_str());
+ }
+ if (!previousQueuedWorkload) {
+ // TODO(b/385028458) maybe load up hint if close to wake up
+ }
+}
+
+void PowerAdvisor::setScreenshotWorkload() {
+ mCommittedWorkload |= Workload::SCREENSHOT;
+}
+
+void PowerAdvisor::setCommittedWorkload(ftl::Flags<Workload> workload) {
+ workload &= TRIGGER_LOAD_CHANGE_HINTS;
+ uint32_t queued = mQueuedWorkload.exchange(0);
+ mCommittedWorkload |= workload;
+
+ bool cancelLoadupHint = queued && !mCommittedWorkload.get();
+ if (cancelLoadupHint) {
+ SFTRACE_INSTANT_FOR_TRACK(WorkloadTracer::TRACK_NAME,
+ ftl::Concat("UncommittedQueuedWorkload: ",
+ ftl::truncated<20>(ftl::Flags<Workload>(queued)
+ .string()
+ .c_str()))
+ .c_str());
+ // TODO(b/385028458) cancel load up hint
+ }
+
+ bool increasedWorkload = queued == 0 && mCommittedWorkload.get() != 0;
+ if (increasedWorkload) {
+ SFTRACE_INSTANT_FOR_TRACK(WorkloadTracer::TRACK_NAME,
+ ftl::Concat("CommittedWorkload: ",
+ ftl::truncated<20>(mCommittedWorkload.string()))
+ .c_str());
+
+ // TODO(b/385028458) load up hint
+ }
+}
+
+void PowerAdvisor::setCompositedWorkload(ftl::Flags<Workload> composited) {
+ composited &= TRIGGER_LOAD_CHANGE_HINTS;
+ mCommittedWorkload = composited;
+}
} // namespace android::adpf::impl
diff --git a/services/surfaceflinger/PowerAdvisor/PowerAdvisor.h b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.h
index 43fc210..540a9df 100644
--- a/services/surfaceflinger/PowerAdvisor/PowerAdvisor.h
+++ b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.h
@@ -32,9 +32,12 @@
#include <fmq/AidlMessageQueue.h>
#pragma clang diagnostic pop
+#include <common/trace.h>
+#include <ftl/flags.h>
#include <scheduler/Time.h>
#include <ui/DisplayIdentification.h>
#include "../Scheduler/OneShotTimer.h"
+#include "Workload.h"
#include "SessionManager.h"
@@ -109,6 +112,26 @@
// Get the session manager, if it exists
virtual std::shared_ptr<SessionManager> getSessionManager() = 0;
+ // --- Track per frame workloads to use for load up hint heuristics
+ // Track queued workload from transactions as they are queued from the binder thread.
+ // The workload is accumulated and reset on frame commit. The queued workload may be
+ // relevant for the next frame so can be used as an early load up hint. Note this is
+ // only a hint because the transaction can remain in the queue and not be applied on
+ // the next frame.
+ virtual void setQueuedWorkload(ftl::Flags<Workload> workload) = 0;
+ // Track additional workload dur to a screenshot request for load up hint heuristics. This
+ // would indicate an immediate increase in GPU workload.
+ virtual void setScreenshotWorkload() = 0;
+ // Track committed workload from transactions that are applied on the main thread.
+ // This workload is determined from the applied transactions. This can provide a high
+ // confidence that the CPU and or GPU workload will increase immediately.
+ virtual void setCommittedWorkload(ftl::Flags<Workload> workload) = 0;
+ // Update committed workload with the actual workload from post composition. This is
+ // used to update the baseline workload so we can detect increases in workloads on the
+ // next commit. We use composite instead of commit to update the baseline to account
+ // for optimizations like caching which may reduce the workload.
+ virtual void setCompositedWorkload(ftl::Flags<Workload> workload) = 0;
+
// --- The following methods may run on threads besides SF main ---
// Send a hint about an upcoming increase in the CPU workload
virtual void notifyCpuLoadUp() = 0;
@@ -158,6 +181,11 @@
void setTotalFrameTargetWorkDuration(Duration targetDuration) override;
std::shared_ptr<SessionManager> getSessionManager() override;
+ void setQueuedWorkload(ftl::Flags<Workload> workload) override;
+ void setScreenshotWorkload() override;
+ void setCommittedWorkload(ftl::Flags<Workload> workload) override;
+ void setCompositedWorkload(ftl::Flags<Workload> workload) override;
+
// --- The following methods may run on threads besides SF main ---
void notifyCpuLoadUp() override;
void notifyDisplayUpdateImminentAndCpuReset() override;
@@ -332,6 +360,11 @@
static constexpr const Duration kFenceWaitStartDelayValidated{150us};
static constexpr const Duration kFenceWaitStartDelaySkippedValidate{250us};
+ // Track queued and committed workloads per frame. Queued workload is atomic because it's
+ // updated on both binder and the main thread.
+ std::atomic<uint32_t> mQueuedWorkload;
+ ftl::Flags<Workload> mCommittedWorkload;
+
void sendHintSessionHint(aidl::android::hardware::power::SessionHint hint);
template <aidl::android::hardware::power::ChannelMessage::ChannelMessageContents::Tag T,
diff --git a/services/surfaceflinger/PowerAdvisor/Workload.h b/services/surfaceflinger/PowerAdvisor/Workload.h
new file mode 100644
index 0000000..7002357
--- /dev/null
+++ b/services/surfaceflinger/PowerAdvisor/Workload.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 <ftl/flags.h>
+#include <stdint.h>
+
+namespace android::adpf {
+// Additional composition workload that can increase cpu load.
+enum class Workload : uint32_t {
+ NONE = 0,
+ // Layer effects like blur and shadows which forces client composition
+ EFFECTS = 1 << 0,
+
+ // Geometry changes which requires HWC to validate and share composition strategy
+ VISIBLE_REGION = 1 << 1,
+
+ // Diplay changes which can cause geometry changes
+ DISPLAY_CHANGES = 1 << 2,
+
+ // Changes in sf duration which can shorten the deadline for sf to composite the frame
+ WAKEUP = 1 << 3,
+
+ // Increases in refresh rates can cause the deadline for sf to composite to be shorter
+ REFRESH_RATE_INCREASE = 1 << 4,
+
+ // Screenshot requests increase both the cpu and gpu workload
+ SCREENSHOT = 1 << 5
+};
+} // namespace android::adpf
diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/QueuedTransactionState.h
similarity index 84%
rename from services/surfaceflinger/TransactionState.h
rename to services/surfaceflinger/QueuedTransactionState.h
index e5d6481..86683da 100644
--- a/services/surfaceflinger/TransactionState.h
+++ b/services/surfaceflinger/QueuedTransactionState.h
@@ -16,14 +16,14 @@
#pragma once
-#include <condition_variable>
#include <memory>
-#include <mutex>
#include <vector>
#include "FrontEnd/LayerCreationArgs.h"
#include "renderengine/ExternalTexture.h"
+#include <PowerAdvisor/Workload.h>
#include <common/FlagManager.h>
+#include <ftl/flags.h>
#include <gui/LayerState.h>
#include <system/window.h>
@@ -47,18 +47,20 @@
uint32_t touchCropId = UNASSIGNED_LAYER_ID;
};
-struct TransactionState {
- TransactionState() = default;
+struct QueuedTransactionState {
+ QueuedTransactionState() = default;
- TransactionState(const FrameTimelineInfo& frameTimelineInfo,
- std::vector<ResolvedComposerState>& composerStates,
- const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
- const sp<IBinder>& applyToken, const InputWindowCommands& inputWindowCommands,
- int64_t desiredPresentTime, bool isAutoTimestamp,
- std::vector<uint64_t> uncacheBufferIds, int64_t postTime,
- bool hasListenerCallbacks, std::vector<ListenerCallbacks> listenerCallbacks,
- int originPid, int originUid, uint64_t transactionId,
- std::vector<uint64_t> mergedTransactionIds)
+ QueuedTransactionState(const FrameTimelineInfo& frameTimelineInfo,
+ std::vector<ResolvedComposerState>& composerStates,
+ const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
+ const sp<IBinder>& applyToken,
+ const InputWindowCommands& inputWindowCommands,
+ int64_t desiredPresentTime, bool isAutoTimestamp,
+ std::vector<uint64_t> uncacheBufferIds, int64_t postTime,
+ bool hasListenerCallbacks,
+ std::vector<ListenerCallbacks> listenerCallbacks, int originPid,
+ int originUid, uint64_t transactionId,
+ std::vector<uint64_t> mergedTransactionIds)
: frameTimelineInfo(frameTimelineInfo),
states(std::move(composerStates)),
displays(displayStates),
@@ -148,6 +150,7 @@
uint64_t id;
bool sentFenceTimeoutWarning = false;
std::vector<uint64_t> mergedTransactionIds;
+ ftl::Flags<adpf::Workload> workloadHint;
};
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 6e2b943..8c22de1 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -504,7 +504,7 @@
return FrameRateCompatibility::Exact;
case ANATIVEWINDOW_FRAME_RATE_MIN:
return FrameRateCompatibility::Min;
- case ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE:
+ case ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_AT_LEAST:
return FrameRateCompatibility::Gte;
case ANATIVEWINDOW_FRAME_RATE_NO_VOTE:
return FrameRateCompatibility::NoVote;
diff --git a/services/surfaceflinger/ScreenCaptureOutput.cpp b/services/surfaceflinger/ScreenCaptureOutput.cpp
index 41a9a1b..5f71b88 100644
--- a/services/surfaceflinger/ScreenCaptureOutput.cpp
+++ b/services/surfaceflinger/ScreenCaptureOutput.cpp
@@ -16,11 +16,13 @@
#include "ScreenCaptureOutput.h"
#include "ScreenCaptureRenderSurface.h"
+#include "common/include/common/FlagManager.h"
#include "ui/Rotation.h"
#include <compositionengine/CompositionEngine.h>
#include <compositionengine/DisplayColorProfileCreationArgs.h>
#include <compositionengine/impl/DisplayColorProfile.h>
+#include <ui/HdrRenderTypeUtils.h>
#include <ui/Rotation.h>
namespace android {
@@ -104,14 +106,84 @@
return clientCompositionDisplay;
}
+std::unordered_map<int32_t, aidl::android::hardware::graphics::composer3::Luts>
+ScreenCaptureOutput::generateLuts() {
+ std::unordered_map<int32_t, aidl::android::hardware::graphics::composer3::Luts> lutsMapper;
+ if (FlagManager::getInstance().luts_api()) {
+ std::vector<sp<GraphicBuffer>> buffers;
+ std::vector<int32_t> layerIds;
+
+ for (const auto* layer : getOutputLayersOrderedByZ()) {
+ const auto& layerState = layer->getState();
+ const auto* layerFEState = layer->getLayerFE().getCompositionState();
+ auto pixelFormat = layerFEState->buffer
+ ? std::make_optional(
+ static_cast<ui::PixelFormat>(layerFEState->buffer->getPixelFormat()))
+ : std::nullopt;
+ const auto hdrType = getHdrRenderType(layerState.dataspace, pixelFormat,
+ layerFEState->desiredHdrSdrRatio);
+ if (layerFEState->buffer && !layerFEState->luts &&
+ hdrType == HdrRenderType::GENERIC_HDR) {
+ buffers.push_back(layerFEState->buffer);
+ layerIds.push_back(layer->getLayerFE().getSequence());
+ }
+ }
+
+ std::vector<aidl::android::hardware::graphics::composer3::Luts> luts;
+ if (auto displayDevice = mRenderArea.getDisplayDevice()) {
+ const auto id = PhysicalDisplayId::tryCast(displayDevice->getId());
+ if (id) {
+ auto& hwc = getCompositionEngine().getHwComposer();
+ hwc.getLuts(*id, buffers, &luts);
+ }
+ }
+
+ if (buffers.size() == luts.size()) {
+ for (size_t i = 0; i < luts.size(); i++) {
+ lutsMapper[layerIds[i]] = std::move(luts[i]);
+ }
+ }
+ }
+ return lutsMapper;
+}
+
std::vector<compositionengine::LayerFE::LayerSettings>
ScreenCaptureOutput::generateClientCompositionRequests(
bool supportsProtectedContent, ui::Dataspace outputDataspace,
std::vector<compositionengine::LayerFE*>& outLayerFEs) {
+ // This map maps the layer unique id to a Lut
+ std::unordered_map<int32_t, aidl::android::hardware::graphics::composer3::Luts> lutsMapper =
+ generateLuts();
+
auto clientCompositionLayers = compositionengine::impl::Output::
generateClientCompositionRequests(supportsProtectedContent, outputDataspace,
outLayerFEs);
+ for (auto& layer : clientCompositionLayers) {
+ if (lutsMapper.find(layer.sequence) != lutsMapper.end()) {
+ auto& aidlLuts = lutsMapper[layer.sequence];
+ if (aidlLuts.pfd.get() >= 0 && aidlLuts.offsets) {
+ std::vector<int32_t> offsets = *aidlLuts.offsets;
+ std::vector<int32_t> dimensions;
+ dimensions.reserve(offsets.size());
+ std::vector<int32_t> sizes;
+ sizes.reserve(offsets.size());
+ std::vector<int32_t> keys;
+ keys.reserve(offsets.size());
+ for (size_t j = 0; j < offsets.size(); j++) {
+ dimensions.emplace_back(
+ static_cast<int32_t>(aidlLuts.lutProperties[j].dimension));
+ sizes.emplace_back(aidlLuts.lutProperties[j].size);
+ keys.emplace_back(
+ static_cast<int32_t>(aidlLuts.lutProperties[j].samplingKeys[0]));
+ }
+ layer.luts = std::make_shared<gui::DisplayLuts>(base::unique_fd(
+ aidlLuts.pfd.dup().get()),
+ offsets, dimensions, sizes, keys);
+ }
+ }
+ }
+
if (mRegionSampling) {
for (auto& layer : clientCompositionLayers) {
layer.backgroundBlurRadius = 0;
diff --git a/services/surfaceflinger/ScreenCaptureOutput.h b/services/surfaceflinger/ScreenCaptureOutput.h
index c233ead..444a28f 100644
--- a/services/surfaceflinger/ScreenCaptureOutput.h
+++ b/services/surfaceflinger/ScreenCaptureOutput.h
@@ -20,6 +20,7 @@
#include <compositionengine/RenderSurface.h>
#include <compositionengine/impl/Output.h>
#include <ui/Rect.h>
+#include <unordered_map>
#include "RenderArea.h"
@@ -65,6 +66,7 @@
const std::shared_ptr<renderengine::ExternalTexture>& buffer) const override;
private:
+ std::unordered_map<int32_t, aidl::android::hardware::graphics::composer3::Luts> generateLuts();
const RenderArea& mRenderArea;
const compositionengine::Output::ColorProfile& mColorProfile;
const bool mRegionSampling;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index e05c5bd9..d6225e2 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -44,6 +44,7 @@
#include <com_android_graphics_libgui_flags.h>
#include <com_android_graphics_surfaceflinger_flags.h>
#include <common/FlagManager.h>
+#include <common/WorkloadTracer.h>
#include <common/trace.h>
#include <compositionengine/CompositionEngine.h>
#include <compositionengine/CompositionRefreshArgs.h>
@@ -156,6 +157,7 @@
#include "MutexUtils.h"
#include "NativeWindowSurface.h"
#include "PowerAdvisor/PowerAdvisor.h"
+#include "PowerAdvisor/Workload.h"
#include "RegionSamplingThread.h"
#include "RenderAreaBuilder.h"
#include "Scheduler/EventThread.h"
@@ -879,6 +881,8 @@
return renderengine::RenderEngine::BlurAlgorithm::GAUSSIAN;
} else if (algorithm == "kawase2") {
return renderengine::RenderEngine::BlurAlgorithm::KAWASE_DUAL_FILTER;
+ } else if (algorithm == "kawase") {
+ return renderengine::RenderEngine::BlurAlgorithm::KAWASE;
} else {
if (FlagManager::getInstance().window_blur_kawase2()) {
return renderengine::RenderEngine::BlurAlgorithm::KAWASE_DUAL_FILTER;
@@ -920,7 +924,8 @@
mCompositionEngine->setTimeStats(mTimeStats);
- mCompositionEngine->setHwComposer(getFactory().createHWComposer(mHwcServiceName));
+ mHWComposer = getFactory().createHWComposer(mHwcServiceName);
+ mCompositionEngine->setHwComposer(mHWComposer.get());
auto& composer = mCompositionEngine->getHwComposer();
composer.setCallback(*this);
mDisplayModeController.setHwComposer(&composer);
@@ -2457,6 +2462,7 @@
bool flushTransactions, bool& outTransactionsAreEmpty) {
using Changes = frontend::RequestedLayerState::Changes;
SFTRACE_CALL();
+ SFTRACE_NAME_FOR_TRACK(WorkloadTracer::TRACK_NAME, "Transaction Handling");
frontend::Update update;
if (flushTransactions) {
SFTRACE_NAME("TransactionHandler:flushTransactions");
@@ -2483,8 +2489,20 @@
mDestroyedHandles.clear();
}
+ size_t addedLayers = update.newLayers.size();
mLayerLifecycleManager.addLayers(std::move(update.newLayers));
update.transactions = mTransactionHandler.flushTransactions();
+ ftl::Flags<adpf::Workload> committedWorkload;
+ for (auto& transaction : update.transactions) {
+ committedWorkload |= transaction.workloadHint;
+ }
+ SFTRACE_INSTANT_FOR_TRACK(WorkloadTracer::TRACK_NAME,
+ ftl::Concat("Layers: +", addedLayers, " -",
+ update.destroyedHandles.size(),
+ " txns:", update.transactions.size())
+ .c_str());
+
+ mPowerAdvisor->setCommittedWorkload(committedWorkload);
if (mTransactionTracing) {
mTransactionTracing->addCommittedTransactions(ftl::to_underlying(vsyncId), frameTimeNs,
update, mFrontEndDisplayInfos,
@@ -2685,7 +2703,7 @@
return false;
}
}
-
+ SFTRACE_NAME_FOR_TRACK(WorkloadTracer::TRACK_NAME, "Commit");
const Period vsyncPeriod = mScheduler->getVsyncSchedule()->period();
// Save this once per commit + composite to ensure consistency
@@ -2759,6 +2777,7 @@
// Hold mStateLock as chooseRefreshRateForContent promotes wp<Layer> to sp<Layer>
// and may eventually call to ~Layer() if it holds the last reference
{
+ SFTRACE_NAME_FOR_TRACK(WorkloadTracer::TRACK_NAME, "Refresh Rate Selection");
bool updateAttachedChoreographer = mUpdateAttachedChoreographer;
mUpdateAttachedChoreographer = false;
@@ -2785,6 +2804,8 @@
CompositeResultsPerDisplay SurfaceFlinger::composite(
PhysicalDisplayId pacesetterId, const scheduler::FrameTargeters& frameTargeters) {
+ SFTRACE_ASYNC_FOR_TRACK_BEGIN(WorkloadTracer::TRACK_NAME, "Composition",
+ WorkloadTracer::COMPOSITION_TRACE_COOKIE);
const scheduler::FrameTarget& pacesetterTarget =
frameTargeters.get(pacesetterId)->get()->target();
@@ -2947,10 +2968,34 @@
}
mCompositionEngine->present(refreshArgs);
- moveSnapshotsFromCompositionArgs(refreshArgs, layers);
+ ftl::Flags<adpf::Workload> compositedWorkload;
+ if (refreshArgs.updatingGeometryThisFrame || refreshArgs.updatingOutputGeometryThisFrame) {
+ compositedWorkload |= adpf::Workload::VISIBLE_REGION;
+ }
+ if (mFrontEndDisplayInfosChanged) {
+ compositedWorkload |= adpf::Workload::DISPLAY_CHANGES;
+ SFTRACE_INSTANT_FOR_TRACK(WorkloadTracer::TRACK_NAME, "Display Changes");
+ }
+ int index = 0;
+ std::array<char, WorkloadTracer::COMPOSITION_SUMMARY_SIZE> compositionSummary = {0};
+ auto lastLayerStack = ui::INVALID_LAYER_STACK;
for (auto& [layer, layerFE] : layers) {
CompositionResult compositionResult{layerFE->stealCompositionResult()};
+ if (index < compositionSummary.size()) {
+ if (lastLayerStack != ui::INVALID_LAYER_STACK &&
+ lastLayerStack != layerFE->mSnapshot->outputFilter.layerStack) {
+ // add a space to separate displays
+ compositionSummary[index++] = ' ';
+ }
+ lastLayerStack = layerFE->mSnapshot->outputFilter.layerStack;
+ compositionSummary[index++] = layerFE->mSnapshot->classifyCompositionForDebug(
+ layerFE->getHwcCompositionType());
+ if (layerFE->mSnapshot->hasEffect()) {
+ compositedWorkload |= adpf::Workload::EFFECTS;
+ }
+ }
+
if (compositionResult.lastClientCompositionFence) {
layer->setWasClientComposed(compositionResult.lastClientCompositionFence);
}
@@ -2959,6 +3004,20 @@
}
}
+ // Concisely describe the layers composited this frame using single chars. GPU composited layers
+ // are uppercase, DPU composited are lowercase. Special chars denote effects (blur, shadow,
+ // etc.). This provides a snapshot of the compositing workload.
+ SFTRACE_INSTANT_FOR_TRACK(WorkloadTracer::TRACK_NAME,
+ ftl::Concat("Layers: ", layers.size(), " ",
+ ftl::truncated<WorkloadTracer::COMPOSITION_SUMMARY_SIZE>(
+ compositionSummary.data()))
+ .c_str());
+
+ mPowerAdvisor->setCompositedWorkload(compositedWorkload);
+ moveSnapshotsFromCompositionArgs(refreshArgs, layers);
+ SFTRACE_ASYNC_FOR_TRACK_END(WorkloadTracer::TRACK_NAME,
+ WorkloadTracer::COMPOSITION_TRACE_COOKIE);
+ SFTRACE_NAME_FOR_TRACK(WorkloadTracer::TRACK_NAME, "Post Composition");
SFTRACE_NAME("postComposition");
mTimeStats->recordFrameDuration(pacesetterTarget.frameBeginTime().ns(), systemTime());
@@ -3212,12 +3271,12 @@
const auto schedule = mScheduler->getVsyncSchedule();
const TimePoint vsyncDeadline = schedule->vsyncDeadlineAfter(presentTime);
- const Period vsyncPeriod = schedule->period();
+ const Fps renderRate = pacesetterDisplay->refreshRateSelector().getActiveMode().fps;
const nsecs_t vsyncPhase =
mScheduler->getVsyncConfiguration().getCurrentConfigs().late.sfOffset;
- const CompositorTiming compositorTiming(vsyncDeadline.ns(), vsyncPeriod.ns(), vsyncPhase,
- presentLatency.ns());
+ const CompositorTiming compositorTiming(vsyncDeadline.ns(), renderRate.getPeriodNsecs(),
+ vsyncPhase, presentLatency.ns());
ui::DisplayMap<ui::LayerStack, const DisplayDevice*> layerStackToDisplay;
{
@@ -4774,16 +4833,16 @@
// For tests only
bool SurfaceFlinger::flushTransactionQueues() {
mTransactionHandler.collectTransactions();
- std::vector<TransactionState> transactions = mTransactionHandler.flushTransactions();
+ std::vector<QueuedTransactionState> transactions = mTransactionHandler.flushTransactions();
return applyTransactions(transactions);
}
-bool SurfaceFlinger::applyTransactions(std::vector<TransactionState>& transactions) {
+bool SurfaceFlinger::applyTransactions(std::vector<QueuedTransactionState>& transactions) {
Mutex::Autolock lock(mStateLock);
return applyTransactionsLocked(transactions);
}
-bool SurfaceFlinger::applyTransactionsLocked(std::vector<TransactionState>& transactions) {
+bool SurfaceFlinger::applyTransactionsLocked(std::vector<QueuedTransactionState>& transactions) {
bool needsTraversal = false;
// Now apply all transactions.
for (auto& transaction : transactions) {
@@ -4873,8 +4932,15 @@
const int originPid = ipc->getCallingPid();
const int originUid = ipc->getCallingUid();
uint32_t permissions = LayerStatePermissions::getTransactionPermissions(originPid, originUid);
+ ftl::Flags<adpf::Workload> queuedWorkload;
for (auto& composerState : states) {
composerState.state.sanitize(permissions);
+ if (composerState.state.what & layer_state_t::COMPOSITION_EFFECTS) {
+ queuedWorkload |= adpf::Workload::EFFECTS;
+ }
+ if (composerState.state.what & layer_state_t::VISIBLE_REGION_CHANGES) {
+ queuedWorkload |= adpf::Workload::VISIBLE_REGION;
+ }
}
for (DisplayState& display : displays) {
@@ -4897,6 +4963,10 @@
flags &= ~(eEarlyWakeupStart | eEarlyWakeupEnd);
}
}
+ if (flags & eEarlyWakeupStart) {
+ queuedWorkload |= adpf::Workload::WAKEUP;
+ }
+ mPowerAdvisor->setQueuedWorkload(queuedWorkload);
const int64_t postTime = systemTime();
@@ -4944,22 +5014,23 @@
}
}
- TransactionState state{frameTimelineInfo,
- resolvedStates,
- displays,
- flags,
- applyToken,
- std::move(inputWindowCommands),
- desiredPresentTime,
- isAutoTimestamp,
- std::move(uncacheBufferIds),
- postTime,
- hasListenerCallbacks,
- listenerCallbacks,
- originPid,
- originUid,
- transactionId,
- mergedTransactionIds};
+ QueuedTransactionState state{frameTimelineInfo,
+ resolvedStates,
+ displays,
+ flags,
+ applyToken,
+ std::move(inputWindowCommands),
+ desiredPresentTime,
+ isAutoTimestamp,
+ std::move(uncacheBufferIds),
+ postTime,
+ hasListenerCallbacks,
+ listenerCallbacks,
+ originPid,
+ originUid,
+ transactionId,
+ mergedTransactionIds};
+ state.workloadHint = queuedWorkload;
if (mTransactionTracing) {
mTransactionTracing->addQueuedTransaction(state);
@@ -5044,7 +5115,7 @@
}
bool SurfaceFlinger::applyAndCommitDisplayTransactionStatesLocked(
- std::vector<TransactionState>& transactions) {
+ std::vector<QueuedTransactionState>& transactions) {
bool needsTraversal = false;
uint32_t transactionFlags = 0;
for (auto& transaction : transactions) {
@@ -5438,7 +5509,7 @@
}
void SurfaceFlinger::initializeDisplays() {
- TransactionState state;
+ QueuedTransactionState state;
state.inputWindowCommands = mInputWindowCommands;
const nsecs_t now = systemTime();
state.desiredPresentTime = now;
@@ -5453,7 +5524,7 @@
state.displays.push(DisplayState(display.token(), ui::LayerStack::fromValue(layerStack++)));
}
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back(state);
{
@@ -7393,6 +7464,8 @@
std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) {
return mScheduler
->schedule([=, this, &renderAreaBuilder, &layers]() REQUIRES(kMainThreadContext) {
+ SFTRACE_NAME_FOR_TRACK(WorkloadTracer::TRACK_NAME, "Screenshot");
+ mPowerAdvisor->setScreenshotWorkload();
SFTRACE_NAME("getSnapshotsFromMainThread");
layers = getLayerSnapshotsFn();
// Non-threaded RenderEngine eventually returns to the main thread a 2nd time
@@ -7575,7 +7648,7 @@
if (hdrBuffer && gainmapBuffer) {
ftl::SharedFuture<FenceResult> hdrRenderFuture =
- renderScreenImpl(renderArea.get(), hdrBuffer, regionSampling, grayscale,
+ renderScreenImpl(std::move(renderArea), hdrBuffer, regionSampling, grayscale,
isProtected, captureResults, displayState, layers);
captureResults.buffer = buffer->getBuffer();
captureResults.optionalGainMap = gainmapBuffer->getBuffer();
@@ -7599,7 +7672,7 @@
})
.share();
} else {
- renderFuture = renderScreenImpl(renderArea.get(), buffer, regionSampling, grayscale,
+ renderFuture = renderScreenImpl(std::move(renderArea), buffer, regionSampling, grayscale,
isProtected, captureResults, displayState, layers);
}
@@ -7620,7 +7693,8 @@
}
ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl(
- const RenderArea* renderArea, const std::shared_ptr<renderengine::ExternalTexture>& buffer,
+ std::unique_ptr<const RenderArea> renderArea,
+ const std::shared_ptr<renderengine::ExternalTexture>& buffer,
bool regionSampling, bool grayscale, bool isProtected, ScreenCaptureResults& captureResults,
const std::optional<OutputCompositionState>& displayState,
const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) {
@@ -7697,6 +7771,7 @@
std::unique_ptr<compositionengine::CompositionEngine> compositionEngine =
mFactory.createCompositionEngine();
compositionEngine->setRenderEngine(mRenderEngine.get());
+ compositionEngine->setHwComposer(mHWComposer.get());
std::vector<sp<compositionengine::LayerFE>> layerFEs;
layerFEs.reserve(layers.size());
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index c85c084..a793d50 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -87,6 +87,7 @@
#include "LayerVector.h"
#include "MutexUtils.h"
#include "PowerAdvisor/PowerAdvisor.h"
+#include "QueuedTransactionState.h"
#include "Scheduler/ISchedulerCallback.h"
#include "Scheduler/RefreshRateSelector.h"
#include "Scheduler/Scheduler.h"
@@ -95,7 +96,6 @@
#include "Tracing/LayerTracing.h"
#include "Tracing/TransactionTracing.h"
#include "TransactionCallbackInvoker.h"
-#include "TransactionState.h"
#include "Utils/OnceFuture.h"
#include <algorithm>
@@ -803,8 +803,9 @@
// For test only
bool flushTransactionQueues() REQUIRES(kMainThreadContext);
- bool applyTransactions(std::vector<TransactionState>&) REQUIRES(kMainThreadContext);
- bool applyAndCommitDisplayTransactionStatesLocked(std::vector<TransactionState>& transactions)
+ bool applyTransactions(std::vector<QueuedTransactionState>&) REQUIRES(kMainThreadContext);
+ bool applyAndCommitDisplayTransactionStatesLocked(
+ std::vector<QueuedTransactionState>& transactions)
REQUIRES(kMainThreadContext, mStateLock);
// Returns true if there is at least one transaction that needs to be flushed
@@ -833,7 +834,7 @@
static LatchUnsignaledConfig getLatchUnsignaledConfig();
bool shouldLatchUnsignaled(const layer_state_t&, size_t numStates, bool firstTransaction) const;
- bool applyTransactionsLocked(std::vector<TransactionState>& transactions)
+ bool applyTransactionsLocked(std::vector<QueuedTransactionState>& transactions)
REQUIRES(mStateLock, kMainThreadContext);
uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands)
@@ -893,7 +894,8 @@
const std::shared_ptr<renderengine::ExternalTexture>& gainmapBuffer = nullptr);
ftl::SharedFuture<FenceResult> renderScreenImpl(
- const RenderArea*, const std::shared_ptr<renderengine::ExternalTexture>&,
+ std::unique_ptr<const RenderArea> renderArea,
+ const std::shared_ptr<renderengine::ExternalTexture>&,
bool regionSampling, bool grayscale, bool isProtected, ScreenCaptureResults&,
const std::optional<OutputCompositionState>& displayState,
const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers);
@@ -1367,6 +1369,7 @@
std::atomic<int> mNumTrustedPresentationListeners = 0;
std::unique_ptr<compositionengine::CompositionEngine> mCompositionEngine;
+ std::unique_ptr<HWComposer> mHWComposer;
CompositionCoveragePerDisplay mCompositionCoverage;
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
index f39a4d2..2676ca6 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -20,8 +20,8 @@
#include "FrontEnd/LayerCreationArgs.h"
#include "LayerProtoHelper.h"
+#include "QueuedTransactionState.h"
#include "TransactionProtoParser.h"
-#include "TransactionState.h"
#include "gui/LayerState.h"
namespace android::surfaceflinger {
@@ -51,7 +51,8 @@
~FakeExternalTexture() = default;
};
-perfetto::protos::TransactionState TransactionProtoParser::toProto(const TransactionState& t) {
+perfetto::protos::TransactionState TransactionProtoParser::toProto(
+ const QueuedTransactionState& t) {
perfetto::protos::TransactionState proto;
proto.set_pid(t.originPid);
proto.set_uid(t.originUid);
@@ -300,9 +301,9 @@
return proto;
}
-TransactionState TransactionProtoParser::fromProto(
+QueuedTransactionState TransactionProtoParser::fromProto(
const perfetto::protos::TransactionState& proto) {
- TransactionState t;
+ QueuedTransactionState t;
t.originPid = proto.pid();
t.originUid = proto.uid();
t.frameTimelineInfo.vsyncId = proto.vsync_id();
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.h b/services/surfaceflinger/Tracing/TransactionProtoParser.h
index b3ab71c..a02e231 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.h
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.h
@@ -21,7 +21,7 @@
#include "FrontEnd/DisplayInfo.h"
#include "FrontEnd/LayerCreationArgs.h"
-#include "TransactionState.h"
+#include "QueuedTransactionState.h"
namespace android::surfaceflinger {
@@ -44,14 +44,14 @@
TransactionProtoParser(std::unique_ptr<FlingerDataMapper> provider)
: mMapper(std::move(provider)) {}
- perfetto::protos::TransactionState toProto(const TransactionState&);
+ perfetto::protos::TransactionState toProto(const QueuedTransactionState&);
perfetto::protos::TransactionState toProto(
const std::map<uint32_t /* layerId */, TracingLayerState>&);
perfetto::protos::LayerCreationArgs toProto(const LayerCreationArgs& args);
perfetto::protos::LayerState toProto(const ResolvedComposerState&);
static perfetto::protos::DisplayInfo toProto(const frontend::DisplayInfo&, uint32_t layerStack);
- TransactionState fromProto(const perfetto::protos::TransactionState&);
+ QueuedTransactionState fromProto(const perfetto::protos::TransactionState&);
void mergeFromProto(const perfetto::protos::LayerState&, TracingLayerState& outState);
void fromProto(const perfetto::protos::LayerCreationArgs&, LayerCreationArgs& outArgs);
std::unique_ptr<FlingerDataMapper> mMapper;
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.cpp b/services/surfaceflinger/Tracing/TransactionTracing.cpp
index bc9f809..1cd7517 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.cpp
+++ b/services/surfaceflinger/Tracing/TransactionTracing.cpp
@@ -166,7 +166,7 @@
mBuffer.dump(result);
}
-void TransactionTracing::addQueuedTransaction(const TransactionState& transaction) {
+void TransactionTracing::addQueuedTransaction(const QueuedTransactionState& transaction) {
perfetto::protos::TransactionState* state =
new perfetto::protos::TransactionState(mProtoParser.toProto(transaction));
mTransactionQueue.push(state);
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.h b/services/surfaceflinger/Tracing/TransactionTracing.h
index 7a0fb5e..d784168 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.h
+++ b/services/surfaceflinger/Tracing/TransactionTracing.h
@@ -134,7 +134,7 @@
// Flush event from perfetto data source
void onFlush(Mode mode);
- void addQueuedTransaction(const TransactionState&);
+ void addQueuedTransaction(const QueuedTransactionState&);
void addCommittedTransactions(int64_t vsyncId, nsecs_t commitTime, frontend::Update& update,
const frontend::DisplayInfos&, bool displayInfoChanged);
status_t writeToFile(const std::string& filename = FILE_PATH);
diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
index 1dba175..5cf4244 100644
--- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
+++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
@@ -31,8 +31,8 @@
#include "FrontEnd/LayerCreationArgs.h"
#include "FrontEnd/RequestedLayerState.h"
#include "LayerProtoHelper.h"
+#include "QueuedTransactionState.h"
#include "Tracing/LayerTracing.h"
-#include "TransactionState.h"
#include "cutils/properties.h"
#include "LayerTraceGenerator.h"
@@ -95,11 +95,11 @@
addedLayers.emplace_back(std::make_unique<frontend::RequestedLayerState>(args));
}
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.reserve((size_t)entry.transactions_size());
for (int j = 0; j < entry.transactions_size(); j++) {
// apply transactions
- TransactionState transaction = parser.fromProto(entry.transactions(j));
+ QueuedTransactionState transaction = parser.fromProto(entry.transactions(j));
for (auto& resolvedComposerState : transaction.states) {
if (resolvedComposerState.state.what & layer_state_t::eInputInfoChanged) {
if (!resolvedComposerState.state.windowInfoHandle->getInfo()->inputConfig.test(
diff --git a/services/surfaceflinger/common/include/common/WorkloadTracer.h b/services/surfaceflinger/common/include/common/WorkloadTracer.h
new file mode 100644
index 0000000..39b6fa1
--- /dev/null
+++ b/services/surfaceflinger/common/include/common/WorkloadTracer.h
@@ -0,0 +1,29 @@
+
+/*
+ * 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 <ftl/flags.h>
+#include <stdint.h>
+namespace android::WorkloadTracer {
+
+static constexpr int32_t COMPOSITION_TRACE_COOKIE = 1;
+static constexpr int32_t POST_COMPOSITION_TRACE_COOKIE = 2;
+static constexpr size_t COMPOSITION_SUMMARY_SIZE = 20;
+static constexpr const char* TRACK_NAME = "CriticalWorkload";
+
+} // namespace android::WorkloadTracer
\ No newline at end of file
diff --git a/services/surfaceflinger/common/include/common/trace.h b/services/surfaceflinger/common/include/common/trace.h
index dc5716b..9a7e97f 100644
--- a/services/surfaceflinger/common/include/common/trace.h
+++ b/services/surfaceflinger/common/include/common/trace.h
@@ -65,6 +65,8 @@
#define SFTRACE_NAME(name) ::android::ScopedTrace PASTE(___tracer, __LINE__)(name)
// SFTRACE_CALL is an SFTRACE_NAME that uses the current function name.
#define SFTRACE_CALL() SFTRACE_NAME(__FUNCTION__)
+#define SFTRACE_NAME_FOR_TRACK(trackName, name) \
+ ::android::ScopedTraceForTrack PASTE(___tracer, __LINE__)(trackName, name)
#define SFTRACE_FORMAT(fmt, ...) \
::android::ScopedTrace PASTE(___tracer, __LINE__)(fmt, ##__VA_ARGS__)
@@ -87,4 +89,21 @@
inline ~ScopedTrace() { SFTRACE_END(); }
};
+class ScopedTraceForTrack {
+public:
+ inline ScopedTraceForTrack(const char* trackName, const char* name)
+ : mCookie(getUniqueCookie()), mTrackName(trackName) {
+ SFTRACE_ASYNC_FOR_TRACK_BEGIN(mTrackName, name, mCookie);
+ }
+ inline ~ScopedTraceForTrack() { SFTRACE_ASYNC_FOR_TRACK_END(mTrackName, mCookie); }
+
+private:
+ static int32_t getUniqueCookie() {
+ static std::atomic<int32_t> sUniqueCookie = 1000;
+ return sUniqueCookie++;
+ }
+ int32_t mCookie;
+ const char* mTrackName;
+};
+
} // namespace android
diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
index f247c9f..151611c 100644
--- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
@@ -585,6 +585,83 @@
}
}
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetClientDrawnCornerRadius) {
+ sp<SurfaceControl> layer;
+ const uint8_t size = 64;
+ const uint8_t testArea = 4;
+ const float cornerRadius = 20.0f;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", size, size));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, size, size));
+
+ Transaction()
+ .setClientDrawnCornerRadius(layer, cornerRadius)
+ .setCornerRadius(layer, cornerRadius)
+ .setCrop(layer, Rect(size, size))
+ .apply();
+
+ {
+ const uint8_t bottom = size - 1;
+ const uint8_t right = size - 1;
+ auto shot = getScreenCapture();
+ // Solid corners
+ shot->expectColor(Rect(0, 0, testArea, testArea), Color::RED);
+ shot->expectColor(Rect(size - testArea, 0, right, testArea), Color::RED);
+ shot->expectColor(Rect(0, bottom - testArea, testArea, bottom), Color::RED);
+ shot->expectColor(Rect(size - testArea, bottom - testArea, right, bottom), Color::RED);
+ // Solid center
+ shot->expectColor(Rect(size / 2 - testArea / 2, size / 2 - testArea / 2,
+ size / 2 + testArea / 2, size / 2 + testArea / 2),
+ Color::RED);
+ }
+}
+
+// Test if ParentCornerRadiusTakesPrecedence if the parent's client drawn corner radius crop
+// is fully contained by the child corner radius crop.
+TEST_P(LayerTypeAndRenderTypeTransactionTest, ParentCornerRadiusPrecedenceClientDrawnCornerRadius) {
+ sp<SurfaceControl> parent;
+ sp<SurfaceControl> child;
+ const uint32_t size = 64;
+ const uint32_t parentSize = size * 3;
+ const Rect parentCrop(size, size, size, size);
+ const uint32_t testLength = 4;
+ const float cornerRadius = 20.0f;
+ ASSERT_NO_FATAL_FAILURE(parent = createLayer("parent", parentSize, parentSize));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(parent, Color::RED, parentSize, parentSize));
+ ASSERT_NO_FATAL_FAILURE(child = createLayer("child", size, size));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::GREEN, size, size));
+
+ Transaction()
+ .setCornerRadius(parent, cornerRadius)
+ .setCrop(parent, parentCrop)
+ .setClientDrawnCornerRadius(parent, cornerRadius)
+ .reparent(child, parent)
+ .setPosition(child, size, size)
+ .apply(true);
+
+ {
+ const uint32_t top = size;
+ const uint32_t left = size;
+ const uint32_t bottom = size * 2;
+ const uint32_t right = size * 2;
+ auto shot = getScreenCapture();
+ // Corners are RED because parent's client drawn corner radius is actually 0
+ // and the child is fully within the parent's crop
+ // TL
+ shot->expectColor(Rect(left, top, testLength, testLength), Color::RED);
+ // TR
+ shot->expectColor(Rect(right - testLength, top, testLength, testLength), Color::RED);
+ // BL
+ shot->expectColor(Rect(left, bottom - testLength, testLength, testLength), Color::RED);
+ // BR
+ shot->expectColor(Rect(right - testLength, bottom - testLength, testLength, testLength),
+ Color::RED);
+ // Solid center
+ shot->expectColor(Rect(parentSize / 2 - testLength, parentSize / 2 - testLength, testLength,
+ testLength),
+ Color::GREEN);
+ }
+}
+
TEST_P(LayerTypeAndRenderTypeTransactionTest, SetBackgroundBlurRadiusSimple) {
if (!deviceSupportsBlurs()) GTEST_SKIP();
if (!deviceUsesSkiaRenderEngine()) GTEST_SKIP();
diff --git a/services/surfaceflinger/tests/benchmarks/LayerLifecycleManager_benchmarks.cpp b/services/surfaceflinger/tests/benchmarks/LayerLifecycleManager_benchmarks.cpp
index 7641a45..0925118 100644
--- a/services/surfaceflinger/tests/benchmarks/LayerLifecycleManager_benchmarks.cpp
+++ b/services/surfaceflinger/tests/benchmarks/LayerLifecycleManager_benchmarks.cpp
@@ -50,7 +50,7 @@
layers.emplace_back(LayerLifecycleManagerHelper::rootLayer(1));
lifecycleManager.addLayers(std::move(layers));
lifecycleManager.commitChanges();
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
auto& transactionState = transactions.back().states.front();
@@ -74,7 +74,7 @@
std::vector<std::unique_ptr<RequestedLayerState>> layers;
layers.emplace_back(LayerLifecycleManagerHelper::rootLayer(1));
lifecycleManager.addLayers(std::move(layers));
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
auto& transactionState = transactions.back().states.front();
diff --git a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
index 9794620..ee5d919 100644
--- a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
+++ b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
@@ -66,8 +66,8 @@
/*mirror=*/UNASSIGNED_LAYER_ID));
}
- static std::vector<TransactionState> setZTransaction(uint32_t id, int32_t z) {
- std::vector<TransactionState> transactions;
+ static std::vector<QueuedTransactionState> setZTransaction(uint32_t id, int32_t z) {
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -109,8 +109,9 @@
mLifecycleManager.addLayers(std::move(layers));
}
- std::vector<TransactionState> reparentLayerTransaction(uint32_t id, uint32_t newParentId) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> reparentLayerTransaction(uint32_t id,
+ uint32_t newParentId) {
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().parentId = newParentId;
@@ -124,8 +125,9 @@
mLifecycleManager.applyTransactions(reparentLayerTransaction(id, newParentId));
}
- std::vector<TransactionState> relativeLayerTransaction(uint32_t id, uint32_t relativeParentId) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> relativeLayerTransaction(uint32_t id,
+ uint32_t relativeParentId) {
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().relativeParentId = relativeParentId;
@@ -139,7 +141,7 @@
}
void removeRelativeZ(uint32_t id) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().state.what = layer_state_t::eLayerChanged;
@@ -148,7 +150,7 @@
}
void setPosition(uint32_t id, float x, float y) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().state.what = layer_state_t::ePositionChanged;
@@ -167,7 +169,7 @@
}
void updateBackgroundColor(uint32_t id, half alpha) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
@@ -183,7 +185,7 @@
}
void setCrop(uint32_t id, const FloatRect& crop) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -196,7 +198,7 @@
void setCrop(uint32_t id, const Rect& crop) { setCrop(id, crop.toFloatRect()); }
void setFlags(uint32_t id, uint32_t mask, uint32_t flags) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -208,7 +210,7 @@
}
void setAlpha(uint32_t id, float alpha) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -219,7 +221,7 @@
}
void setAutoRefresh(uint32_t id, bool autoRefresh) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -236,7 +238,7 @@
void showLayer(uint32_t id) { setFlags(id, layer_state_t::eLayerHidden, 0); }
void setColor(uint32_t id, half3 rgb = half3(1._hf, 1._hf, 1._hf)) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().state.what = layer_state_t::eColorChanged;
@@ -246,7 +248,7 @@
}
void setLayerStack(uint32_t id, int32_t layerStack) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -257,7 +259,7 @@
}
void setTouchableRegion(uint32_t id, Region region) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -272,7 +274,7 @@
}
void setInputInfo(uint32_t id, std::function<void(gui::WindowInfo&)> configureInput) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -291,7 +293,7 @@
void setTouchableRegionCrop(uint32_t id, Region region, uint32_t touchCropId,
bool replaceTouchableRegionWithCrop) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -309,7 +311,7 @@
}
void setBackgroundBlurRadius(uint32_t id, uint32_t backgroundBlurRadius) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -320,7 +322,7 @@
}
void setFrameRateSelectionPriority(uint32_t id, int32_t priority) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -332,7 +334,7 @@
void setFrameRate(uint32_t id, float frameRate, int8_t compatibility,
int8_t changeFrameRateStrategy) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -345,7 +347,7 @@
}
void setFrameRate(uint32_t id, Layer::FrameRate framerate) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -358,7 +360,7 @@
}
void setFrameRateCategory(uint32_t id, int8_t frameRateCategory) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -369,7 +371,7 @@
}
void setFrameRateSelectionStrategy(uint32_t id, int8_t strategy) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -381,7 +383,7 @@
}
void setDefaultFrameRateCompatibility(uint32_t id, int8_t defaultFrameRateCompatibility) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -394,7 +396,7 @@
}
void setRoundedCorners(uint32_t id, float radius) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -405,7 +407,7 @@
}
void setBuffer(uint32_t id, std::shared_ptr<renderengine::ExternalTexture> texture) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -438,7 +440,7 @@
}
void setBufferCrop(uint32_t id, const Rect& bufferCrop) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -449,7 +451,7 @@
}
void setDamageRegion(uint32_t id, const Region& damageRegion) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -460,7 +462,7 @@
}
void setDataspace(uint32_t id, ui::Dataspace dataspace) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -473,7 +475,7 @@
void setMatrix(uint32_t id, float dsdx, float dtdx, float dtdy, float dsdy) {
layer_state_t::matrix22_t matrix{dsdx, dtdx, dtdy, dsdy};
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -483,8 +485,31 @@
mLifecycleManager.applyTransactions(transactions);
}
+ void setClientDrawnCornerRadius(uint32_t id, float clientDrawnCornerRadius) {
+ std::vector<QueuedTransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what =
+ layer_state_t::eClientDrawnCornerRadiusChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.clientDrawnCornerRadius = clientDrawnCornerRadius;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void setClientDrawnShadowRadius(uint32_t id, float clientDrawnShadowRadius) {
+ std::vector<QueuedTransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eClientDrawnShadowsChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.clientDrawnShadowRadius = clientDrawnShadowRadius;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
void setShadowRadius(uint32_t id, float shadowRadius) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -495,7 +520,7 @@
}
void setTrustedOverlay(uint32_t id, gui::TrustedOverlay trustedOverlay) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -506,7 +531,7 @@
}
void setDropInputMode(uint32_t id, gui::DropInputMode dropInputMode) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -517,7 +542,7 @@
}
void setGameMode(uint32_t id, gui::GameMode gameMode) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().state.what = layer_state_t::eMetadataChanged;
@@ -529,7 +554,7 @@
}
void setEdgeExtensionEffect(uint32_t id, int edge) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
index 53a9062..f3d6dcc 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
@@ -584,7 +584,7 @@
auto layer = createLegacyAndFrontedEndLayer(1);
showLayer(1);
- setFrameRate(1, (33_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE,
+ setFrameRate(1, (33_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_AT_LEAST,
ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
setFrameRateCategory(1, 0);
@@ -623,7 +623,7 @@
auto layer = createLegacyAndFrontedEndLayer(1);
showLayer(1);
- setFrameRate(1, (33_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE,
+ setFrameRate(1, (33_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_AT_LEAST,
ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
setFrameRateCategory(1, 0);
@@ -662,7 +662,7 @@
auto layer = createLegacyAndFrontedEndLayer(1);
showLayer(1);
- setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE,
+ setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_AT_LEAST,
ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
EXPECT_EQ(1u, layerCount());
@@ -694,7 +694,7 @@
auto layer = createLegacyAndFrontedEndLayer(1);
showLayer(1);
- setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE,
+ setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_AT_LEAST,
ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
setFrameRateCategory(1, 0);
diff --git a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
index c7cc21c..976cecb 100644
--- a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
@@ -21,7 +21,7 @@
#include "FrontEnd/LayerLifecycleManager.h"
#include "LayerHierarchyTest.h"
-#include "TransactionState.h"
+#include "QueuedTransactionState.h"
using namespace android::surfaceflinger;
@@ -104,7 +104,7 @@
EXPECT_FALSE(managedLayers.front()->changes.test(RequestedLayerState::Changes::Z));
// apply transactions that do not affect the hierarchy
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().state.backgroundBlurRadius = 22;
@@ -297,7 +297,7 @@
layers.emplace_back(rootLayer(1));
lifecycleManager.addLayers(std::move(layers));
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().state.bgColor.a = 0.5;
@@ -326,7 +326,7 @@
layers.emplace_back(rootLayer(1));
lifecycleManager.addLayers(std::move(layers));
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().state.bgColor.a = 0.5;
@@ -360,7 +360,7 @@
layers.emplace_back(rootLayer(1));
lifecycleManager.addLayers(std::move(layers));
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().state.bgColor.a = 0.5;
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 4d322ef..ab8c733 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -28,6 +28,7 @@
#include "ui/GraphicTypes.h"
#include <com_android_graphics_libgui_flags.h>
+#include <cmath>
#define UPDATE_AND_VERIFY(BUILDER, ...) \
({ \
@@ -329,7 +330,7 @@
}
TEST_F(LayerSnapshotTest, UpdateMetadata) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().state.what = layer_state_t::eMetadataChanged;
@@ -374,7 +375,7 @@
TEST_F(LayerSnapshotTest, UpdateMetadataOfHiddenLayers) {
hideLayer(1);
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().state.what = layer_state_t::eMetadataChanged;
@@ -1425,6 +1426,93 @@
EXPECT_EQ(getSnapshot(1)->geomContentCrop, Rect(0, 0, 100, 100));
}
+TEST_F(LayerSnapshotTest, setCornerRadius) {
+ static constexpr float RADIUS = 123.f;
+ setRoundedCorners(1, RADIUS);
+ setCrop(1, Rect{1000, 1000});
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->roundedCorner.radius.x, RADIUS);
+}
+
+TEST_F(LayerSnapshotTest, ignoreCornerRadius) {
+ static constexpr float RADIUS = 123.f;
+ setClientDrawnCornerRadius(1, RADIUS);
+ setRoundedCorners(1, RADIUS);
+ setCrop(1, Rect{1000, 1000});
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_TRUE(getSnapshot({.id = 1})->roundedCorner.hasClientDrawnRadius());
+ EXPECT_EQ(getSnapshot({.id = 1})->roundedCorner.radius.x, 0.f);
+}
+
+TEST_F(LayerSnapshotTest, childInheritsParentScaledSettings) {
+ // ROOT
+ // ├── 1 (crop rect set to contain child layer)
+ // │ ├── 11
+ static constexpr float RADIUS = 123.f;
+
+ setRoundedCorners(1, RADIUS);
+ FloatRect parentCropRect(1, 1, 999, 999);
+ setCrop(1, parentCropRect);
+ // Rotate surface by 90
+ setMatrix(11, 0.f, -1.f, 1.f, 0.f);
+
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+
+ ui::Transform t = getSnapshot({.id = 11})->localTransform.inverse();
+
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 11})->roundedCorner.cropRect, t.transform(parentCropRect));
+ EXPECT_EQ(getSnapshot({.id = 11})->roundedCorner.radius.x, RADIUS * t.getScaleX());
+ EXPECT_EQ(getSnapshot({.id = 11})->roundedCorner.radius.y, RADIUS * t.getScaleY());
+ EXPECT_EQ(getSnapshot({.id = 11})->roundedCorner.requestedRadius.x, RADIUS * t.getScaleX());
+ EXPECT_EQ(getSnapshot({.id = 11})->roundedCorner.requestedRadius.y, RADIUS * t.getScaleY());
+}
+
+TEST_F(LayerSnapshotTest, childInheritsParentClientDrawnCornerRadius) {
+ // ROOT
+ // ├── 1 (crop rect set to contain child layers )
+ // │ ├── 11
+ // │ │ └── 111
+
+ static constexpr float RADIUS = 123.f;
+
+ setClientDrawnCornerRadius(1, RADIUS);
+ setRoundedCorners(1, RADIUS);
+ setCrop(1, Rect(1, 1, 999, 999));
+
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_TRUE(getSnapshot({.id = 1})->roundedCorner.hasClientDrawnRadius());
+ EXPECT_TRUE(getSnapshot({.id = 11})->roundedCorner.hasRoundedCorners());
+ EXPECT_EQ(getSnapshot({.id = 11})->roundedCorner.radius.x, RADIUS);
+}
+
+TEST_F(LayerSnapshotTest, childIgnoreCornerRadiusOverridesParent) {
+ // ROOT
+ // ├── 1 (crop rect set to contain child layers )
+ // │ ├── 11
+ // │ │ └── 111
+
+ static constexpr float RADIUS = 123.f;
+
+ setRoundedCorners(1, RADIUS);
+ setCrop(1, Rect(1, 1, 999, 999));
+
+ setClientDrawnCornerRadius(11, RADIUS);
+
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->roundedCorner.radius.x, RADIUS);
+ EXPECT_EQ(getSnapshot({.id = 11})->roundedCorner.radius.x, 0.f);
+ EXPECT_EQ(getSnapshot({.id = 111})->roundedCorner.radius.x, RADIUS);
+}
+
+TEST_F(LayerSnapshotTest, ignoreShadows) {
+ static constexpr float SHADOW_RADIUS = 123.f;
+ setClientDrawnShadowRadius(1, SHADOW_RADIUS);
+ setShadowRadius(1, SHADOW_RADIUS);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->shadowSettings.length, 0.f);
+}
+
TEST_F(LayerSnapshotTest, setShadowRadius) {
static constexpr float SHADOW_RADIUS = 123.f;
setShadowRadius(1, SHADOW_RADIUS);
@@ -1557,7 +1645,7 @@
setColor(3, {-1._hf, -1._hf, -1._hf});
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged;
@@ -1586,7 +1674,7 @@
setColor(3, {-1._hf, -1._hf, -1._hf});
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged;
@@ -2021,7 +2109,7 @@
EXPECT_FALSE(getSnapshot(1)->contentDirty);
}
TEST_F(LayerSnapshotTest, shouldUpdatePictureProfileHandle) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.back().layerId = 1;
@@ -2040,7 +2128,7 @@
TEST_F(LayerSnapshotTest, shouldUpdatePictureProfilePriorityFromAppContentPriority) {
{
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.back().layerId = 1;
@@ -2063,7 +2151,7 @@
2);
}
{
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.back().layerId = 1;
diff --git a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
index 5c25f34..d7f7bdb 100644
--- a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
@@ -39,6 +39,7 @@
using namespace std::chrono_literals;
using namespace testing;
using namespace android::power;
+using namespace ftl::flag_operators;
namespace android::adpf::impl {
@@ -54,6 +55,8 @@
void setTimingTestingMode(bool testinMode);
void allowReportActualToAcquireMutex();
bool sessionExists();
+ ftl::Flags<Workload> getCommittedWorkload() const;
+ ftl::Flags<Workload> getQueuedWorkload() const;
int64_t toNanos(Duration d);
struct GpuTestConfig {
@@ -315,6 +318,14 @@
return mPowerAdvisor->sTargetSafetyMargin;
}
+ftl::Flags<Workload> PowerAdvisorTest::getCommittedWorkload() const {
+ return mPowerAdvisor->mCommittedWorkload;
+}
+
+ftl::Flags<Workload> PowerAdvisorTest::getQueuedWorkload() const {
+ return ftl::Flags<Workload>{mPowerAdvisor->mQueuedWorkload.load()};
+}
+
namespace {
TEST_F(PowerAdvisorTest, hintSessionUseHwcDisplay) {
@@ -842,5 +853,32 @@
ASSERT_EQ(hint, SessionHint::CPU_LOAD_UP);
}
+TEST_F(PowerAdvisorTest, trackQueuedWorkloads) {
+ mPowerAdvisor->setQueuedWorkload(ftl::Flags<Workload>());
+ ASSERT_EQ(getQueuedWorkload(), ftl::Flags<Workload>());
+
+ // verify workloads are queued
+ mPowerAdvisor->setQueuedWorkload(ftl::Flags<Workload>(Workload::VISIBLE_REGION));
+ ASSERT_EQ(getQueuedWorkload(), ftl::Flags<Workload>(Workload::VISIBLE_REGION));
+
+ mPowerAdvisor->setQueuedWorkload(ftl::Flags<Workload>(Workload::EFFECTS));
+ ASSERT_EQ(getQueuedWorkload(), Workload::VISIBLE_REGION | Workload::EFFECTS);
+
+ // verify queued workloads are cleared after commit
+ mPowerAdvisor->setCommittedWorkload(ftl::Flags<Workload>());
+ ASSERT_EQ(getQueuedWorkload(), ftl::Flags<Workload>());
+}
+
+TEST_F(PowerAdvisorTest, trackCommittedWorkloads) {
+ // verify queued workloads are cleared after commit
+ mPowerAdvisor->setCommittedWorkload(Workload::SCREENSHOT | Workload::VISIBLE_REGION);
+ ASSERT_EQ(getCommittedWorkload(), Workload::SCREENSHOT | Workload::VISIBLE_REGION);
+
+ // on composite, verify we update the committed workload so we track workload increases for the
+ // next frame accurately
+ mPowerAdvisor->setCompositedWorkload(Workload::VISIBLE_REGION | Workload::DISPLAY_CHANGES);
+ ASSERT_EQ(getCommittedWorkload(), Workload::VISIBLE_REGION | Workload::DISPLAY_CHANGES);
+}
+
} // namespace
} // namespace android::adpf::impl
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index bd1382e..2353ef8 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -184,8 +184,8 @@
}
void setupComposer(std::unique_ptr<Hwc2::Composer> composer) {
- mFlinger->mCompositionEngine->setHwComposer(
- std::make_unique<impl::HWComposer>(std::move(composer)));
+ mFlinger->mHWComposer = std::make_unique<impl::HWComposer>(std::move(composer));
+ mFlinger->mCompositionEngine->setHwComposer(mFlinger->mHWComposer.get());
mFlinger->mDisplayModeController.setHwComposer(
&mFlinger->mCompositionEngine->getHwComposer());
}
@@ -473,7 +473,7 @@
auto displayState = std::optional{display->getCompositionDisplay()->getState()};
auto layers = getLayerSnapshotsFn();
- return mFlinger->renderScreenImpl(renderArea.get(), buffer, regionSampling,
+ return mFlinger->renderScreenImpl(std::move(renderArea), buffer, regionSampling,
false /* grayscale */, false /* isProtected */,
captureResults, displayState, layers);
}
@@ -514,7 +514,7 @@
mergedTransactionIds);
}
- auto setTransactionStateInternal(TransactionState& transaction) {
+ auto setTransactionStateInternal(QueuedTransactionState& transaction) {
return FTL_FAKE_GUARD(kMainThreadContext,
mFlinger->mTransactionHandler.queueTransaction(
std::move(transaction)));
@@ -771,7 +771,8 @@
mutableCurrentState().displays.clear();
mutableDrawingState().displays.clear();
mFlinger->mScheduler.reset();
- mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
+ mFlinger->mHWComposer = std::unique_ptr<HWComposer>();
+ mFlinger->mCompositionEngine->setHwComposer(mFlinger->mHWComposer.get());
mFlinger->mRenderEngine = std::unique_ptr<renderengine::RenderEngine>();
mFlinger->mCompositionEngine->setRenderEngine(mFlinger->mRenderEngine.get());
mFlinger->mTransactionTracing.reset();
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 1e8cd0a..69dfcc4 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -33,8 +33,8 @@
#include <vector>
#include "FrontEnd/TransactionHandler.h"
+#include "QueuedTransactionState.h"
#include "TestableSurfaceFlinger.h"
-#include "TransactionState.h"
#include <com_android_graphics_surfaceflinger_flags.h>
@@ -84,7 +84,7 @@
static_assert(0xffffffffffffffff == static_cast<uint64_t>(-1));
};
- void checkEqual(TransactionInfo info, TransactionState state) {
+ void checkEqual(TransactionInfo info, QueuedTransactionState state) {
EXPECT_EQ(0u, info.states.size());
EXPECT_EQ(0u, state.states.size());
@@ -318,7 +318,7 @@
auto applyToken2 = sp<BBinder>::make();
// Transaction 1 has a buffer with an unfired fence. It should not be ready to be applied.
- TransactionState transaction1;
+ QueuedTransactionState transaction1;
transaction1.applyToken = applyToken1;
transaction1.id = 42069;
transaction1.states.emplace_back();
@@ -340,7 +340,7 @@
transaction1.isAutoTimestamp = true;
// Transaction 2 should be ready to be applied.
- TransactionState transaction2;
+ QueuedTransactionState transaction2;
transaction2.applyToken = applyToken2;
transaction2.id = 2;
transaction2.isAutoTimestamp = true;
@@ -446,15 +446,15 @@
resolvedStates.emplace_back(resolvedState);
}
- TransactionState transactionState(transaction.frameTimelineInfo, resolvedStates,
- transaction.displays, transaction.flags,
- transaction.applyToken,
- transaction.inputWindowCommands,
- transaction.desiredPresentTime,
- transaction.isAutoTimestamp, {}, systemTime(),
- mHasListenerCallbacks, mCallbacks, getpid(),
- static_cast<int>(getuid()), transaction.id,
- transaction.mergedTransactionIds);
+ QueuedTransactionState transactionState(transaction.frameTimelineInfo, resolvedStates,
+ transaction.displays, transaction.flags,
+ transaction.applyToken,
+ transaction.inputWindowCommands,
+ transaction.desiredPresentTime,
+ transaction.isAutoTimestamp, {}, systemTime(),
+ mHasListenerCallbacks, mCallbacks, getpid(),
+ static_cast<int>(getuid()), transaction.id,
+ transaction.mergedTransactionIds);
mFlinger.setTransactionStateInternal(transactionState);
}
mFlinger.flushTransactionQueues();
@@ -955,12 +955,12 @@
TEST(TransactionHandlerTest, QueueTransaction) {
TransactionHandler handler;
- TransactionState transaction;
+ QueuedTransactionState transaction;
transaction.applyToken = sp<BBinder>::make();
transaction.id = 42;
handler.queueTransaction(std::move(transaction));
handler.collectTransactions();
- std::vector<TransactionState> transactionsReadyToBeApplied = handler.flushTransactions();
+ std::vector<QueuedTransactionState> transactionsReadyToBeApplied = handler.flushTransactions();
EXPECT_EQ(transactionsReadyToBeApplied.size(), 1u);
EXPECT_EQ(transactionsReadyToBeApplied.front().id, 42u);
diff --git a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
index af02330..d3eec5c 100644
--- a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
@@ -30,7 +30,7 @@
TEST(TransactionProtoParserTest, parse) {
const sp<IBinder> displayHandle = sp<BBinder>::make();
- TransactionState t1;
+ QueuedTransactionState t1;
t1.originPid = 1;
t1.originUid = 2;
t1.frameTimelineInfo.vsyncId = 3;
@@ -86,7 +86,7 @@
TransactionProtoParser parser(std::make_unique<TestMapper>(displayHandle));
perfetto::protos::TransactionState proto = parser.toProto(t1);
- TransactionState t2 = parser.fromProto(proto);
+ QueuedTransactionState t2 = parser.fromProto(proto);
ASSERT_EQ(t1.originPid, t2.originPid);
ASSERT_EQ(t1.originUid, t2.originUid);
diff --git a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
index f8f08c7..036d8c4 100644
--- a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
@@ -49,19 +49,19 @@
void queueAndCommitTransaction(int64_t vsyncId) {
frontend::Update update;
- TransactionState transaction;
+ QueuedTransactionState transaction;
transaction.id = static_cast<uint64_t>(vsyncId * 3);
transaction.originUid = 1;
transaction.originPid = 2;
mTracing.addQueuedTransaction(transaction);
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
update.transactions.emplace_back(transaction);
mTracing.addCommittedTransactions(vsyncId, 0, update, {}, false);
flush();
}
void verifyEntry(const perfetto::protos::TransactionTraceEntry& actualProto,
- const std::vector<TransactionState>& expectedTransactions,
+ const std::vector<QueuedTransactionState>& expectedTransactions,
int64_t expectedVsyncId) {
EXPECT_EQ(actualProto.vsync_id(), expectedVsyncId);
ASSERT_EQ(actualProto.transactions().size(),
@@ -92,10 +92,10 @@
};
TEST_F(TransactionTracingTest, addTransactions) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.reserve(100);
for (uint64_t i = 0; i < 100; i++) {
- TransactionState transaction;
+ QueuedTransactionState transaction;
transaction.id = i;
transaction.originPid = static_cast<int32_t>(i);
transaction.mergedTransactionIds = std::vector<uint64_t>{i + 100, i + 102};
@@ -108,13 +108,13 @@
int64_t firstTransactionSetVsyncId = 42;
frontend::Update firstUpdate;
firstUpdate.transactions =
- std::vector<TransactionState>(transactions.begin() + 50, transactions.end());
+ std::vector<QueuedTransactionState>(transactions.begin() + 50, transactions.end());
mTracing.addCommittedTransactions(firstTransactionSetVsyncId, 0, firstUpdate, {}, false);
int64_t secondTransactionSetVsyncId = 43;
frontend::Update secondUpdate;
secondUpdate.transactions =
- std::vector<TransactionState>(transactions.begin(), transactions.begin() + 50);
+ std::vector<QueuedTransactionState>(transactions.begin(), transactions.begin() + 50);
mTracing.addCommittedTransactions(secondTransactionSetVsyncId, 0, secondUpdate, {}, false);
flush();
@@ -140,7 +140,7 @@
getLayerCreationArgs(mChildLayerId, mParentLayerId,
/*layerIdToMirror=*/UNASSIGNED_LAYER_ID, /*flags=*/456,
/*addToRoot=*/true));
- TransactionState transaction;
+ QueuedTransactionState transaction;
transaction.id = 50;
ResolvedComposerState layerState;
layerState.layerId = mParentLayerId;
@@ -164,7 +164,7 @@
// add transactions that modify the layer state further so we can test that layer state
// gets merged
{
- TransactionState transaction;
+ QueuedTransactionState transaction;
transaction.id = 51;
ResolvedComposerState layerState;
layerState.layerId = mParentLayerId;
@@ -278,7 +278,7 @@
/*layerIdToMirror=*/mLayerId, /*flags=*/0,
/*addToRoot=*/false));
- TransactionState transaction;
+ QueuedTransactionState transaction;
transaction.id = 50;
ResolvedComposerState layerState;
layerState.layerId = mLayerId;
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 2bf66ac..7319f1e 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -193,6 +193,8 @@
MOCK_METHOD(Error, getLuts,
(Display, const std::vector<sp<GraphicBuffer>>&,
std::vector<aidl::android::hardware::graphics::composer3::Luts>*));
+ MOCK_METHOD4(getLayerPresentFences,
+ Error(Display, std::vector<Layer>*, std::vector<int>*, std::vector<int64_t>*));
};
} // namespace Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerAdvisor.h
index fd55597..5abee16 100644
--- a/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerAdvisor.h
+++ b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerAdvisor.h
@@ -65,6 +65,10 @@
MOCK_METHOD(void, setTotalFrameTargetWorkDuration, (Duration targetDuration), (override));
MOCK_METHOD(std::shared_ptr<SessionManager>, getSessionManager, (), (override));
MOCK_METHOD(sp<IBinder>, getOrCreateSessionManagerForBinder, (uid_t uid), (override));
+ MOCK_METHOD(void, setQueuedWorkload, (ftl::Flags<Workload> workload), (override));
+ MOCK_METHOD(void, setScreenshotWorkload, (), (override));
+ MOCK_METHOD(void, setCommittedWorkload, (ftl::Flags<Workload> workload), (override));
+ MOCK_METHOD(void, setCompositedWorkload, (ftl::Flags<Workload> workload), (override));
};
} // namespace android::adpf::mock
diff --git a/vulkan/vkjson/vkjson.cc b/vulkan/vkjson/vkjson.cc
index 3cb9405..8c0cce2 100644
--- a/vulkan/vkjson/vkjson.cc
+++ b/vulkan/vkjson/vkjson.cc
@@ -38,6 +38,12 @@
namespace {
+/*
+ * Annotation to tell clang that we intend to fall through from one case to
+ * another in a switch. Sourced from android-base/macros.h.
+ */
+#define FALLTHROUGH_INTENDED [[clang::fallthrough]]
+
inline bool IsIntegral(double value) {
#if defined(ANDROID)
// Android NDK doesn't provide std::trunc yet
diff --git a/vulkan/vkjson/vkjson.h b/vulkan/vkjson/vkjson.h
index 28de680..5818c73 100644
--- a/vulkan/vkjson/vkjson.h
+++ b/vulkan/vkjson/vkjson.h
@@ -33,12 +33,6 @@
#undef max
#endif
-/*
- * Annotation to tell clang that we intend to fall through from one case to
- * another in a switch. Sourced from android-base/macros.h.
- */
-#define FALLTHROUGH_INTENDED [[clang::fallthrough]]
-
struct VkJsonLayer {
VkLayerProperties properties;
std::vector<VkExtensionProperties> extensions;