diff options
71 files changed, 2080 insertions, 1108 deletions
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp index 372008e63b..94590876b7 100644 --- a/cmds/dumpstate/Android.bp +++ b/cmds/dumpstate/Android.bp @@ -134,6 +134,7 @@ cc_binary { "main.cpp", ], required: [ + "alloctop", "atrace", "bugreport_procdump", "default_screenshot", diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 4758607122..fcc6108f5f 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -1264,6 +1264,15 @@ static void DumpIpAddrAndRules() { 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"}); + } +} + static Dumpstate::RunStatus RunDumpsysTextByPriority(const std::string& title, int priority, std::chrono::milliseconds timeout, std::chrono::milliseconds service_timeout) { @@ -1773,6 +1782,8 @@ Dumpstate::RunStatus Dumpstate::dumpstate() { DoKmsg(); + DumpKernelMemoryAllocations(); + DumpShutdownCheckpoints(); DumpIpAddrAndRules(); diff --git a/data/etc/Android.bp b/data/etc/Android.bp index 64ef8278e4..cd8ca89de5 100644 --- a/data/etc/Android.bp +++ b/data/etc/Android.bp @@ -305,6 +305,12 @@ prebuilt_etc { } 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 9ea6549e8a..52dbb61ac5 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 @@ typedef struct APerformanceHintManager APerformanceHintManager; * 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. - * - * The caller may reuse this object and modify the settings in it - * to create additional sessions. + * 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. */ typedef struct ASessionCreationConfig ASessionCreationConfig; @@ -181,27 +185,43 @@ APerformanceHintSession* _Nullable APerformanceHint_createSession( 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 @@ int64_t APerformanceHint_getPreferredUpdateRateNanos( * 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 @@ int APerformanceHint_updateTargetWorkDuration( * @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 @@ void APerformanceHint_closeSession( * 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 @@ int APerformanceHint_reportActualWorkDuration2( 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 @@ int APerformanceHint_notifyWorkloadSpike( * * @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 @@ APerformanceHintSession* _Nonnull APerformanceHint_borrowSessionFromJava( ASessionCreationConfig* _Nonnull ASessionCreationConfig_create() __INTRODUCED_IN(36); - /** * Destroys a {@link ASessionCreationConfig} and frees all * resources associated with it. @@ -526,11 +626,8 @@ void ASessionCreationConfig_release( * @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 @@ int ASessionCreationConfig_setTids( * * @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 @@ int ASessionCreationConfig_setTargetWorkDurationNanos( * @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 @@ int ASessionCreationConfig_setPreferPowerEfficiency( * 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 @@ int ASessionCreationConfig_setGraphicsPipeline( * @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/input/AccelerationCurve.h b/include/input/AccelerationCurve.h index 0cf648a2f7..8a4a5d429b 100644 --- a/include/input/AccelerationCurve.h +++ b/include/input/AccelerationCurve.h @@ -46,4 +46,15 @@ struct AccelerationCurveSegment { 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/performance_hint_private.h b/include/private/performance_hint_private.h index e3f98badbe..a468313341 100644 --- a/include/private/performance_hint_private.h +++ b/include/private/performance_hint_private.h @@ -125,8 +125,10 @@ APerformanceHintSession* APerformanceHint_createSessionInternal(APerformanceHint /** * 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/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h index 9ef4e694dd..f7465e2c1a 100644 --- a/libs/binder/include/binder/IPCThreadState.h +++ b/libs/binder/include/binder/IPCThreadState.h @@ -64,7 +64,10 @@ public: * 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/Parcel.h b/libs/binder/include/binder/Parcel.h index b4efa0af6d..69a73d415e 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h @@ -388,6 +388,11 @@ public: 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 +406,6 @@ public: // 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. @@ -632,6 +635,11 @@ public: 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 +693,7 @@ private: // 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; diff --git a/libs/binder/include/binder/SafeInterface.h b/libs/binder/include/binder/SafeInterface.h index 0b4f196b8f..bcbd14f9d4 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/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h index bd46c473b4..d69d318a28 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 @@ binder_status_t AIBinder_unlinkToDeath(AIBinder* binder, AIBinder_DeathRecipient * 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 66be94f5b5..fb92e05de5 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 8a06274e9c..c0cac830d7 100644 --- a/libs/binder/rust/src/state.rs +++ b/libs/binder/rust/src/state.rs @@ -101,7 +101,10 @@ impl ThreadState { /// 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 1a58d6b44d..288c54bab9 100644 --- a/libs/binder/rust/src/system_only.rs +++ b/libs/binder/rust/src/system_only.rs @@ -24,21 +24,16 @@ 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::{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 { diff --git a/libs/binder/tests/binderSafeInterfaceTest.cpp b/libs/binder/tests/binderSafeInterfaceTest.cpp index 849dc7c4d5..7d1556e7d2 100644 --- a/libs/binder/tests/binderSafeInterfaceTest.cpp +++ b/libs/binder/tests/binderSafeInterfaceTest.cpp @@ -789,7 +789,7 @@ TEST_F(SafeInterfaceTest, TestCallMeBack) { 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; } diff --git a/libs/binder/tests/binderUtilsHostTest.cpp b/libs/binder/tests/binderUtilsHostTest.cpp index a62ad96be1..ca70b6644b 100644 --- a/libs/binder/tests/binderUtilsHostTest.cpp +++ b/libs/binder/tests/binderUtilsHostTest.cpp @@ -89,8 +89,8 @@ TEST(UtilsHost, ExecuteLongRunning2) { } // ~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 07f0143767..401c2749ef 100644 --- a/libs/binder/tests/parcel_fuzzer/binder.cpp +++ b/libs/binder/tests/parcel_fuzzer/binder.cpp @@ -121,6 +121,11 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { 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; diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp index dfd178a450..61b9612ba5 100644 --- a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp +++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp @@ -40,6 +40,13 @@ void fillRandomParcel(Parcel* outputParcel, FuzzedDataProvider&& provider, 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 @@ void fillRandomParcel(Parcel* outputParcel, FuzzedDataProvider&& provider, } 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/input/AccelerationCurve.cpp b/libs/input/AccelerationCurve.cpp index 0a92a71596..0b47f3e6e7 100644 --- a/libs/input/AccelerationCurve.cpp +++ b/libs/input/AccelerationCurve.cpp @@ -40,6 +40,18 @@ static_assert(kSegments.back().maxPointerSpeedMmPerS == std::numeric_limits<doub 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 @@ std::vector<AccelerationCurveSegment> createAccelerationCurveForPointerSensitivi 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 6a55726db1..56ccaab9ad 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -327,8 +327,8 @@ std::unique_ptr<InputChannel> InputChannel::create(const std::string& name, 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/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl index 31592cd6e3..6ce3fba477 100644 --- a/libs/input/android/os/IInputConstants.aidl +++ b/libs/input/android/os/IInputConstants.aidl @@ -76,6 +76,9 @@ interface IInputConstants /* 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/input_flags.aconfig b/libs/input/input_flags.aconfig index fd7704815f..6cdd249b9e 100644 --- a/libs/input/input_flags.aconfig +++ b/libs/input/input_flags.aconfig @@ -109,13 +109,6 @@ flag { } 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." diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index 0167c433cb..85a37fe04a 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -58,11 +58,19 @@ cc_test { "-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 @@ cc_test { "libstatssocket_lazy", ], }, - host: { - sanitize: { - address: true, - }, - }, }, native_coverage: false, } diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp index 7e8ccef18e..14d08eea74 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaRenderEngine.cpp @@ -544,9 +544,18 @@ sk_sp<SkShader> SkiaRenderEngine::createRuntimeEffectShader( } 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 37b69f6590..7331bbc418 100644 --- a/libs/renderengine/skia/VulkanInterface.cpp +++ b/libs/renderengine/skia/VulkanInterface.cpp @@ -204,10 +204,10 @@ static skgpu::VulkanGetProc sGetProc = [](const char* proc_name, 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/filters/KawaseBlurDualFilter.cpp b/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp index da47aae15b..750e08fa55 100644 --- a/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp +++ b/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp @@ -74,13 +74,6 @@ KawaseBlurDualFilter::KawaseBlurDualFilter() : BlurFilter() { 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,17 @@ sk_sp<SkImage> KawaseBlurDualFilter::generate(SkiaGpuContext* context, const uin 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 = static_cast<float>(blurRect.width() / scale); + const auto newH = static_cast<float>(blurRect.height() / scale); + return context->createRenderTarget(input->imageInfo().makeWH(newW, newH)); + }; + // 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 8bd0d0f33d..1ef83a4706 100644 --- a/libs/tracing_perfetto/Android.bp +++ b/libs/tracing_perfetto/Android.bp @@ -21,7 +21,7 @@ package { default_applicable_licenses: ["frameworks_native_license"], } -cc_library_shared { +cc_library { name: "libtracing_perfetto", export_include_dirs: [ "include", @@ -37,6 +37,7 @@ cc_library_shared { srcs: [ "tracing_perfetto.cpp", "tracing_perfetto_internal.cpp", + "tracing_sdk.cpp", ], shared_libs: [ @@ -45,6 +46,10 @@ cc_library_shared { "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 0000000000..4a6e849567 --- /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 d203467783..0dab517b7f 100644 --- a/libs/tracing_perfetto/tests/Android.bp +++ b/libs/tracing_perfetto/tests/Android.bp @@ -21,12 +21,44 @@ package { 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 @@ cc_test { ], 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 0000000000..b2630e1829 --- /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 e9fee2e6cf..b21a090677 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 8c4d4a8925..af61bc2192 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 @@ TracingSession TracingSession::Builder::Build() { } { - 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 @@ TracingSession TracingSession::Builder::Build() { } } - 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 @@ std::vector<uint8_t> TracingSession::ReadBlocking() { } // 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 8edb4143ee..0000000000 --- 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 c35e078b02..4b7021393f 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 traceBegin(uint64_t category, const char* name) { 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 @@ void traceFormatBegin(uint64_t category, const char* fmt, ...) { vsnprintf(buf, BUFFER_SIZE, fmt, ap); va_end(ap); - if (preferAtrace) { atrace_begin(category, buf); } else if (preferPerfetto) { @@ -99,26 +101,28 @@ void traceAsyncEnd(uint64_t category, const char* name, int32_t cookie) { } 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 traceInstant(uint64_t category, const char* name) { 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 traceFormatInstant(uint64_t category, const char* fmt, ...) { } 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 traceCounter(uint64_t category, const char* name, int64_t value) { } 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 0000000000..02e8d10aa4 --- /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/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index cd4ed5c55b..0b1e849bc7 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" @@ -924,6 +925,23 @@ Error<EnumErrorWrapper<InputEventInjectionResult>> injectionError(InputEventInje 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& displayTransform, + 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.displayTransform = displayTransform; + inputTarget.firstDownTimeInTarget = firstDownTimeInTarget; + return inputTarget; +} + } // namespace // --- InputDispatcher --- @@ -1128,7 +1146,7 @@ std::chrono::nanoseconds InputDispatcher::getDispatchingTimeoutLocked( 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 +1351,7 @@ bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEnt 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 +1364,8 @@ bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEnt // 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 +1471,19 @@ void InputDispatcher::addRecentEventLocked(std::shared_ptr<const EventEntry> ent } } -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 +1498,7 @@ std::vector<InputTarget> InputDispatcher::findOutsideTargetsLocked( } // 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,14 +1518,16 @@ std::vector<InputTarget> InputDispatcher::findOutsideTargetsLocked( 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))) { + if (!windowAcceptsTouchAt(info, displayId, x, y, isStylus, + getDisplayTransform(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 @@ -1517,7 +1538,7 @@ std::vector<sp<WindowInfoHandle>> InputDispatcher::findTouchedSpyWindowsAtLocked // 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)) { + if (!windowHasTouchingPointers(windowHandle, deviceId, touchStatesByDisplay)) { continue; } // If it already has pointers down for this device, then give it this pointer, too. @@ -1828,7 +1849,7 @@ void InputDispatcher::dispatchPointerCaptureChangedLocked( 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 +2237,7 @@ void InputDispatcher::cancelEventsForAnrLocked(const std::shared_ptr<Connection> 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 @@ -2472,7 +2493,7 @@ InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const Motio // 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); @@ -2518,7 +2539,8 @@ InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const Motio } 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 +2677,7 @@ InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const Motio 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) { @@ -2779,7 +2801,7 @@ InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const Motio 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 +2901,8 @@ void InputDispatcher::finishDragAndDrop(ui::LogicalDisplayId displayId, float x, 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 +2957,8 @@ void InputDispatcher::addDragEventLocked(const MotionEntry& entry) { 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 +2993,6 @@ void InputDispatcher::addDragEventLocked(const MotionEntry& entry) { } } -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 +3007,17 @@ void InputDispatcher::addWindowTargetLocked(const sp<WindowInfoHandle>& windowHa 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.getDisplayTransform( + windowHandle->getInfo()->displayId), + firstDownTimeInTarget)); it = inputTargets.end() - 1; } @@ -3060,13 +3062,17 @@ void InputDispatcher::addPointerWindowTargetLocked( 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.getDisplayTransform( + windowHandle->getInfo()->displayId), + firstDownTimeInTarget)); it = inputTargets.end() - 1; } @@ -3099,9 +3105,7 @@ void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& 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.displayTransform = mWindowInfos.getDisplayTransform(displayId); target.setDefaultPointerTransform(target.displayTransform); inputTargets.push_back(target); } @@ -3164,7 +3168,8 @@ InputDispatcher::TouchOcclusionInfo InputDispatcher::computeTouchOcclusionInfoLo 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 +3181,8 @@ InputDispatcher::TouchOcclusionInfo InputDispatcher::computeTouchOcclusionInfoLo } 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 +3254,16 @@ bool InputDispatcher::isTouchTrustedLocked(const TouchOcclusionInfo& occlusionIn 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 +3272,8 @@ bool InputDispatcher::isWindowObscuredAtPointLocked(const sp<WindowInfoHandle>& 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) { @@ -4118,7 +4127,8 @@ int InputDispatcher::handleReceiveCallback(int events, sp<IBinder> connectionTok } 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", @@ -4153,11 +4163,11 @@ void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked( // 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 +4291,10 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( 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); - } + const ui::Transform displayTransform = + mWindowInfos.getDisplayTransform(motionEntry.displayId); + targets.back().displayTransform = displayTransform; + targets.back().setDefaultPointerTransform(displayTransform); } logOutboundMotionDetails("cancel - ", motionEntry); break; @@ -4340,7 +4349,7 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( } 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 +4376,10 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( 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); - } + const ui::Transform displayTransform = + mWindowInfos.getDisplayTransform(motionEntry.displayId); + targets.back().displayTransform = displayTransform; + targets.back().setDefaultPointerTransform(displayTransform); } logOutboundMotionDetails("down - ", motionEntry); break; @@ -4642,10 +4650,7 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs& args) { } 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(); @@ -5149,9 +5154,8 @@ void InputDispatcher::transformMotionEntryForInjectionLocked( 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 +5188,7 @@ void InputDispatcher::decrementPendingForegroundDispatches(const EventEntry& ent } } -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 +5207,7 @@ sp<WindowInfoHandle> InputDispatcher::getWindowHandleLocked( } // 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 +5215,7 @@ sp<WindowInfoHandle> InputDispatcher::getWindowHandleLocked( 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 +5227,98 @@ sp<WindowInfoHandle> InputDispatcher::getWindowHandleLocked( 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); +} + +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::getTransformLocked(ui::LogicalDisplayId displayId) const { +ui::Transform InputDispatcher::DispatcherWindowInfo::getDisplayTransform( + ui::LogicalDisplayId displayId) const { auto displayInfoIt = mDisplayInfos.find(displayId); return displayInfoIt != mDisplayInfos.end() ? displayInfoIt->second.transform : kIdentityTransform; } +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 +5386,14 @@ void InputDispatcher::updateWindowHandlesForDisplayLocked( 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 +5432,7 @@ void InputDispatcher::updateWindowHandlesForDisplayLocked( } } - // Insert or replace - mWindowHandlesByDisplay[displayId] = newHandles; + mWindowInfos.setWindowHandlesForDisplay(displayId, std::move(newHandles)); } /** @@ -5412,12 +5482,14 @@ void InputDispatcher::setInputWindowsLocked( } // 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 +5501,7 @@ void InputDispatcher::setInputWindowsLocked( 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 +5545,8 @@ void InputDispatcher::setInputWindowsLocked( [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 +5568,7 @@ void InputDispatcher::setInputWindowsLocked( // 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 +5645,7 @@ void InputDispatcher::setFocusedDisplay(ui::LogicalDisplayId displayId) { 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 +5784,7 @@ bool InputDispatcher::focusedWindowIsOwnedByLocked(gui::Pid pid, gui::Uid uid) { if (focusedToken == nullptr) { return false; } - sp<WindowInfoHandle> windowHandle = getWindowHandleLocked(focusedToken); + sp<WindowInfoHandle> windowHandle = mWindowInfos.findWindowHandle(focusedToken); return isWindowOwnedBy(windowHandle, pid, uid); } @@ -5719,7 +5792,7 @@ bool InputDispatcher::recentWindowsAreOwnedByLocked(gui::Pid pid, gui::Uid uid) 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 +5807,12 @@ void InputDispatcher::setMaximumObscuringOpacityForTouch(float opacity) { 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,15 +5821,25 @@ InputDispatcher::findTouchStateWindowAndDisplayLocked(const sp<IBinder>& token) 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); -} - -bool InputDispatcher::windowHasTouchingPointersLocked(const sp<WindowInfoHandle>& windowHandle, - DeviceId deviceId) const { +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)); + + return std::make_tuple(const_cast<TouchState*>(constTouchState), + const_cast<TouchedWindow*>(constTouchedWindow), displayId); +} + +bool InputDispatcher::windowHasTouchingPointers( + const sp<WindowInfoHandle>& windowHandle, DeviceId deviceId, + const std::unordered_map<ui::LogicalDisplayId, TouchState>& touchStatesByDisplay) { const auto& [touchState, touchedWindow, _] = - findTouchStateWindowAndDisplayLocked(windowHandle->getToken()); + findTouchStateWindowAndDisplay(windowHandle->getToken(), touchStatesByDisplay); if (touchState == nullptr) { // No touching pointers at all return false; @@ -5775,7 +5860,8 @@ bool InputDispatcher::transferTouchGesture(const sp<IBinder>& fromToken, const s 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 +5876,8 @@ bool InputDispatcher::transferTouchGesture(const sp<IBinder>& fromToken, const s 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 +5953,11 @@ bool InputDispatcher::transferTouchGesture(const sp<IBinder>& fromToken, const s * 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 +5985,15 @@ bool InputDispatcher::transferTouchOnDisplay(const sp<IBinder>& destChannelToken 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 +6045,7 @@ std::string InputDispatcher::dumpPointerCaptureStateLocked() const { 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 +6094,7 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) const { 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) { @@ -6301,7 +6366,8 @@ status_t InputDispatcher::pilferPointersLocked(const sp<IBinder>& token) { 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 +6419,7 @@ void InputDispatcher::requestPointerCapture(const sp<IBinder>& windowToken, bool { // 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 +6559,18 @@ void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime, } 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.getDisplayTransform( + windowHandle->getInfo() + ->displayId), + downTime)); } } releaseDispatchEntry(std::move(dispatchEntry)); @@ -6557,7 +6624,7 @@ void InputDispatcher::onAnrLocked(const std::shared_ptr<Connection>& connection) 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 +6675,27 @@ void InputDispatcher::updateLastAnrStateLocked(const std::string& windowLabel, 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 +6735,7 @@ void InputDispatcher::processConnectionUnresponsiveLocked(const Connection& conn // 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 +6753,7 @@ void InputDispatcher::processConnectionResponsiveLocked(const Connection& connec 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 +6818,7 @@ std::unique_ptr<const KeyEntry> InputDispatcher::afterKeyEventLockedInterruptabl // 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 +6904,7 @@ std::unique_ptr<const KeyEntry> InputDispatcher::afterKeyEventLockedInterruptabl } } - 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 +7046,7 @@ void InputDispatcher::setFocusedWindow(const FocusRequest& request) { 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 +7064,7 @@ void InputDispatcher::onFocusChangedLocked( 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"; } @@ -7112,14 +7182,10 @@ void InputDispatcher::onWindowInfosChanged(const gui::WindowInfosUpdate& update) // 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 +7332,7 @@ void InputDispatcher::transferWallpaperTouch( 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 fade8535c3..fd550dd396 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -252,19 +252,13 @@ private: // 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,62 @@ private: }; 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; + + // 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 +582,6 @@ private: 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, @@ -696,13 +724,19 @@ private: 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); - - 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<TouchState*, TouchedWindow*, ui::LogicalDisplayId> + findTouchStateWindowAndDisplay( + const sp<IBinder>& token, + std::unordered_map<ui::LogicalDisplayId, TouchState>& touchStatesByDisplay); + + static std::tuple<const TouchState*, const TouchedWindow*, ui::LogicalDisplayId> + findTouchStateWindowAndDisplay( + const sp<IBinder>& token, + const std::unordered_map<ui::LogicalDisplayId, TouchState>& touchStatesByDisplay); + + static bool windowHasTouchingPointers( + const sp<android::gui::WindowInfoHandle>& windowHandle, DeviceId deviceId, + const std::unordered_map<ui::LogicalDisplayId, TouchState>& touchStatesByDisplay); // Statistics gathering. nsecs_t mLastStatisticPushTime = 0; diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h index b885ba17f3..5dcd98419d 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 @@ public: 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/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index 4d6b6c7fd5..404a509247 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -139,9 +139,17 @@ struct InputReaderConfiguration { // 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. + // 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> displaysWithMousePointerAccelerationDisabled; + // 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). // @@ -275,11 +283,15 @@ struct InputReaderConfiguration { defaultPointerDisplayId(ui::LogicalDisplayId::DEFAULT), mousePointerSpeed(0), displaysWithMousePointerAccelerationDisabled(), + 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/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index b33659cae1..e9f17e796f 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -481,15 +481,21 @@ void CursorInputMapper::configureOnChangePointerSpeed(const InputReaderConfigura 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.displaysWithMousePointerAccelerationDisabled.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/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp index 6bd949a09d..827076a2cf 100644 --- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp +++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp @@ -81,7 +81,6 @@ GestureConverter::GestureConverter(InputReaderContext& readerContext, 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 @@ std::list<NotifyArgs> GestureConverter::handleFling(nsecs_t when, nsecs_t readTi 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 8d92ead80b..be76b61ce6 100644 --- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h +++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h @@ -104,7 +104,6 @@ private: 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 d4e8fdfdc5..a31c4e96d7 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 @@ TEST_F(CursorInputMapperUnitTest, ConfigureDisplayIdNoAssociatedViewport) { 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); diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp b/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp index db68d8a14a..c4257a83c3 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::interceptKeyBeforeQueueing(const KeyEvent& input 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 a9e39d1630..c387eacb51 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 @@ private: 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/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp index fe40a5eb4d..8fa439d633 100644 --- a/services/inputflinger/tests/GestureConverter_test.cpp +++ b/services/inputflinger/tests/GestureConverter_test.cpp @@ -1297,7 +1297,6 @@ TEST_F(GestureConverterTest, FlingTapDown) { 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 3413caa1f0..fb67cebf29 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -117,8 +117,12 @@ static constexpr gui::Uid SECONDARY_WINDOW_UID{1012}; // 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; @@ -14684,4 +14688,219 @@ TEST_F(InputDispatcherTest, FocusedDisplayChangeIsNotified) { 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 9d2256f52f..0906536e32 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -2474,10 +2474,10 @@ TEST_F(ExternalStylusIntegrationTest, FusedExternalStylusPressureNotReported) { 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)); diff --git a/services/inputflinger/tests/TestEventMatchers.h b/services/inputflinger/tests/TestEventMatchers.h index 7078e49343..7fb8895719 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 @@ inline std::string pointFToString(const PointF& p) { return std::string("(") + std::to_string(p.x) + ", " + std::to_string(p.y) + ")"; } +} // namespace + /// Source class WithSourceMatcher { public: @@ -706,8 +719,8 @@ public: } 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/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index 99a67e4b26..e63a14bb5e 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -629,7 +629,7 @@ Error Display::getRequestedLuts(LayerLuts* outLuts, 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()); diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp index f1091a6f03..d369403737 100644 --- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp +++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp @@ -182,8 +182,8 @@ void LayerLifecycleManager::onHandlesDestroyed( } } -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 330da9a3d3..072be35b26 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 @@ public: // 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/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h index 7ddd7baf1e..dd861a74f4 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 a1e8213132..5bf86e5705 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 @@ void TransactionHandler::collectTransactions() { } } -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 @@ std::vector<TransactionState> TransactionHandler::flushTransactions() { } 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::applyUnsignaledBufferTransaction( } } -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 @@ TransactionHandler::TransactionReadiness TransactionHandler::applyFilters( 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 00f6bcebe6..e78dd88be2 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 @@ namespace surfaceflinger::frontend { 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 @@ public: 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 @@ private: // 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 4af27ab84d..f7dfeb84bc 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/TransactionState.h b/services/surfaceflinger/QueuedTransactionState.h index e5d648194f..af40c026a0 100644 --- a/services/surfaceflinger/TransactionState.h +++ b/services/surfaceflinger/QueuedTransactionState.h @@ -16,9 +16,7 @@ #pragma once -#include <condition_variable> #include <memory> -#include <mutex> #include <vector> #include "FrontEnd/LayerCreationArgs.h" #include "renderengine/ExternalTexture.h" @@ -47,18 +45,20 @@ public: 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), diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index e05c5bd97c..dd1119a5d1 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -4774,16 +4774,16 @@ void SurfaceFlinger::addTransactionReadyFilters() { // 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) { @@ -4944,22 +4944,22 @@ status_t SurfaceFlinger::setTransactionState( } } - 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}; if (mTransactionTracing) { mTransactionTracing->addQueuedTransaction(state); @@ -5044,7 +5044,7 @@ bool SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelin } bool SurfaceFlinger::applyAndCommitDisplayTransactionStatesLocked( - std::vector<TransactionState>& transactions) { + std::vector<QueuedTransactionState>& transactions) { bool needsTraversal = false; uint32_t transactionFlags = 0; for (auto& transaction : transactions) { @@ -5438,7 +5438,7 @@ void SurfaceFlinger::onHandleDestroyed(sp<Layer>& layer, uint32_t layerId) { } void SurfaceFlinger::initializeDisplays() { - TransactionState state; + QueuedTransactionState state; state.inputWindowCommands = mInputWindowCommands; const nsecs_t now = systemTime(); state.desiredPresentTime = now; @@ -5453,7 +5453,7 @@ void SurfaceFlinger::initializeDisplays() { state.displays.push(DisplayState(display.token(), ui::LayerStack::fromValue(layerStack++))); } - std::vector<TransactionState> transactions; + std::vector<QueuedTransactionState> transactions; transactions.emplace_back(state); { diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index c85c0846c5..c2e687f385 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 @@ private: // 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 @@ private: 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) diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp index f39a4d2af4..2676ca6777 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 @@ public: ~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 @@ perfetto::protos::LayerCreationArgs TransactionProtoParser::toProto(const LayerC 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 b3ab71cfb5..a02e2318ef 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 @@ public: 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 bc9f8094f2..1cd75170bf 100644 --- a/services/surfaceflinger/Tracing/TransactionTracing.cpp +++ b/services/surfaceflinger/Tracing/TransactionTracing.cpp @@ -166,7 +166,7 @@ void TransactionTracing::dump(std::string& result) const { 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 7a0fb5ef6e..d784168553 100644 --- a/services/surfaceflinger/Tracing/TransactionTracing.h +++ b/services/surfaceflinger/Tracing/TransactionTracing.h @@ -134,7 +134,7 @@ public: // 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 1dba17585c..5cf42449c0 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 @@ bool LayerTraceGenerator::generate(const perfetto::protos::TransactionTraceFile& 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/tests/benchmarks/LayerLifecycleManager_benchmarks.cpp b/services/surfaceflinger/tests/benchmarks/LayerLifecycleManager_benchmarks.cpp index 7641a45ba4..0925118f87 100644 --- a/services/surfaceflinger/tests/benchmarks/LayerLifecycleManager_benchmarks.cpp +++ b/services/surfaceflinger/tests/benchmarks/LayerLifecycleManager_benchmarks.cpp @@ -50,7 +50,7 @@ static void updateClientStates(benchmark::State& state) { 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 @@ static void updateClientStatesNoChanges(benchmark::State& state) { 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 97946205ba..a894c418f6 100644 --- a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h +++ b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h @@ -66,8 +66,8 @@ public: /*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 @@ public: 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 @@ public: 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 @@ public: } 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 @@ public: } 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 @@ public: } 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 @@ public: } 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 @@ public: 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 @@ public: } 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 @@ public: } 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 @@ public: 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 @@ public: } 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 @@ public: } 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 @@ public: } 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 @@ public: 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 @@ public: } 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 @@ public: } 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 @@ public: 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 @@ public: } 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 @@ public: } 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 @@ public: } 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 @@ public: } 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 @@ public: } 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 @@ public: } 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 @@ public: } 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 @@ public: } 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 @@ public: } 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 @@ public: 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({}); @@ -484,7 +486,7 @@ public: } 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 +497,7 @@ public: } 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 +508,7 @@ public: } 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 +519,7 @@ public: } 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 +531,7 @@ public: } 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/LayerLifecycleManagerTest.cpp b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp index c7cc21ce07..976cecbe40 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 @@ TEST_F(LayerLifecycleManagerTest, updateLayerStates) { 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 @@ TEST_F(LayerLifecycleManagerTest, canAddBackgroundLayer) { 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 @@ TEST_F(LayerLifecycleManagerTest, canDestroyBackgroundLayer) { 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 @@ TEST_F(LayerLifecycleManagerTest, onParentDestroyDestroysBackgroundLayer) { 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 4d322efe86..1177d1601e 100644 --- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp @@ -329,7 +329,7 @@ TEST_F(LayerSnapshotTest, ReparentingUpdatesGameMode) { } 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 +374,7 @@ TEST_F(LayerSnapshotTest, UpdateMetadata) { 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; @@ -1557,7 +1557,7 @@ TEST_F(LayerSnapshotTest, NonVisibleLayerWithInput) { 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 +1586,7 @@ TEST_F(LayerSnapshotTest, NonVisibleLayerWithInputShouldNotBeIncluded) { 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 +2021,7 @@ TEST_F(LayerSnapshotTest, contentDirtyWhenParentGeometryChanges) { 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 +2040,7 @@ TEST_F(LayerSnapshotTest, shouldUpdatePictureProfileHandle) { 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 +2063,7 @@ TEST_F(LayerSnapshotTest, shouldUpdatePictureProfilePriorityFromAppContentPriori 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/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index bd1382e851..3455c13bb7 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -514,7 +514,7 @@ public: mergedTransactionIds); } - auto setTransactionStateInternal(TransactionState& transaction) { + auto setTransactionStateInternal(QueuedTransactionState& transaction) { return FTL_FAKE_GUARD(kMainThreadContext, mFlinger->mTransactionHandler.queueTransaction( std::move(transaction))); diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp index 1e8cd0a0ca..69dfcc4a8f 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 @@ public: 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 @@ TEST_F(TransactionApplicationTest, ApplyTokensUseDifferentQueues) { 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 @@ TEST_F(TransactionApplicationTest, ApplyTokensUseDifferentQueues) { 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 @@ public: 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_F(LatchUnsignaledDisabledTest, Flush_KeepInTheUnsignaledTheQueue) { 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 af0233063e..d3eec5c6f3 100644 --- a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp @@ -30,7 +30,7 @@ namespace android { 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 @@ TEST(TransactionProtoParserTest, parse) { 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 f8f08c78fd..036d8c414d 100644 --- a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp @@ -49,19 +49,19 @@ protected: 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 @@ protected: }; 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 @@ TEST_F(TransactionTracingTest, addTransactions) { 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 @@ protected: 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 @@ protected: // 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 @@ protected: /*layerIdToMirror=*/mLayerId, /*flags=*/0, /*addToRoot=*/false)); - TransactionState transaction; + QueuedTransactionState transaction; transaction.id = 50; ResolvedComposerState layerState; layerState.layerId = mLayerId; |