diff options
95 files changed, 2739 insertions, 884 deletions
diff --git a/cmds/lshal/libprocpartition/include/procpartition/procpartition.h b/cmds/lshal/libprocpartition/include/procpartition/procpartition.h index ca1e690694..9c0fc18697 100644 --- a/cmds/lshal/libprocpartition/include/procpartition/procpartition.h +++ b/cmds/lshal/libprocpartition/include/procpartition/procpartition.h @@ -27,6 +27,8 @@ namespace procpartition { enum class Partition { UNKNOWN = 0, SYSTEM, + SYSTEM_EXT, + PRODUCT, VENDOR, ODM }; diff --git a/cmds/lshal/libprocpartition/procpartition.cpp b/cmds/lshal/libprocpartition/procpartition.cpp index 9645f3a3f3..35fad577a4 100644 --- a/cmds/lshal/libprocpartition/procpartition.cpp +++ b/cmds/lshal/libprocpartition/procpartition.cpp @@ -24,6 +24,8 @@ namespace procpartition { std::ostream& operator<<(std::ostream& os, Partition p) { switch (p) { case Partition::SYSTEM: return os << "system"; + case Partition::SYSTEM_EXT: return os << "system_ext"; + case Partition::PRODUCT: return os << "product"; case Partition::VENDOR: return os << "vendor"; case Partition::ODM: return os << "odm"; case Partition::UNKNOWN: // fallthrough @@ -57,6 +59,12 @@ Partition parsePartition(const std::string& s) { if (s == "system") { return Partition::SYSTEM; } + if (s == "system_ext") { + return Partition::SYSTEM_EXT; + } + if (s == "product") { + return Partition::PRODUCT; + } if (s == "vendor") { return Partition::VENDOR; } diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp index 38a125bb54..59c4d53bc0 100644 --- a/cmds/servicemanager/ServiceManager.cpp +++ b/cmds/servicemanager/ServiceManager.cpp @@ -410,7 +410,16 @@ Status ServiceManager::getService2(const std::string& name, os::Service* outServ return Status::ok(); } -Status ServiceManager::checkService(const std::string& name, os::Service* outService) { +Status ServiceManager::checkService(const std::string& name, sp<IBinder>* outBinder) { + SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_PROTO_FIELDS( + PERFETTO_TE_PROTO_FIELD_CSTR(kProtoServiceName, name.c_str()))); + + *outBinder = tryGetBinder(name, false).service; + // returns ok regardless of result for legacy reasons + return Status::ok(); +} + +Status ServiceManager::checkService2(const std::string& name, os::Service* outService) { SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_PROTO_FIELDS( PERFETTO_TE_PROTO_FIELD_CSTR(kProtoServiceName, name.c_str()))); diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h index 964abee6af..5c4d891218 100644 --- a/cmds/servicemanager/ServiceManager.h +++ b/cmds/servicemanager/ServiceManager.h @@ -46,7 +46,8 @@ public: // getService will try to start any services it cannot find binder::Status getService(const std::string& name, sp<IBinder>* outBinder) override; binder::Status getService2(const std::string& name, os::Service* outService) override; - binder::Status checkService(const std::string& name, os::Service* outService) override; + binder::Status checkService(const std::string& name, sp<IBinder>* outBinder) override; + binder::Status checkService2(const std::string& name, os::Service* outService) override; binder::Status addService(const std::string& name, const sp<IBinder>& binder, bool allowIsolated, int32_t dumpPriority) override; binder::Status listServices(int32_t dumpPriority, std::vector<std::string>* outList) override; diff --git a/cmds/servicemanager/test_sm.cpp b/cmds/servicemanager/test_sm.cpp index e620770e18..7ad84faca0 100644 --- a/cmds/servicemanager/test_sm.cpp +++ b/cmds/servicemanager/test_sm.cpp @@ -204,6 +204,11 @@ TEST(GetService, HappyHappy) { sp<IBinder> outBinder; EXPECT_TRUE(sm->getService("foo", &outBinder).isOk()); EXPECT_EQ(service, outBinder); + + EXPECT_TRUE(sm->checkService2("foo", &out).isOk()); + EXPECT_EQ(service, out.get<Service::Tag::serviceWithMetadata>().service); + EXPECT_TRUE(sm->checkService("foo", &outBinder).isOk()); + EXPECT_EQ(service, outBinder); } TEST(GetService, NonExistant) { diff --git a/data/etc/Android.bp b/data/etc/Android.bp index 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/surface_control_input_receiver.h b/include/android/surface_control_input_receiver.h index f0503f6324..59d3a31b30 100644 --- a/include/android/surface_control_input_receiver.h +++ b/include/android/surface_control_input_receiver.h @@ -39,11 +39,11 @@ __BEGIN_DECLS * * \param motionEvent The motion event. This must be released with AInputEvent_release. * + * \return true if the event is handled by the client, false otherwise. * Available since API level 35. */ typedef bool (*AInputReceiver_onMotionEvent)(void *_Null_unspecified context, - AInputEvent *_Nonnull motionEvent) - __INTRODUCED_IN(__ANDROID_API_V__); + AInputEvent *_Nonnull motionEvent); /** * The AInputReceiver_onKeyEvent callback is invoked when the registered input channel receives * a key event. @@ -53,11 +53,12 @@ typedef bool (*AInputReceiver_onMotionEvent)(void *_Null_unspecified context, * * \param keyEvent The key event. This must be released with AInputEvent_release. * + * \return true if the event is handled by the client, false otherwise. System may generate + * a fallback key event if the event is not handled. * Available since API level 35. */ typedef bool (*AInputReceiver_onKeyEvent)(void *_Null_unspecified context, - AInputEvent *_Nonnull keyEvent) - __INTRODUCED_IN(__ANDROID_API_V__); + AInputEvent *_Nonnull keyEvent); typedef struct AInputReceiverCallbacks AInputReceiverCallbacks; diff --git a/include/android/system_health.h b/include/android/system_health.h index 352eb72b0c..6d59706490 100644 --- a/include/android/system_health.h +++ b/include/android/system_health.h @@ -16,6 +16,31 @@ /** * @defgroup SystemHealth +* + * SystemHealth provides access to data about how various system resources are used by applications. + * + * CPU/GPU headroom APIs are designed to be best used by applications with consistent and intense + * workload such as games to query the remaining capacity headroom over a short period and perform + * optimization accordingly. Due to the nature of the fast job scheduling and frequency scaling of + * CPU and GPU, the headroom by nature will have "TOCTOU" problem which makes it less suitable for + * apps with inconsistent or low workload to take any useful action but simply monitoring. And to + * avoid oscillation it's not recommended to adjust workload too frequent (on each polling request) + * or too aggressively. As the headroom calculation is more based on reflecting past history usage + * than predicting future capacity. Take game as an example, if the API returns CPU headroom of 0 in + * one scenario (especially if it's constant across multiple calls), or some value significantly + * smaller than other scenarios, then it can reason that the recent performance result is more CPU + * bottlenecked. Then reducing the CPU workload intensity can help reserve some headroom to handle + * the load variance better, which can result in less frame drops or smooth FPS value. On the other + * hand, if the API returns large CPU headroom constantly, the app can be more confident to increase + * the workload and expect higher possibility of device meeting its performance expectation. + * App can also use thermal APIs to read the current thermal status and headroom first, then poll + * the CPU and GPU headroom if the device is (about to) getting thermal throttled. If the CPU/GPU + * headrooms provide enough significance such as one valued at 0 while the other at 100, then it can + * be used to infer that reducing CPU workload could be more efficient to cool down the device. + * There is a caveat that the power controller may scale down the frequency of the CPU and GPU due + * to thermal and other reasons, which can result in a higher than usual percentage usage of the + * capacity. + * * @{ */ @@ -70,28 +95,14 @@ typedef struct ACpuHeadroomParams ACpuHeadroomParams; */ typedef struct AGpuHeadroomParams AGpuHeadroomParams; -/** - * Creates a new instance of ACpuHeadroomParams. - * - * When the client finishes using {@link ACpuHeadroomParams}, - * {@link ACpuHeadroomParams_destroy()} must be called to destroy - * and free up the resources associated with {@link ACpuHeadroomParams}. - * - * Available since API level 36. - * - * @return A new instance of ACpuHeadroomParams. - */ -ACpuHeadroomParams* _Nonnull ACpuHeadroomParams_create(void) -__INTRODUCED_IN(36); - typedef enum ACpuHeadroomCalculationType : int32_t { /** - * Use the minimum headroom value within the calculation window. + * The headroom calculation type bases on minimum value over a specified window. * Introduced in API level 36. */ ACPU_HEADROOM_CALCULATION_TYPE_MIN = 0, /** - * Use the average headroom value within the calculation window. + * The headroom calculation type bases on average value over a specified window. * Introduced in API level 36. */ ACPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1, @@ -99,51 +110,55 @@ typedef enum ACpuHeadroomCalculationType : int32_t { typedef enum AGpuHeadroomCalculationType : int32_t { /** - * Use the minimum headroom value within the calculation window. + * The headroom calculation type bases on minimum value over a specified window. * Introduced in API level 36. */ AGPU_HEADROOM_CALCULATION_TYPE_MIN = 0, /** - * Use the average headroom value within the calculation window. + * The headroom calculation type bases on average value over a specified window. * Introduced in API level 36. */ AGPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1, } AGpuHeadroomCalculationType; /** - * Sets the headroom calculation window size in ACpuHeadroomParams. + * Sets the CPU headroom calculation window size in milliseconds. * * Available since API level 36. * * @param params The params to be set. - * @param windowMillis The window size in milliseconds ranged from [50, 10000]. The smaller the + * @param windowMillis The window size in milliseconds ranges from + * {@link ASystemHealth_getCpuHeadroomCalculationWindowRange}. The smaller the * window size, the larger fluctuation in the headroom value should be expected. * The default value can be retrieved from the * {@link #ACpuHeadroomParams_getCalculationWindowMillis} if not set. The device * will try to use the closest feasible window size to this param. */ -void ACpuHeadroomParams_setCalculationWindowMillis(ACpuHeadroomParams* _Nonnull params, +void ACpuHeadroomParams_setCalculationWindowMillis(ACpuHeadroomParams *_Nonnull params, int windowMillis) __INTRODUCED_IN(36); /** - * Gets the headroom calculation window size in ACpuHeadroomParams. + * Gets the CPU headroom calculation window size in milliseconds. + * + * This will return the default value chosen by the device if not set. * * Available since API level 36. * - * @param params The params to be set. + * @param params The params to read from. * @return This will return the default value chosen by the device if the params is not set. */ int ACpuHeadroomParams_getCalculationWindowMillis(ACpuHeadroomParams* _Nonnull params) __INTRODUCED_IN(36); /** - * Sets the headroom calculation window size in AGpuHeadroomParams. + * Sets the GPU headroom calculation window size in milliseconds. * * Available since API level 36. * * @param params The params to be set. - * @param windowMillis The window size in milliseconds ranged from [50, 10000]. The smaller the + * @param windowMillis The window size in milliseconds ranges from + * {@link ASystemHealth_getGpuHeadroomCalculationWindowRange}. The smaller the * window size, the larger fluctuation in the headroom value should be expected. * The default value can be retrieved from the * {@link #AGpuHeadroomParams_getCalculationWindowMillis} if not set. The device @@ -154,18 +169,20 @@ void AGpuHeadroomParams_setCalculationWindowMillis(AGpuHeadroomParams* _Nonnull __INTRODUCED_IN(36); /** - * Gets the headroom calculation window size in AGpuHeadroomParams. + * Gets the GPU headroom calculation window size in milliseconds. + * + * This will return the default value chosen by the device if not set. * * Available since API level 36. * - * @param params The params to be set. + * @param params The params to read from. * @return This will return the default value chosen by the device if the params is not set. */ int AGpuHeadroomParams_getCalculationWindowMillis(AGpuHeadroomParams* _Nonnull params) __INTRODUCED_IN(36); /** - * Sets the headroom calculation type in ACpuHeadroomParams. + * Sets the CPU headroom calculation type in {@link ACpuHeadroomParams}. * * Available since API level 36. * @@ -177,11 +194,13 @@ void ACpuHeadroomParams_setCalculationType(ACpuHeadroomParams* _Nonnull params, __INTRODUCED_IN(36); /** - * Gets the headroom calculation type in ACpuHeadroomParams. + * Gets the CPU headroom calculation type in {@link ACpuHeadroomParams}. + * + * This will return the default value chosen by the device if not set. * * Available since API level 36. * - * @param params The params to be set. + * @param params The params to read from. * @return The headroom calculation type. */ ACpuHeadroomCalculationType @@ -189,7 +208,7 @@ ACpuHeadroomParams_getCalculationType(ACpuHeadroomParams* _Nonnull params) __INTRODUCED_IN(36); /** - * Sets the headroom calculation type in AGpuHeadroomParams. + * Sets the GPU headroom calculation type in {@link AGpuHeadroomParams}. * * Available since API level 36. * @@ -201,11 +220,13 @@ void AGpuHeadroomParams_setCalculationType(AGpuHeadroomParams* _Nonnull params, __INTRODUCED_IN(36); /** - * Gets the headroom calculation type in AGpuHeadroomParams. + * Gets the GPU headroom calculation type in {@link AGpuHeadroomParams}. + * + * This will return the default value chosen by the device if not set. * * Available since API level 36. * - * @param params The params to be set. + * @param params The params to read from. * @return The headroom calculation type. */ AGpuHeadroomCalculationType @@ -213,50 +234,115 @@ AGpuHeadroomParams_getCalculationType(AGpuHeadroomParams* _Nonnull params) __INTRODUCED_IN(36); /** - * Sets the thread TIDs to track in ACpuHeadroomParams. + * Sets the thread TIDs to track in {@link ACpuHeadroomParams}. + * + * The TIDs should belong to the same of the process that will make the headroom call. And they + * should not have different core affinity. + * + * If not set or set to empty, the headroom will be based on the PID of the process making the call. * * Available since API level 36. * * @param params The params to be set. - * @param tids Non-null array of TIDs, maximum 5. + * @param tids Non-null array of TIDs, where maximum size can be read from + * {@link ASystemHealth_getMaxCpuHeadroomTidsSize}. * @param tidsSize The size of the tids array. */ void ACpuHeadroomParams_setTids(ACpuHeadroomParams* _Nonnull params, const int* _Nonnull tids, - int tidsSize) + size_t tidsSize) __INTRODUCED_IN(36); /** - * Creates a new instance of AGpuHeadroomParams. + * Creates a new instance of {@link ACpuHeadroomParams}. + * + * When the client finishes using {@link ACpuHeadroomParams}, + * {@link ACpuHeadroomParams_destroy} must be called to destroy + * and free up the resources associated with {@link ACpuHeadroomParams}. + * + * Available since API level 36. + * + * @return A new instance of {@link ACpuHeadroomParams}. + */ +ACpuHeadroomParams* _Nonnull ACpuHeadroomParams_create(void) +__INTRODUCED_IN(36); + +/** + * Creates a new instance of {@link AGpuHeadroomParams}. * * When the client finishes using {@link AGpuHeadroomParams}, - * {@link AGpuHeadroomParams_destroy()} must be called to destroy + * {@link AGpuHeadroomParams_destroy} must be called to destroy * and free up the resources associated with {@link AGpuHeadroomParams}. * * Available since API level 36. * - * @return A new instance of AGpuHeadroomParams. + * @return A new instance of {@link AGpuHeadroomParams}. */ AGpuHeadroomParams* _Nonnull AGpuHeadroomParams_create(void) __INTRODUCED_IN(36); /** - * Deletes the ACpuHeadroomParams instance. + * Deletes the {@link ACpuHeadroomParams} instance. * * Available since API level 36. * * @param params The params to be deleted. */ -void ACpuHeadroomParams_destroy(ACpuHeadroomParams* _Nonnull params) +void ACpuHeadroomParams_destroy(ACpuHeadroomParams* _Nullable params) __INTRODUCED_IN(36); /** - * Deletes the AGpuHeadroomParams instance. + * Deletes the {@link AGpuHeadroomParams} instance. * * Available since API level 36. * * @param params The params to be deleted. */ -void AGpuHeadroomParams_destroy(AGpuHeadroomParams* _Nonnull params) +void AGpuHeadroomParams_destroy(AGpuHeadroomParams* _Nullable params) +__INTRODUCED_IN(36); + +/** + * Gets the maximum number of TIDs this device supports for getting CPU headroom. + * + * See {@link ACpuHeadroomParams_setTids}. + * + * Available since API level 36. + * + * @param outSize Non-null output pointer to the max size. + * @return 0 on success. + * ENOTSUP if the CPU headroom API is unsupported. + */ +int ASystemHealth_getMaxCpuHeadroomTidsSize(size_t* _Nonnull outSize); + +/** + * Gets the range of the calculation window size for CPU headroom. + * + * In API version 36, the range will be a superset of [50, 10000]. + * + * Available since API level 36. + * + * @param outMinMillis Non-null output pointer to be set to the minimum window size in milliseconds. + * @param outMaxMillis Non-null output pointer to be set to the maximum window size in milliseconds. + * @return 0 on success. + * ENOTSUP if API is unsupported. + */ +int ASystemHealth_getCpuHeadroomCalculationWindowRange(int32_t* _Nonnull outMinMillis, + int32_t* _Nonnull outMaxMillis) +__INTRODUCED_IN(36); + +/** + * Gets the range of the calculation window size for GPU headroom. + * + * In API version 36, the range will be a superset of [50, 10000]. + * + * Available since API level 36. + * + * @param outMinMillis Non-null output pointer to be set to the minimum window size in milliseconds. + * @param outMaxMillis Non-null output pointer to be set to the maximum window size in milliseconds. + * @return 0 on success. + * ENOTSUP if API is unsupported. + */ +int ASystemHealth_getGpuHeadroomCalculationWindowRange(int32_t* _Nonnull outMinMillis, + int32_t* _Nonnull outMaxMillis) __INTRODUCED_IN(36); /** @@ -267,15 +353,21 @@ __INTRODUCED_IN(36); * be used with the thermal status and headroom to determine if reducing the CPU bound workload can * help reduce the device temperature to avoid thermal throttling. * + * If the params are valid, each call will perform at least one synchronous binder transaction that + * can take more than 1ms. So it's not recommended to call or wait for this on critical threads. + * Some devices may implement this as an on-demand API with lazy initialization, so the caller + * should expect higher latency when making the first call (especially with non-default params) + * since app starts or after changing params, as the device may need to change its data collection. + * * Available since API level 36. * - * @param params The params to customize the CPU headroom calculation, or nullptr to use the default + * @param params The params to customize the CPU headroom calculation, or nullptr to use default. * @param outHeadroom Non-null output pointer to a single float, which will be set to the CPU * headroom value. The value will be a single value or `Float.NaN` if it's - * temporarily unavailable. + * temporarily unavailable due to server error or not enough user CPU workload. * Each valid value ranges from [0, 100], where 0 indicates no more cpu resources * can be granted. - * @return 0 on success + * @return 0 on success. * EPIPE if failed to get the CPU headroom. * EPERM if the TIDs do not belong to the same process. * ENOTSUP if API or requested params is unsupported. @@ -292,15 +384,21 @@ __INTRODUCED_IN(36); * be used with the thermal status and headroom to determine if reducing the GPU bound workload can * help reduce the device temperature to avoid thermal throttling. * + * If the params are valid, each call will perform at least one synchronous binder transaction that + * can take more than 1ms. So it's not recommended to call or wait for this on critical threads. + * Some devices may implement this as an on-demand API with lazy initialization, so the caller + * should expect higher latency when making the first call (especially with non-default params) + * since app starts or after changing params, as the device may need to change its data collection. + * * Available since API level 36 * - * @param params The params to customize the GPU headroom calculation, or nullptr to use the default + * @param params The params to customize the GPU headroom calculation, or nullptr to use default * @param outHeadroom Non-null output pointer to a single float, which will be set to the GPU * headroom value. The value will be a single value or `Float.NaN` if it's * temporarily unavailable. * Each valid value ranges from [0, 100], where 0 indicates no more gpu resources * can be granted. - * @return 0 on success + * @return 0 on success. * EPIPE if failed to get the GPU headroom. * ENOTSUP if API or requested params is unsupported. */ @@ -311,13 +409,14 @@ __INTRODUCED_IN(36); /** * Gets minimum polling interval for calling {@link ASystemHealth_getCpuHeadroom} in milliseconds. * - * The getCpuHeadroom API may return cached result if called more frequently than the interval. + * The {@link ASystemHealth_getCpuHeadroom} API may return cached result if called more frequently + * than the interval. * * Available since API level 36. * * @param outMinIntervalMillis Non-null output pointer to a int64_t, which * will be set to the minimum polling interval in milliseconds. - * @return 0 on success + * @return 0 on success. * EPIPE if failed to get the minimum polling interval. * ENOTSUP if API is unsupported. */ @@ -327,13 +426,14 @@ __INTRODUCED_IN(36); /** * Gets minimum polling interval for calling {@link ASystemHealth_getGpuHeadroom} in milliseconds. * - * The getGpuHeadroom API may return cached result if called more frequent than the interval. + * The {@link ASystemHealth_getGpuHeadroom} API may return cached result if called more frequently + * than the interval. * * Available since API level 36. * * @param outMinIntervalMillis Non-null output pointer to a int64_t, which * will be set to the minimum polling interval in milliseconds. - * @return 0 on success + * @return 0 on success. * EPIPE if failed to get the minimum polling interval. * ENOTSUP if API is unsupported. */ diff --git a/include/ftl/finalizer.h b/include/ftl/finalizer.h new file mode 100644 index 0000000000..0251957ad0 --- /dev/null +++ b/include/ftl/finalizer.h @@ -0,0 +1,211 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <cstddef> + +#include <functional> +#include <type_traits> +#include <utility> + +#include <ftl/function.h> + +namespace android::ftl { + +// An RAII wrapper that invokes a function object as a finalizer when destroyed. +// +// The function object must take no arguments, and must return void. If the function object needs +// any context for the call, it must store it itself, for example with a lambda capture. +// +// The stored function object will be called once (unless canceled via the `cancel()` member +// function) at the first of: +// +// - The Finalizer instance is destroyed. +// - `operator()` is used to invoke the contained function. +// - The Finalizer instance is move-assigned a new value. The function being replaced will be +// invoked, and the replacement will be stored to be called later. +// +// The intent with this class is to keep cleanup code next to the code that requires that +// cleanup be performed. +// +// bool read_file(std::string filename) { +// FILE* f = fopen(filename.c_str(), "rb"); +// if (f == nullptr) return false; +// const auto cleanup = ftl::Finalizer([f]() { fclose(f); }); +// // fread(...), etc +// return true; +// } +// +// The `FinalFunction` template argument to Finalizer<FinalFunction> allows a polymorphic function +// type for storing the finalization function, such as `std::function` or `ftl::Function`. +// +// For convenience, this header defines a few useful aliases for using those types. +// +// - `FinalizerStd`, an alias for `Finalizer<std::function<void()>>` +// - `FinalizerFtl`, an alias for `Finalizer<ftl::Function<void()>>` +// - `FinalizerFtl1`, an alias for `Finalizer<ftl::Function<void(), 1>>` +// - `FinalizerFtl2`, an alias for `Finalizer<ftl::Function<void(), 2>>` +// - `FinalizerFtl3`, an alias for `Finalizer<ftl::Function<void(), 3>>` +// +// Clients of this header are free to define other aliases they need. +// +// A Finalizer that uses a polymorphic function type can be returned from a function call and/or +// stored as member data (to be destroyed along with the containing class). +// +// auto register(Observer* observer) -> ftl::FinalizerStd<void()> { +// const auto id = observers.add(observer); +// return ftl::Finalizer([id]() { observers.remove(id); }); +// } +// +// { +// const auto _ = register(observer); +// // do the things that required the registered observer. +// } +// // the observer is removed. +// +// Cautions: +// +// 1. When a Finalizer is stored as member data, you will almost certainly want that cleanup to +// happen first, before the rest of the other member data is destroyed. For safety you should +// assume that the finalization function will access that data directly or indirectly. +// +// This means that Finalizers should be defined last, after all other normal member data in a +// class. +// +// class MyClass { +// public: +// bool initialize() { +// ready_ = true; +// cleanup_ = ftl::Finalizer([this]() { ready_ = false; }); +// return true; +// } +// +// bool ready_ = false; +// +// // Finalizers should be last so other class members can be accessed before being +// // destroyed. +// ftl::FinalizerStd<void()> cleanup_; +// }; +// +// 2. Care must be taken to use `ftl::Finalizer()` when constructing locally from a lambda. If you +// forget to do so, you are just creating a lambda that won't be automatically invoked! +// +// const auto bad = [&counter](){ ++counter; }; // Just a lambda instance +// const auto good = ftl::Finalizer([&counter](){ ++counter; }); +// +template <typename FinalFunction> +class Finalizer final { + // requires(std::is_invocable_r_v<void, FinalFunction>) + static_assert(std::is_invocable_r_v<void, FinalFunction>); + + public: + // A default constructed Finalizer does nothing when destroyed. + // requires(std::is_default_constructible_v<FinalFunction>) + constexpr Finalizer() = default; + + // Constructs a Finalizer from a function object. + // requires(std::is_invocable_v<F>) + template <typename F, typename = std::enable_if_t<std::is_invocable_v<F>>> + [[nodiscard]] explicit constexpr Finalizer(F&& function) + : Finalizer(std::forward<F>(function), false) {} + + constexpr ~Finalizer() { maybe_invoke(); } + + // Disallow copying. + Finalizer(const Finalizer& that) = delete; + auto operator=(const Finalizer& that) = delete; + + // Move construction + // requires(std::is_move_constructible_v<FinalFunction>) + [[nodiscard]] constexpr Finalizer(Finalizer&& that) + : Finalizer(std::move(that.function_), std::exchange(that.canceled_, true)) {} + + // Implicit conversion move construction + // requires(!std::is_same_v<Finalizer, Finalizer<F>>) + template <typename F, typename = std::enable_if_t<!std::is_same_v<Finalizer, Finalizer<F>>>> + // NOLINTNEXTLINE(google-explicit-constructor, cppcoreguidelines-rvalue-reference-param-not-moved) + [[nodiscard]] constexpr Finalizer(Finalizer<F>&& that) + : Finalizer(std::move(that.function_), std::exchange(that.canceled_, true)) {} + + // Move assignment + // requires(std::is_move_assignable_v<FinalFunction>) + constexpr auto operator=(Finalizer&& that) -> Finalizer& { + maybe_invoke(); + + function_ = std::move(that.function_); + canceled_ = std::exchange(that.canceled_, true); + + return *this; + } + + // Implicit conversion move assignment + // requires(!std::is_same_v<Finalizer, Finalizer<F>>) + template <typename F, typename = std::enable_if_t<!std::is_same_v<Finalizer, Finalizer<F>>>> + // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) + constexpr auto operator=(Finalizer<F>&& that) -> Finalizer& { + *this = Finalizer(std::move(that.function_), std::exchange(that.canceled_, true)); + return *this; + } + + // Cancels the final function, preventing it from being invoked. + constexpr void cancel() { + canceled_ = true; + maybe_nullify_function(); + } + + // Invokes the final function now, if not already invoked. + constexpr void operator()() { maybe_invoke(); } + + private: + template <typename> + friend class Finalizer; + + template <typename F, typename = std::enable_if_t<std::is_invocable_v<F>>> + [[nodiscard]] explicit constexpr Finalizer(F&& function, bool canceled) + : function_(std::forward<F>(function)), canceled_(canceled) {} + + constexpr void maybe_invoke() { + if (!std::exchange(canceled_, true)) { + std::invoke(function_); + maybe_nullify_function(); + } + } + + constexpr void maybe_nullify_function() { + // Sets function_ to nullptr if that is supported for the backing type. + if constexpr (std::is_assignable_v<FinalFunction, nullptr_t>) { + function_ = nullptr; + } + } + + FinalFunction function_; + bool canceled_ = true; +}; + +template <typename F> +Finalizer(F&&) -> Finalizer<std::decay_t<F>>; + +// A standard alias for using `std::function` as the polymorphic function type. +using FinalizerStd = Finalizer<std::function<void()>>; + +// Helpful aliases for using `ftl::Function` as the polymorphic function type. +using FinalizerFtl = Finalizer<Function<void()>>; +using FinalizerFtl1 = Finalizer<Function<void(), 1>>; +using FinalizerFtl2 = Finalizer<Function<void(), 2>>; +using FinalizerFtl3 = Finalizer<Function<void(), 3>>; + +} // namespace android::ftl
\ No newline at end of file diff --git a/include/private/OWNERS b/include/private/OWNERS index 37da96d488..db3ae48698 100644 --- a/include/private/OWNERS +++ b/include/private/OWNERS @@ -1,3 +1,4 @@ # ADPF per-file thermal_private.h = file:platform/frameworks/base:/ADPF_OWNERS per-file performance_hint_private.h = file:platform/frameworks/base:/ADPF_OWNERS +per-file system_health_private.h = file:platform/frameworks/base:/ADPF_OWNERS diff --git a/libs/binder/BackendUnifiedServiceManager.cpp b/libs/binder/BackendUnifiedServiceManager.cpp index 34d5a09948..7c0319aead 100644 --- a/libs/binder/BackendUnifiedServiceManager.cpp +++ b/libs/binder/BackendUnifiedServiceManager.cpp @@ -15,6 +15,7 @@ */ #include "BackendUnifiedServiceManager.h" +#include <android-base/strings.h> #include <android/os/IAccessor.h> #include <android/os/IServiceManager.h> #include <binder/RpcSession.h> @@ -47,6 +48,9 @@ using AidlServiceManager = android::os::IServiceManager; using android::os::IAccessor; using binder::Status; +static const char* kUnsupportedOpNoServiceManager = + "Unsupported operation without a kernel binder servicemanager process"; + static const char* kStaticCachableList[] = { // go/keep-sorted start "accessibility", @@ -211,7 +215,9 @@ Status BackendUnifiedServiceManager::getService(const ::std::string& name, sp<IBinder>* _aidl_return) { os::Service service; Status status = getService2(name, &service); - *_aidl_return = service.get<os::Service::Tag::serviceWithMetadata>().service; + if (status.isOk()) { + *_aidl_return = service.get<os::Service::Tag::serviceWithMetadata>().service; + } return status; } @@ -220,7 +226,10 @@ Status BackendUnifiedServiceManager::getService2(const ::std::string& name, os:: return Status::ok(); } os::Service service; - Status status = mTheRealServiceManager->getService2(name, &service); + Status status = Status::ok(); + if (mTheRealServiceManager) { + status = mTheRealServiceManager->getService2(name, &service); + } if (status.isOk()) { status = toBinderService(name, service, _out); @@ -231,13 +240,26 @@ Status BackendUnifiedServiceManager::getService2(const ::std::string& name, os:: return status; } -Status BackendUnifiedServiceManager::checkService(const ::std::string& name, os::Service* _out) { +Status BackendUnifiedServiceManager::checkService(const ::std::string& name, + sp<IBinder>* _aidl_return) { + os::Service service; + Status status = checkService2(name, &service); + if (status.isOk()) { + *_aidl_return = service.get<os::Service::Tag::serviceWithMetadata>().service; + } + return status; +} + +Status BackendUnifiedServiceManager::checkService2(const ::std::string& name, os::Service* _out) { os::Service service; if (returnIfCached(name, _out)) { return Status::ok(); } - Status status = mTheRealServiceManager->checkService(name, &service); + Status status = Status::ok(); + if (mTheRealServiceManager) { + status = mTheRealServiceManager->checkService2(name, &service); + } if (status.isOk()) { status = toBinderService(name, service, _out); if (status.isOk()) { @@ -315,66 +337,156 @@ Status BackendUnifiedServiceManager::toBinderService(const ::std::string& name, Status BackendUnifiedServiceManager::addService(const ::std::string& name, const sp<IBinder>& service, bool allowIsolated, int32_t dumpPriority) { - Status status = mTheRealServiceManager->addService(name, service, allowIsolated, dumpPriority); - // mEnableAddServiceCache is true by default. - if (kUseCacheInAddService && mEnableAddServiceCache && status.isOk()) { - return updateCache(name, service, - dumpPriority & android::os::IServiceManager::FLAG_IS_LAZY_SERVICE); + if (mTheRealServiceManager) { + Status status = + mTheRealServiceManager->addService(name, service, allowIsolated, dumpPriority); + // mEnableAddServiceCache is true by default. + if (kUseCacheInAddService && mEnableAddServiceCache && status.isOk()) { + return updateCache(name, service, + dumpPriority & android::os::IServiceManager::FLAG_IS_LAZY_SERVICE); + } + return status; } - return status; + return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION, + kUnsupportedOpNoServiceManager); } Status BackendUnifiedServiceManager::listServices(int32_t dumpPriority, ::std::vector<::std::string>* _aidl_return) { - return mTheRealServiceManager->listServices(dumpPriority, _aidl_return); + Status status = Status::ok(); + if (mTheRealServiceManager) { + status = mTheRealServiceManager->listServices(dumpPriority, _aidl_return); + } + if (!status.isOk()) return status; + + appendInjectedAccessorServices(_aidl_return); + + return status; } Status BackendUnifiedServiceManager::registerForNotifications( const ::std::string& name, const sp<os::IServiceCallback>& callback) { - return mTheRealServiceManager->registerForNotifications(name, callback); + if (mTheRealServiceManager) { + return mTheRealServiceManager->registerForNotifications(name, callback); + } + return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION, + kUnsupportedOpNoServiceManager); } Status BackendUnifiedServiceManager::unregisterForNotifications( const ::std::string& name, const sp<os::IServiceCallback>& callback) { - return mTheRealServiceManager->unregisterForNotifications(name, callback); + if (mTheRealServiceManager) { + return mTheRealServiceManager->unregisterForNotifications(name, callback); + } + return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION, + kUnsupportedOpNoServiceManager); } Status BackendUnifiedServiceManager::isDeclared(const ::std::string& name, bool* _aidl_return) { - return mTheRealServiceManager->isDeclared(name, _aidl_return); + Status status = Status::ok(); + if (mTheRealServiceManager) { + status = mTheRealServiceManager->isDeclared(name, _aidl_return); + } + if (!status.isOk()) return status; + + if (!*_aidl_return) { + forEachInjectedAccessorService([&](const std::string& instance) { + if (name == instance) { + *_aidl_return = true; + } + }); + } + + return status; } Status BackendUnifiedServiceManager::getDeclaredInstances( const ::std::string& iface, ::std::vector<::std::string>* _aidl_return) { - return mTheRealServiceManager->getDeclaredInstances(iface, _aidl_return); + Status status = Status::ok(); + if (mTheRealServiceManager) { + status = mTheRealServiceManager->getDeclaredInstances(iface, _aidl_return); + } + if (!status.isOk()) return status; + + forEachInjectedAccessorService([&](const std::string& instance) { + // Declared instances have the format + // <interface>/instance like foo.bar.ISomething/instance + // If it does not have that format, consider the instance to be "" + std::string_view name(instance); + if (base::ConsumePrefix(&name, iface + "/")) { + _aidl_return->emplace_back(name); + } else if (iface == instance) { + _aidl_return->push_back(""); + } + }); + + return status; } Status BackendUnifiedServiceManager::updatableViaApex( const ::std::string& name, ::std::optional<::std::string>* _aidl_return) { - return mTheRealServiceManager->updatableViaApex(name, _aidl_return); + if (mTheRealServiceManager) { + return mTheRealServiceManager->updatableViaApex(name, _aidl_return); + } + return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION, + kUnsupportedOpNoServiceManager); } Status BackendUnifiedServiceManager::getUpdatableNames(const ::std::string& apexName, ::std::vector<::std::string>* _aidl_return) { - return mTheRealServiceManager->getUpdatableNames(apexName, _aidl_return); + if (mTheRealServiceManager) { + return mTheRealServiceManager->getUpdatableNames(apexName, _aidl_return); + } + return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION, + kUnsupportedOpNoServiceManager); } Status BackendUnifiedServiceManager::getConnectionInfo( const ::std::string& name, ::std::optional<os::ConnectionInfo>* _aidl_return) { - return mTheRealServiceManager->getConnectionInfo(name, _aidl_return); + if (mTheRealServiceManager) { + return mTheRealServiceManager->getConnectionInfo(name, _aidl_return); + } + return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION, + kUnsupportedOpNoServiceManager); } Status BackendUnifiedServiceManager::registerClientCallback( const ::std::string& name, const sp<IBinder>& service, const sp<os::IClientCallback>& callback) { - return mTheRealServiceManager->registerClientCallback(name, service, callback); + if (mTheRealServiceManager) { + return mTheRealServiceManager->registerClientCallback(name, service, callback); + } + return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION, + kUnsupportedOpNoServiceManager); } Status BackendUnifiedServiceManager::tryUnregisterService(const ::std::string& name, const sp<IBinder>& service) { - return mTheRealServiceManager->tryUnregisterService(name, service); + if (mTheRealServiceManager) { + return mTheRealServiceManager->tryUnregisterService(name, service); + } + return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION, + kUnsupportedOpNoServiceManager); } Status BackendUnifiedServiceManager::getServiceDebugInfo( ::std::vector<os::ServiceDebugInfo>* _aidl_return) { - return mTheRealServiceManager->getServiceDebugInfo(_aidl_return); + if (mTheRealServiceManager) { + return mTheRealServiceManager->getServiceDebugInfo(_aidl_return); + } + return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION, + kUnsupportedOpNoServiceManager); } [[clang::no_destroy]] static std::once_flag gUSmOnce; [[clang::no_destroy]] static sp<BackendUnifiedServiceManager> gUnifiedServiceManager; +static bool hasOutOfProcessServiceManager() { +#ifndef BINDER_WITH_KERNEL_IPC + return false; +#else +#if defined(__BIONIC__) && !defined(__ANDROID_VNDK__) + return android::base::GetBoolProperty("servicemanager.installed", true); +#else + return true; +#endif +#endif // BINDER_WITH_KERNEL_IPC +} + sp<BackendUnifiedServiceManager> getBackendUnifiedServiceManager() { std::call_once(gUSmOnce, []() { #if defined(__BIONIC__) && !defined(__ANDROID_VNDK__) - /* wait for service manager */ { + /* wait for service manager */ + if (hasOutOfProcessServiceManager()) { using std::literals::chrono_literals::operator""s; using android::base::WaitForProperty; while (!WaitForProperty("servicemanager.ready", "true", 1s)) { @@ -384,7 +496,7 @@ sp<BackendUnifiedServiceManager> getBackendUnifiedServiceManager() { #endif sp<AidlServiceManager> sm = nullptr; - while (sm == nullptr) { + while (hasOutOfProcessServiceManager() && sm == nullptr) { sm = interface_cast<AidlServiceManager>( ProcessState::self()->getContextObject(nullptr)); if (sm == nullptr) { diff --git a/libs/binder/BackendUnifiedServiceManager.h b/libs/binder/BackendUnifiedServiceManager.h index 6a0d06a079..c14f28063f 100644 --- a/libs/binder/BackendUnifiedServiceManager.h +++ b/libs/binder/BackendUnifiedServiceManager.h @@ -122,7 +122,8 @@ public: binder::Status getService(const ::std::string& name, sp<IBinder>* _aidl_return) override; binder::Status getService2(const ::std::string& name, os::Service* out) override; - binder::Status checkService(const ::std::string& name, os::Service* out) override; + binder::Status checkService(const ::std::string& name, sp<IBinder>* _aidl_return) override; + binder::Status checkService2(const ::std::string& name, os::Service* out) override; binder::Status addService(const ::std::string& name, const sp<IBinder>& service, bool allowIsolated, int32_t dumpPriority) override; binder::Status listServices(int32_t dumpPriority, @@ -167,5 +168,9 @@ private: sp<BackendUnifiedServiceManager> getBackendUnifiedServiceManager(); android::binder::Status getInjectedAccessor(const std::string& name, android::os::Service* service); +void appendInjectedAccessorServices(std::vector<std::string>* list); +// Do not call any other service manager APIs that might take the accessor +// mutex because this will be holding it! +void forEachInjectedAccessorService(const std::function<void(const std::string&)>& f); } // namespace android diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp index 53bd08d420..0a22588a90 100644 --- a/libs/binder/Binder.cpp +++ b/libs/binder/Binder.cpp @@ -288,7 +288,7 @@ public: // for below objects RpcMutex mLock; std::set<sp<RpcServerLink>> mRpcServerLinks; - BpBinder::ObjectManager mObjects; + BpBinder::ObjectManager mObjectMgr; unique_fd mRecordingFd; }; @@ -468,7 +468,7 @@ void* BBinder::attachObject(const void* objectID, void* object, void* cleanupCoo LOG_ALWAYS_FATAL_IF(!e, "no memory"); RpcMutexUniqueLock _l(e->mLock); - return e->mObjects.attach(objectID, object, cleanupCookie, func); + return e->mObjectMgr.attach(objectID, object, cleanupCookie, func); } void* BBinder::findObject(const void* objectID) const @@ -477,7 +477,7 @@ void* BBinder::findObject(const void* objectID) const if (!e) return nullptr; RpcMutexUniqueLock _l(e->mLock); - return e->mObjects.find(objectID); + return e->mObjectMgr.find(objectID); } void* BBinder::detachObject(const void* objectID) { @@ -485,7 +485,7 @@ void* BBinder::detachObject(const void* objectID) { if (!e) return nullptr; RpcMutexUniqueLock _l(e->mLock); - return e->mObjects.detach(objectID); + return e->mObjectMgr.detach(objectID); } void BBinder::withLock(const std::function<void()>& doWithLock) { @@ -501,7 +501,7 @@ sp<IBinder> BBinder::lookupOrCreateWeak(const void* objectID, object_make_func m Extras* e = getOrCreateExtras(); LOG_ALWAYS_FATAL_IF(!e, "no memory"); RpcMutexUniqueLock _l(e->mLock); - return e->mObjects.lookupOrCreateWeak(objectID, make, makeArgs); + return e->mObjectMgr.lookupOrCreateWeak(objectID, make, makeArgs); } BBinder* BBinder::localBinder() diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp index 3758b6521c..444f06174e 100644 --- a/libs/binder/BpBinder.cpp +++ b/libs/binder/BpBinder.cpp @@ -78,7 +78,16 @@ BpBinder::ObjectManager::ObjectManager() BpBinder::ObjectManager::~ObjectManager() { - kill(); + const size_t N = mObjects.size(); + ALOGV("Killing %zu objects in manager %p", N, this); + for (auto i : mObjects) { + const entry_t& e = i.second; + if (e.func != nullptr) { + e.func(i.first, e.object, e.cleanupCookie); + } + } + + mObjects.clear(); } void* BpBinder::ObjectManager::attach(const void* objectID, void* object, void* cleanupCookie, @@ -144,20 +153,6 @@ sp<IBinder> BpBinder::ObjectManager::lookupOrCreateWeak(const void* objectID, ob return newObj; } -void BpBinder::ObjectManager::kill() -{ - const size_t N = mObjects.size(); - ALOGV("Killing %zu objects in manager %p", N, this); - for (auto i : mObjects) { - const entry_t& e = i.second; - if (e.func != nullptr) { - e.func(i.first, e.object, e.cleanupCookie); - } - } - - mObjects.clear(); -} - // --------------------------------------------------------------------------- sp<BpBinder> BpBinder::create(int32_t handle, std::function<void()>* postTask) { @@ -697,19 +692,19 @@ void BpBinder::reportOneDeath(const Obituary& obit) void* BpBinder::attachObject(const void* objectID, void* object, void* cleanupCookie, object_cleanup_func func) { RpcMutexUniqueLock _l(mLock); - ALOGV("Attaching object %p to binder %p (manager=%p)", object, this, &mObjects); - return mObjects.attach(objectID, object, cleanupCookie, func); + ALOGV("Attaching object %p to binder %p (manager=%p)", object, this, &mObjectMgr); + return mObjectMgr.attach(objectID, object, cleanupCookie, func); } void* BpBinder::findObject(const void* objectID) const { RpcMutexUniqueLock _l(mLock); - return mObjects.find(objectID); + return mObjectMgr.find(objectID); } void* BpBinder::detachObject(const void* objectID) { RpcMutexUniqueLock _l(mLock); - return mObjects.detach(objectID); + return mObjectMgr.detach(objectID); } void BpBinder::withLock(const std::function<void()>& doWithLock) { @@ -720,7 +715,7 @@ void BpBinder::withLock(const std::function<void()>& doWithLock) { sp<IBinder> BpBinder::lookupOrCreateWeak(const void* objectID, object_make_func make, const void* makeArgs) { RpcMutexUniqueLock _l(mLock); - return mObjects.lookupOrCreateWeak(objectID, make, makeArgs); + return mObjectMgr.lookupOrCreateWeak(objectID, make, makeArgs); } BpBinder* BpBinder::remoteBinder() diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp index 53435c357b..719e445794 100644 --- a/libs/binder/IServiceManager.cpp +++ b/libs/binder/IServiceManager.cpp @@ -304,6 +304,25 @@ android::binder::Status getInjectedAccessor(const std::string& name, return android::binder::Status::ok(); } +void appendInjectedAccessorServices(std::vector<std::string>* list) { + LOG_ALWAYS_FATAL_IF(list == nullptr, + "Attempted to get list of services from Accessors with nullptr"); + std::lock_guard<std::mutex> lock(gAccessorProvidersMutex); + for (const auto& entry : gAccessorProviders) { + list->insert(list->end(), entry.mProvider->instances().begin(), + entry.mProvider->instances().end()); + } +} + +void forEachInjectedAccessorService(const std::function<void(const std::string&)>& f) { + std::lock_guard<std::mutex> lock(gAccessorProvidersMutex); + for (const auto& entry : gAccessorProviders) { + for (const auto& instance : entry.mProvider->instances()) { + f(instance); + } + } +} + sp<IServiceManager> defaultServiceManager() { std::call_once(gSmOnce, []() { @@ -605,7 +624,7 @@ sp<IBinder> CppBackendShim::getService(const String16& name) const { sp<IBinder> CppBackendShim::checkService(const String16& name) const { Service ret; - if (!mUnifiedServiceManager->checkService(String8(name).c_str(), &ret).isOk()) { + if (!mUnifiedServiceManager->checkService2(String8(name).c_str(), &ret).isOk()) { return nullptr; } return ret.get<Service::Tag::serviceWithMetadata>().service; diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 0b7cd8154d..c0ebee006b 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -1293,10 +1293,6 @@ status_t Parcel::writeUniqueFileDescriptorVector(const std::vector<unique_fd>& v status_t Parcel::writeUniqueFileDescriptorVector(const std::optional<std::vector<unique_fd>>& val) { return writeData(val); } -status_t Parcel::writeUniqueFileDescriptorVector( - const std::unique_ptr<std::vector<unique_fd>>& val) { - return writeData(val); -} status_t Parcel::writeStrongBinderVector(const std::vector<sp<IBinder>>& val) { return writeData(val); } status_t Parcel::writeStrongBinderVector(const std::optional<std::vector<sp<IBinder>>>& val) { return writeData(val); } @@ -1352,10 +1348,6 @@ status_t Parcel::readUtf8VectorFromUtf16Vector(std::vector<std::string>* val) co status_t Parcel::readUniqueFileDescriptorVector(std::optional<std::vector<unique_fd>>* val) const { return readData(val); } -status_t Parcel::readUniqueFileDescriptorVector( - std::unique_ptr<std::vector<unique_fd>>* val) const { - return readData(val); -} status_t Parcel::readUniqueFileDescriptorVector(std::vector<unique_fd>* val) const { return readData(val); } @@ -3397,14 +3389,6 @@ void Parcel::scanForFds() const { } #ifdef BINDER_WITH_KERNEL_IPC -size_t Parcel::getBlobAshmemSize() const -{ - // This used to return the size of all blobs that were written to ashmem, now we're returning - // the ashmem currently referenced by this Parcel, which should be equivalent. - // TODO(b/202029388): Remove method once ABI can be changed. - return getOpenAshmemSize(); -} - size_t Parcel::getOpenAshmemSize() const { auto* kernelFields = maybeKernelFields(); diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING index 9e5e79f89b..4332f8ae79 100644 --- a/libs/binder/TEST_MAPPING +++ b/libs/binder/TEST_MAPPING @@ -37,6 +37,9 @@ "name": "binderStabilityTest" }, { + "name": "binderStabilityIntegrationTest" + }, + { "name": "binderRpcWireProtocolTest" }, { diff --git a/libs/binder/aidl/android/os/IServiceManager.aidl b/libs/binder/aidl/android/os/IServiceManager.aidl index 69edef86aa..6539238ce7 100644 --- a/libs/binder/aidl/android/os/IServiceManager.aidl +++ b/libs/binder/aidl/android/os/IServiceManager.aidl @@ -83,11 +83,20 @@ interface IServiceManager { /** * Retrieve an existing service called @a name from the service + * manager. Non-blocking. Returns null if the service does not exist. + * + * @deprecated TODO(b/355394904): Use checkService2 instead. This does not + * return metadata that is included in ServiceWithMetadata + */ + @UnsupportedAppUsage + @nullable IBinder checkService(@utf8InCpp String name); + + /** + * Retrieve an existing service called @a name from the service * manager. Non-blocking. Returns null if the service does not * exist. */ - @UnsupportedAppUsage - Service checkService(@utf8InCpp String name); + Service checkService2(@utf8InCpp String name); /** * Place a new @a service called @a name into the service diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h index 7518044ce6..935bd8dbc6 100644 --- a/libs/binder/include/binder/BpBinder.h +++ b/libs/binder/include/binder/BpBinder.h @@ -104,6 +104,7 @@ public: // Stop the current recording. LIBBINDER_EXPORTED status_t stopRecordingBinder(); + // Note: This class is not thread safe so protect uses of it when necessary class ObjectManager { public: ObjectManager(); @@ -116,8 +117,6 @@ public: sp<IBinder> lookupOrCreateWeak(const void* objectID, IBinder::object_make_func make, const void* makeArgs); - void kill(); - private: ObjectManager(const ObjectManager&); ObjectManager& operator=(const ObjectManager&); @@ -224,7 +223,7 @@ private: volatile int32_t mObitsSent; Vector<Obituary>* mObituaries; std::unique_ptr<FrozenStateChange> mFrozen; - ObjectManager mObjects; + ObjectManager mObjectMgr; mutable String16 mDescriptorCache; int32_t mTrackedUid; diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h index cdee17c2ee..bb45ad2ad5 100644 --- a/libs/binder/include/binder/IInterface.h +++ b/libs/binder/include/binder/IInterface.h @@ -243,7 +243,6 @@ constexpr const char* const kManualInterfaces[] = { "android.media.IMediaHTTPService", "android.media.IMediaLogService", "android.media.IMediaMetadataRetriever", - "android.media.IMediaMetricsService", "android.media.IMediaPlayer", "android.media.IMediaPlayerClient", "android.media.IMediaPlayerService", diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h index 7d79baa34d..d248f22e89 100644 --- a/libs/binder/include/binder/IServiceManager.h +++ b/libs/binder/include/binder/IServiceManager.h @@ -80,6 +80,14 @@ public: /** * Register a service. + * + * Note: + * This status_t return value may be an exception code from an underlying + * Status type that doesn't have a representive error code in + * utils/Errors.h. + * One example of this is a return value of -7 + * (Status::Exception::EX_UNSUPPORTED_OPERATION) when the service manager + * process is not installed on the device when addService is called. */ // NOLINTNEXTLINE(google-default-arguments) virtual status_t addService(const String16& name, const sp<IBinder>& service, diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h index b4efa0af6d..6c4c6fe573 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h @@ -383,11 +383,13 @@ public: LIBBINDER_EXPORTED status_t writeUniqueFileDescriptorVector(const std::optional<std::vector<binder::unique_fd>>& val); LIBBINDER_EXPORTED status_t - writeUniqueFileDescriptorVector(const std::unique_ptr<std::vector<binder::unique_fd>>& val) - __attribute__((deprecated("use std::optional version instead"))); - LIBBINDER_EXPORTED status_t writeUniqueFileDescriptorVector(const std::vector<binder::unique_fd>& val); + // WARNING: deprecated and incompatible with AIDL. You should use Parcelable + // definitions outside of Parcel to represent shared memory, such as + // IMemory or with ParcelFileDescriptor. We should remove this, or move it to be + // external to Parcel, it's not a very encapsulated API. + // // Writes a blob to the parcel. // If the blob is small, then it is stored in-place, otherwise it is // transferred by way of an anonymous shared memory region. Prefer sending @@ -401,8 +403,6 @@ 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. @@ -627,11 +627,13 @@ public: LIBBINDER_EXPORTED status_t readUniqueFileDescriptorVector(std::optional<std::vector<binder::unique_fd>>* val) const; LIBBINDER_EXPORTED status_t - readUniqueFileDescriptorVector(std::unique_ptr<std::vector<binder::unique_fd>>* val) const - __attribute__((deprecated("use std::optional version instead"))); - LIBBINDER_EXPORTED status_t readUniqueFileDescriptorVector(std::vector<binder::unique_fd>* val) const; + // WARNING: deprecated and incompatible with AIDL. You should use Parcelable + // definitions outside of Parcel to represent shared memory, such as + // IMemory or with ParcelFileDescriptor. We should remove this, or move it to be + // external to Parcel, it's not a very encapsulated API. + // // Reads a blob from the parcel. // The caller should call release() on the blob after reading its contents. LIBBINDER_EXPORTED status_t readBlob(size_t len, ReadableBlob* outBlob) const; @@ -685,6 +687,7 @@ 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; @@ -1485,14 +1488,15 @@ public: * Note: for historical reasons, this does not include ashmem memory which * is referenced by this Parcel, but which this parcel doesn't own (e.g. * writeFileDescriptor is called without 'takeOwnership' true). + * + * WARNING: you should not use this, but rather, unparcel, and inspect + * each FD independently. This counts ashmem size, but there may be + * other resources used for non-ashmem FDs, such as other types of + * shared memory, files, etc.. */ LIBBINDER_EXPORTED size_t getOpenAshmemSize() const; private: - // TODO(b/202029388): Remove 'getBlobAshmemSize' once no prebuilts reference - // this - LIBBINDER_EXPORTED size_t getBlobAshmemSize() const; - // Needed so that we can save object metadata to the disk friend class android::binder::debug::RecordedTransaction; }; diff --git a/libs/binder/include/binder/SafeInterface.h b/libs/binder/include/binder/SafeInterface.h index 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/include/binder/Stability.h b/libs/binder/include/binder/Stability.h index cafb8aa04b..bfe0a5af9c 100644 --- a/libs/binder/include/binder/Stability.h +++ b/libs/binder/include/binder/Stability.h @@ -20,6 +20,8 @@ #include <binder/IBinder.h> #include <string> +class BinderStabilityIntegrationTest_ExpectedStabilityForItsPartition_Test; + namespace android { class BpBinder; @@ -127,6 +129,8 @@ private: // through Parcel) friend ::android::ProcessState; + friend ::BinderStabilityIntegrationTest_ExpectedStabilityForItsPartition_Test; + static void tryMarkCompilationUnit(IBinder* binder); // Currently, we use int16_t for Level so that it can fit in BBinder. @@ -156,11 +160,11 @@ private: uint32_t flags); // get stability information as encoded on the wire - static int16_t getRepr(IBinder* binder); + LIBBINDER_EXPORTED static int16_t getRepr(IBinder* binder); // whether a transaction on binder is allowed, if the transaction // is done from a context with a specific stability level - static bool check(int16_t provided, Level required); + LIBBINDER_EXPORTED static bool check(int16_t provided, Level required); static bool isDeclaredLevel(int32_t level); static std::string levelString(int32_t level); diff --git a/libs/binder/ndk/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/system_only.rs b/libs/binder/rust/src/system_only.rs index 1a58d6b44d..50aa336641 100644 --- a/libs/binder/rust/src/system_only.rs +++ b/libs/binder/rust/src/system_only.rs @@ -23,22 +23,17 @@ use std::ffi::{c_void, CStr, CString}; use std::os::raw::c_char; use libc::{sockaddr, sockaddr_un, sockaddr_vm, socklen_t}; -use std::sync::Arc; -use std::{fmt, mem, ptr}; +use std::boxed::Box; +use std::{mem, ptr}; /// Rust wrapper around ABinderRpc_Accessor objects for RPC binder service management. /// /// Dropping the `Accessor` will drop the underlying object and the binder it owns. +#[derive(Debug)] pub struct Accessor { accessor: *mut sys::ABinderRpc_Accessor, } -impl fmt::Debug for Accessor { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "ABinderRpc_Accessor({:p})", self.accessor) - } -} - /// Socket connection info required for libbinder to connect to a service. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ConnectionInfo { @@ -70,7 +65,7 @@ impl Accessor { where F: Fn(&str) -> Option<ConnectionInfo> + Send + Sync + 'static, { - let callback: *mut c_void = Arc::into_raw(Arc::new(callback)) as *mut c_void; + let callback: *mut c_void = Box::into_raw(Box::new(callback)) as *mut c_void; let inst = CString::new(instance).unwrap(); // Safety: The function pointer is a valid connection_info callback. @@ -154,7 +149,7 @@ impl Accessor { /// the string within isize::MAX from the pointer. The memory must not be mutated for /// the duration of this function call and must be valid for reads from the pointer /// to the null terminator. - /// - The `cookie` parameter must be the cookie for an `Arc<F>` and + /// - The `cookie` parameter must be the cookie for a `Box<F>` and /// the caller must hold a ref-count to it. unsafe extern "C" fn connection_info<F>( instance: *const c_char, @@ -167,7 +162,7 @@ impl Accessor { log::error!("Cookie({cookie:p}) or instance({instance:p}) is null!"); return ptr::null_mut(); } - // Safety: The caller promises that `cookie` is for an Arc<F>. + // Safety: The caller promises that `cookie` is for a Box<F>. let callback = unsafe { (cookie as *const F).as_ref().unwrap() }; // Safety: The caller in libbinder_ndk will have already verified this is a valid @@ -212,19 +207,19 @@ impl Accessor { } } - /// Callback that decrements the ref-count. + /// Callback that drops the `Box<F>`. /// This is invoked from C++ when a binder is unlinked. /// /// # Safety /// - /// - The `cookie` parameter must be the cookie for an `Arc<F>` and + /// - The `cookie` parameter must be the cookie for a `Box<F>` and /// the owner must give up a ref-count to it. unsafe extern "C" fn cookie_decr_refcount<F>(cookie: *mut c_void) where F: Fn(&str) -> Option<ConnectionInfo> + Send + Sync + 'static, { - // Safety: The caller promises that `cookie` is for an Arc<F>. - unsafe { Arc::decrement_strong_count(cookie as *const F) }; + // Safety: The caller promises that `cookie` is for a Box<F>. + unsafe { std::mem::drop(Box::from_raw(cookie as *mut F)) }; } } @@ -301,7 +296,7 @@ impl AccessorProvider { where F: Fn(&str) -> Option<Accessor> + Send + Sync + 'static, { - let callback: *mut c_void = Arc::into_raw(Arc::new(provider)) as *mut c_void; + let callback: *mut c_void = Box::into_raw(Box::new(provider)) as *mut c_void; let c_str_instances: Vec<CString> = instances.iter().map(|s| CString::new(s.as_bytes()).unwrap()).collect(); let mut c_instances: Vec<*const c_char> = @@ -351,7 +346,7 @@ impl AccessorProvider { log::error!("Cookie({cookie:p}) or instance({instance:p}) is null!"); return ptr::null_mut(); } - // Safety: The caller promises that `cookie` is for an Arc<F>. + // Safety: The caller promises that `cookie` is for a Box<F>. let callback = unsafe { (cookie as *const F).as_ref().unwrap() }; let inst = { @@ -382,14 +377,14 @@ impl AccessorProvider { /// /// # Safety /// - /// - The `cookie` parameter must be the cookie for an `Arc<F>` and + /// - The `cookie` parameter must be the cookie for a `Box<F>` and /// the owner must give up a ref-count to it. unsafe extern "C" fn accessor_cookie_decr_refcount<F>(cookie: *mut c_void) where F: Fn(&str) -> Option<Accessor> + Send + Sync + 'static, { - // Safety: The caller promises that `cookie` is for an Arc<F>. - unsafe { Arc::decrement_strong_count(cookie as *const F) }; + // Safety: The caller promises that `cookie` is for a Box<F>. + unsafe { std::mem::drop(Box::from_raw(cookie as *mut F)) }; } } diff --git a/libs/binder/servicedispatcher.cpp b/libs/binder/servicedispatcher.cpp index be990657d5..78fe2a8714 100644 --- a/libs/binder/servicedispatcher.cpp +++ b/libs/binder/servicedispatcher.cpp @@ -127,7 +127,12 @@ public: // We can't send BpBinder for regular binder over RPC. return android::binder::Status::fromStatusT(android::INVALID_OPERATION); } - android::binder::Status checkService(const std::string&, android::os::Service*) override { + android::binder::Status checkService(const std::string&, + android::sp<android::IBinder>*) override { + // We can't send BpBinder for regular binder over RPC. + return android::binder::Status::fromStatusT(android::INVALID_OPERATION); + } + android::binder::Status checkService2(const std::string&, android::os::Service*) override { // We can't send BpBinder for regular binder over RPC. return android::binder::Status::fromStatusT(android::INVALID_OPERATION); } diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp index c21d7c61cd..f412dfb6f4 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -803,6 +803,28 @@ cc_test { } cc_test { + name: "binderStabilityIntegrationTest", + defaults: ["binder_test_defaults"], + srcs: [ + "binderStabilityIntegrationTest.cpp", + ], + + shared_libs: [ + "libbinder", + "libutils", + ], + static_libs: [ + "libprocpartition", + ], + + test_suites: [ + "general-tests", + "vts", + ], + require_root: true, +} + +cc_test { name: "binderAllocationLimits", defaults: ["binder_test_defaults"], srcs: ["binderAllocationLimits.cpp"], diff --git a/libs/binder/tests/binderCacheUnitTest.cpp b/libs/binder/tests/binderCacheUnitTest.cpp index 19395c2dcd..121e5ae5e9 100644 --- a/libs/binder/tests/binderCacheUnitTest.cpp +++ b/libs/binder/tests/binderCacheUnitTest.cpp @@ -74,7 +74,7 @@ class MockAidlServiceManager : public os::IServiceManagerDefault { public: MockAidlServiceManager() : innerSm() {} - binder::Status checkService(const ::std::string& name, os::Service* _out) override { + binder::Status checkService2(const ::std::string& name, os::Service* _out) override { os::ServiceWithMetadata serviceWithMetadata = os::ServiceWithMetadata(); serviceWithMetadata.service = innerSm.getService(String16(name.c_str())); serviceWithMetadata.isLazyService = false; @@ -98,7 +98,7 @@ class MockAidlServiceManager2 : public os::IServiceManagerDefault { public: MockAidlServiceManager2() : innerSm() {} - binder::Status checkService(const ::std::string& name, os::Service* _out) override { + binder::Status checkService2(const ::std::string& name, os::Service* _out) override { os::ServiceWithMetadata serviceWithMetadata = os::ServiceWithMetadata(); serviceWithMetadata.service = innerSm.getService(String16(name.c_str())); serviceWithMetadata.isLazyService = true; diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp index c038c95b07..891c0a290c 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -44,6 +44,7 @@ #include <processgroup/processgroup.h> #include <utils/Flattenable.h> #include <utils/SystemClock.h> +#include "binder/IServiceManagerUnitTestHelper.h" #include <linux/sched.h> #include <sys/epoll.h> @@ -585,14 +586,14 @@ TEST_F(BinderLibTest, AddManagerToManager) { EXPECT_EQ(NO_ERROR, sm->addService(String16("binderLibTest-manager"), binder)); } +class LocalRegistrationCallbackImpl : public virtual IServiceManager::LocalRegistrationCallback { + void onServiceRegistration(const String16&, const sp<IBinder>&) override {} + virtual ~LocalRegistrationCallbackImpl() {} +}; + TEST_F(BinderLibTest, RegisterForNotificationsFailure) { auto sm = defaultServiceManager(); - using LocalRegistrationCallback = IServiceManager::LocalRegistrationCallback; - class LocalRegistrationCallbackImpl : public virtual LocalRegistrationCallback { - void onServiceRegistration(const String16&, const sp<IBinder>&) override {} - virtual ~LocalRegistrationCallbackImpl() {} - }; - sp<LocalRegistrationCallback> cb = sp<LocalRegistrationCallbackImpl>::make(); + sp<IServiceManager::LocalRegistrationCallback> cb = sp<LocalRegistrationCallbackImpl>::make(); EXPECT_EQ(BAD_VALUE, sm->registerForNotifications(String16("ValidName"), nullptr)); EXPECT_EQ(UNKNOWN_ERROR, sm->registerForNotifications(String16("InvalidName!$"), cb)); @@ -600,12 +601,7 @@ TEST_F(BinderLibTest, RegisterForNotificationsFailure) { TEST_F(BinderLibTest, UnregisterForNotificationsFailure) { auto sm = defaultServiceManager(); - using LocalRegistrationCallback = IServiceManager::LocalRegistrationCallback; - class LocalRegistrationCallbackImpl : public virtual LocalRegistrationCallback { - void onServiceRegistration(const String16&, const sp<IBinder>&) override {} - virtual ~LocalRegistrationCallbackImpl() {} - }; - sp<LocalRegistrationCallback> cb = sp<LocalRegistrationCallbackImpl>::make(); + sp<IServiceManager::LocalRegistrationCallback> cb = sp<LocalRegistrationCallbackImpl>::make(); EXPECT_EQ(OK, sm->registerForNotifications(String16("ValidName"), cb)); @@ -1788,6 +1784,43 @@ TEST(ServiceNotifications, Unregister) { EXPECT_EQ(sm->unregisterForNotifications(String16("RogerRafa"), cb), OK); } +// Make sure all IServiceManager APIs will function without an AIDL service +// manager registered on the device. +TEST(ServiceManagerNoAidlServer, SanityCheck) { + String16 kServiceName("no_services_exist"); + // This is what clients will see when there is no servicemanager process + // that registers itself as context object 0. + // Can't use setDefaultServiceManager() here because these test cases run in + // the same process and will abort when called twice or before/after + // defaultServiceManager(). + sp<IServiceManager> sm = getServiceManagerShimFromAidlServiceManagerForTests(nullptr); + auto status = sm->addService(kServiceName, sp<BBinder>::make()); + // CppBackendShim returns Status::exceptionCode as the status_t + EXPECT_EQ(status, Status::Exception::EX_UNSUPPORTED_OPERATION) << statusToString(status); + auto service = sm->checkService(String16("no_services_exist")); + EXPECT_TRUE(service == nullptr); + auto list = sm->listServices(android::IServiceManager::DUMP_FLAG_PRIORITY_ALL); + EXPECT_TRUE(list.isEmpty()); + bool declared = sm->isDeclared(kServiceName); + EXPECT_FALSE(declared); + list = sm->getDeclaredInstances(kServiceName); + EXPECT_TRUE(list.isEmpty()); + auto updatable = sm->updatableViaApex(kServiceName); + EXPECT_EQ(updatable, std::nullopt); + list = sm->getUpdatableNames(kServiceName); + EXPECT_TRUE(list.isEmpty()); + auto conInfo = sm->getConnectionInfo(kServiceName); + EXPECT_EQ(conInfo, std::nullopt); + auto cb = sp<LocalRegistrationCallbackImpl>::make(); + status = sm->registerForNotifications(kServiceName, cb); + EXPECT_EQ(status, UNKNOWN_ERROR) << statusToString(status); + status = sm->unregisterForNotifications(kServiceName, cb); + EXPECT_EQ(status, BAD_VALUE) << statusToString(status); + auto dbgInfos = sm->getServiceDebugInfo(); + EXPECT_TRUE(dbgInfos.empty()); + sm->enableAddServiceCache(true); +} + TEST_F(BinderLibTest, ThreadPoolAvailableThreads) { Parcel data, reply; sp<IBinder> server = addServer(); diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp index da5a8e3881..9f656ec96c 100644 --- a/libs/binder/tests/binderRpcTest.cpp +++ b/libs/binder/tests/binderRpcTest.cpp @@ -1328,6 +1328,109 @@ TEST_P(BinderRpcAccessor, InjectNoSockaddrProvided) { EXPECT_EQ(status, OK); } +class BinderRpcAccessorNoConnection : public ::testing::Test {}; + +TEST_F(BinderRpcAccessorNoConnection, listServices) { + const String16 kInstanceName("super.cool.service/better_than_default"); + const String16 kInstanceName2("super.cool.service/better_than_default2"); + + auto receipt = + addAccessorProvider({String8(kInstanceName).c_str(), String8(kInstanceName2).c_str()}, + [&](const String16&) -> sp<IBinder> { return nullptr; }); + EXPECT_FALSE(receipt.expired()); + Vector<String16> list = + defaultServiceManager()->listServices(IServiceManager::DUMP_FLAG_PRIORITY_ALL); + bool name1 = false; + bool name2 = false; + for (auto name : list) { + if (name == kInstanceName) name1 = true; + if (name == kInstanceName2) name2 = true; + } + EXPECT_TRUE(name1); + EXPECT_TRUE(name2); + status_t status = removeAccessorProvider(receipt); + EXPECT_EQ(status, OK); +} + +TEST_F(BinderRpcAccessorNoConnection, isDeclared) { + const String16 kInstanceName("super.cool.service/default"); + const String16 kInstanceName2("still_counts_as_declared"); + + auto receipt = + addAccessorProvider({String8(kInstanceName).c_str(), String8(kInstanceName2).c_str()}, + [&](const String16&) -> sp<IBinder> { return nullptr; }); + EXPECT_FALSE(receipt.expired()); + EXPECT_TRUE(defaultServiceManager()->isDeclared(kInstanceName)); + EXPECT_TRUE(defaultServiceManager()->isDeclared(kInstanceName2)); + EXPECT_FALSE(defaultServiceManager()->isDeclared(String16("doesnt_exist"))); + status_t status = removeAccessorProvider(receipt); + EXPECT_EQ(status, OK); +} + +TEST_F(BinderRpcAccessorNoConnection, getDeclaredInstances) { + const String16 kInstanceName("super.cool.service.IFoo/default"); + const String16 kInstanceName2("super.cool.service.IFoo/extra/default"); + const String16 kInstanceName3("super.cool.service.IFoo"); + + auto receipt = + addAccessorProvider({String8(kInstanceName).c_str(), String8(kInstanceName2).c_str(), + String8(kInstanceName3).c_str()}, + [&](const String16&) -> sp<IBinder> { return nullptr; }); + EXPECT_FALSE(receipt.expired()); + Vector<String16> list = + defaultServiceManager()->getDeclaredInstances(String16("super.cool.service.IFoo")); + // We would prefer ASSERT_EQ here, but we must call removeAccessorProvider + EXPECT_EQ(list.size(), 3u); + if (list.size() == 3) { + bool name1 = false; + bool name2 = false; + bool name3 = false; + for (auto name : list) { + if (name == String16("default")) name1 = true; + if (name == String16("extra/default")) name2 = true; + if (name == String16()) name3 = true; + } + EXPECT_TRUE(name1) << String8(list[0]); + EXPECT_TRUE(name2) << String8(list[1]); + EXPECT_TRUE(name3) << String8(list[2]); + } + + status_t status = removeAccessorProvider(receipt); + EXPECT_EQ(status, OK); +} + +TEST_F(BinderRpcAccessorNoConnection, getDeclaredWrongInstances) { + const String16 kInstanceName("super.cool.service.IFoo"); + + auto receipt = addAccessorProvider({String8(kInstanceName).c_str()}, + [&](const String16&) -> sp<IBinder> { return nullptr; }); + EXPECT_FALSE(receipt.expired()); + Vector<String16> list = defaultServiceManager()->getDeclaredInstances(String16("unknown")); + EXPECT_TRUE(list.empty()); + + status_t status = removeAccessorProvider(receipt); + EXPECT_EQ(status, OK); +} + +TEST_F(BinderRpcAccessorNoConnection, getDeclaredInstancesSlash) { + // This is treated as if there were no '/' and the declared instance is "" + const String16 kInstanceName("super.cool.service.IFoo/"); + + auto receipt = addAccessorProvider({String8(kInstanceName).c_str()}, + [&](const String16&) -> sp<IBinder> { return nullptr; }); + EXPECT_FALSE(receipt.expired()); + Vector<String16> list = + defaultServiceManager()->getDeclaredInstances(String16("super.cool.service.IFoo")); + bool name1 = false; + for (auto name : list) { + if (name == String16("")) name1 = true; + } + EXPECT_TRUE(name1); + + status_t status = removeAccessorProvider(receipt); + EXPECT_EQ(status, OK); +} + constexpr const char* kARpcInstance = "some.instance.name.IFoo/default"; const char* kARpcSupportedServices[] = { kARpcInstance, diff --git a/libs/binder/tests/binderSafeInterfaceTest.cpp b/libs/binder/tests/binderSafeInterfaceTest.cpp index 849dc7c4d5..45b2103637 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; } @@ -858,7 +858,13 @@ TEST_F(SafeInterfaceTest, TestIncrementTwo) { ASSERT_EQ(b + 1, bPlusOne); } -extern "C" int main(int argc, char **argv) { +} // namespace tests +} // namespace android + +int main(int argc, char** argv) { + using namespace android; + using namespace android::tests; + testing::InitGoogleTest(&argc, argv); if (fork() == 0) { @@ -875,6 +881,3 @@ extern "C" int main(int argc, char **argv) { return RUN_ALL_TESTS(); } - -} // namespace tests -} // namespace android diff --git a/libs/binder/tests/binderStabilityIntegrationTest.cpp b/libs/binder/tests/binderStabilityIntegrationTest.cpp new file mode 100644 index 0000000000..a3fc9cc2c8 --- /dev/null +++ b/libs/binder/tests/binderStabilityIntegrationTest.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <binder/Binder.h> +#include <binder/IServiceManager.h> +#include <binder/Stability.h> +#include <gtest/gtest.h> +#include <procpartition/procpartition.h> + +using namespace android; +using android::internal::Stability; // for testing only! +using android::procpartition::getPartition; +using android::procpartition::Partition; + +class BinderStabilityIntegrationTest : public testing::Test, + public ::testing::WithParamInterface<String16> { +public: + virtual ~BinderStabilityIntegrationTest() {} +}; + +TEST_P(BinderStabilityIntegrationTest, ExpectedStabilityForItsPartition) { + const String16& serviceName = GetParam(); + + sp<IBinder> binder = defaultServiceManager()->checkService(serviceName); + if (!binder) GTEST_SKIP() << "Could not get service, may have gone away."; + + pid_t pid; + status_t res = binder->getDebugPid(&pid); + if (res != OK) { + GTEST_SKIP() << "Could not talk to service to get PID, res: " << statusToString(res); + } + + Partition partition = getPartition(pid); + + Stability::Level level = Stability::Level::UNDECLARED; + switch (partition) { + case Partition::SYSTEM: + case Partition::SYSTEM_EXT: + level = Stability::Level::SYSTEM; + break; + case Partition::VENDOR: + case Partition::ODM: + level = Stability::Level::VENDOR; + break; + case Partition::UNKNOWN: + GTEST_SKIP() << "Not sure of partition of process."; + return; + default: + ADD_FAILURE() << "Unrecognized partition for service: " << partition; + return; + } + + ASSERT_TRUE(Stability::check(Stability::getRepr(binder.get()), level)) + << "Binder hosted on partition " << partition + << " should have corresponding stability set."; +} + +std::string PrintTestParam( + const testing::TestParamInfo<BinderStabilityIntegrationTest::ParamType>& info) { + std::string name = String8(info.param).c_str(); + for (size_t i = 0; i < name.size(); i++) { + bool alnum = false; + alnum |= (name[i] >= 'a' && name[i] <= 'z'); + alnum |= (name[i] >= 'A' && name[i] <= 'Z'); + alnum |= (name[i] >= '0' && name[i] <= '9'); + alnum |= (name[i] == '_'); + if (!alnum) name[i] = '_'; + } + + // index for uniqueness + return std::to_string(info.index) + "__" + name; +} + +INSTANTIATE_TEST_CASE_P(RegisteredServices, BinderStabilityIntegrationTest, + ::testing::ValuesIn(defaultServiceManager()->listServices()), + PrintTestParam); diff --git a/libs/binder/tests/binderUtilsHostTest.cpp b/libs/binder/tests/binderUtilsHostTest.cpp index 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..b2ba1ae38d 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; @@ -312,8 +317,6 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { PARCEL_READ_NO_STATUS(int, readParcelFileDescriptor), PARCEL_READ_WITH_STATUS(unique_fd, readUniqueFileDescriptor), - PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<unique_fd>>, - readUniqueFileDescriptorVector), PARCEL_READ_WITH_STATUS(std::optional<std::vector<unique_fd>>, readUniqueFileDescriptorVector), PARCEL_READ_WITH_STATUS(std::vector<unique_fd>, readUniqueFileDescriptorVector), diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp index 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/binderdebug/stats.cpp b/libs/binderdebug/stats.cpp index 9c26afaa97..972fbd5295 100644 --- a/libs/binderdebug/stats.cpp +++ b/libs/binderdebug/stats.cpp @@ -22,9 +22,9 @@ #include <inttypes.h> -namespace android { +int main() { + using namespace android; -extern "C" int main() { // ignore args - we only print csv // we should use a csv library here for escaping, because @@ -58,5 +58,3 @@ extern "C" int main() { } return 0; } - -} // namespace android diff --git a/libs/binderdebug/tests/binderdebug_test.cpp b/libs/binderdebug/tests/binderdebug_test.cpp index ea799c06a2..ad2b581abc 100644 --- a/libs/binderdebug/tests/binderdebug_test.cpp +++ b/libs/binderdebug/tests/binderdebug_test.cpp @@ -60,8 +60,15 @@ TEST(BinderDebugTests, BinderThreads) { EXPECT_GE(pidInfo.threadCount, 1); } -extern "C" { +} // namespace test +} // namespace binderdebug +} // namespace android + int main(int argc, char** argv) { + using namespace android; + using namespace android::binderdebug; + using namespace android::binderdebug::test; + ::testing::InitGoogleTest(&argc, argv); // Create a child/client process to call into the main process so we can ensure @@ -84,7 +91,3 @@ int main(int argc, char** argv) { return RUN_ALL_TESTS(); } -} // extern "C" -} // namespace test -} // namespace binderdebug -} // namespace android diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp index 368f5e079c..08ce855c2b 100644 --- a/libs/ftl/Android.bp +++ b/libs/ftl/Android.bp @@ -21,6 +21,7 @@ cc_test { "enum_test.cpp", "expected_test.cpp", "fake_guard_test.cpp", + "finalizer_test.cpp", "flags_test.cpp", "function_test.cpp", "future_test.cpp", diff --git a/libs/ftl/finalizer_test.cpp b/libs/ftl/finalizer_test.cpp new file mode 100644 index 0000000000..4f5c2258db --- /dev/null +++ b/libs/ftl/finalizer_test.cpp @@ -0,0 +1,209 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <memory> +#include <type_traits> +#include <utility> + +#include <ftl/finalizer.h> +#include <gtest/gtest.h> + +namespace android::test { + +namespace { + +struct Counter { + constexpr auto increment_fn() { + return [this] { ++value_; }; + } + + auto increment_finalizer() { + return ftl::Finalizer([this] { ++value_; }); + } + + [[nodiscard]] constexpr auto value() const -> int { return value_; } + + private: + int value_ = 0; +}; + +struct CounterPair { + constexpr auto increment_first_fn() { return first.increment_fn(); } + constexpr auto increment_second_fn() { return second.increment_fn(); } + [[nodiscard]] constexpr auto values() const -> std::pair<int, int> { + return {first.value(), second.value()}; + } + + private: + Counter first; + Counter second; +}; + +} // namespace + +TEST(Finalizer, DefaultConstructionAndNoOpDestructionWhenPolymorphicType) { + ftl::FinalizerStd finalizer1; + ftl::FinalizerFtl finalizer2; + ftl::FinalizerFtl1 finalizer3; + ftl::FinalizerFtl2 finalizer4; + ftl::FinalizerFtl3 finalizer5; +} + +TEST(Finalizer, InvokesTheFunctionOnDestruction) { + Counter counter; + { + const auto finalizer = counter.increment_finalizer(); + EXPECT_EQ(counter.value(), 0); + } + EXPECT_EQ(counter.value(), 1); +} + +TEST(Finalizer, InvocationCanBeCanceled) { + Counter counter; + { + auto finalizer = counter.increment_finalizer(); + EXPECT_EQ(counter.value(), 0); + finalizer.cancel(); + EXPECT_EQ(counter.value(), 0); + } + EXPECT_EQ(counter.value(), 0); +} + +TEST(Finalizer, InvokesTheFunctionOnce) { + Counter counter; + { + auto finalizer = counter.increment_finalizer(); + EXPECT_EQ(counter.value(), 0); + finalizer(); + EXPECT_EQ(counter.value(), 1); + finalizer(); + EXPECT_EQ(counter.value(), 1); + } + EXPECT_EQ(counter.value(), 1); +} + +TEST(Finalizer, SelfInvocationIsAllowedAndANoOp) { + Counter counter; + ftl::FinalizerStd finalizer; + finalizer = ftl::Finalizer([&]() { + counter.increment_fn()(); + finalizer(); // recursive invocation should do nothing. + }); + EXPECT_EQ(counter.value(), 0); + finalizer(); + EXPECT_EQ(counter.value(), 1); +} + +TEST(Finalizer, MoveConstruction) { + Counter counter; + { + ftl::FinalizerStd outer_finalizer = counter.increment_finalizer(); + EXPECT_EQ(counter.value(), 0); + { + ftl::FinalizerStd inner_finalizer = std::move(outer_finalizer); + static_assert(std::is_same_v<decltype(inner_finalizer), decltype(outer_finalizer)>); + EXPECT_EQ(counter.value(), 0); + } + EXPECT_EQ(counter.value(), 1); + } + EXPECT_EQ(counter.value(), 1); +} + +TEST(Finalizer, MoveConstructionWithImplicitConversion) { + Counter counter; + { + auto outer_finalizer = counter.increment_finalizer(); + EXPECT_EQ(counter.value(), 0); + { + ftl::FinalizerStd inner_finalizer = std::move(outer_finalizer); + static_assert(!std::is_same_v<decltype(inner_finalizer), decltype(outer_finalizer)>); + EXPECT_EQ(counter.value(), 0); + } + EXPECT_EQ(counter.value(), 1); + } + EXPECT_EQ(counter.value(), 1); +} + +TEST(Finalizer, MoveAssignment) { + CounterPair pair; + { + ftl::FinalizerStd outer_finalizer = ftl::Finalizer(pair.increment_first_fn()); + EXPECT_EQ(pair.values(), std::make_pair(0, 0)); + + { + ftl::FinalizerStd inner_finalizer = ftl::Finalizer(pair.increment_second_fn()); + static_assert(std::is_same_v<decltype(inner_finalizer), decltype(outer_finalizer)>); + EXPECT_EQ(pair.values(), std::make_pair(0, 0)); + inner_finalizer = std::move(outer_finalizer); + EXPECT_EQ(pair.values(), std::make_pair(0, 1)); + } + EXPECT_EQ(pair.values(), std::make_pair(1, 1)); + } + EXPECT_EQ(pair.values(), std::make_pair(1, 1)); +} + +TEST(Finalizer, MoveAssignmentWithImplicitConversion) { + CounterPair pair; + { + auto outer_finalizer = ftl::Finalizer(pair.increment_first_fn()); + EXPECT_EQ(pair.values(), std::make_pair(0, 0)); + + { + ftl::FinalizerStd inner_finalizer = ftl::Finalizer(pair.increment_second_fn()); + static_assert(!std::is_same_v<decltype(inner_finalizer), decltype(outer_finalizer)>); + EXPECT_EQ(pair.values(), std::make_pair(0, 0)); + inner_finalizer = std::move(outer_finalizer); + EXPECT_EQ(pair.values(), std::make_pair(0, 1)); + } + EXPECT_EQ(pair.values(), std::make_pair(1, 1)); + } + EXPECT_EQ(pair.values(), std::make_pair(1, 1)); +} + +TEST(Finalizer, NullifiesTheFunctionWhenInvokedIfPossible) { + auto shared = std::make_shared<int>(0); + std::weak_ptr<int> weak = shared; + + int count = 0; + { + auto lambda = [capture = std::move(shared)]() {}; + auto finalizer = ftl::Finalizer(std::move(lambda)); + EXPECT_FALSE(weak.expired()); + + // A lambda is not nullable. Invoking the finalizer cannot destroy it to destroy the lambda's + // capture. + finalizer(); + EXPECT_FALSE(weak.expired()); + } + // The lambda is only destroyed when the finalizer instance is destroyed. + EXPECT_TRUE(weak.expired()); + + shared = std::make_shared<int>(0); + weak = shared; + + { + auto lambda = [capture = std::move(shared)]() {}; + auto finalizer = ftl::FinalizerStd(std::move(lambda)); + EXPECT_FALSE(weak.expired()); + + // Since std::function is used, and is nullable, invoking the finalizer will destroy the + // contained function, which will destroy the lambda's capture. + finalizer(); + EXPECT_TRUE(weak.expired()); + } +} + +} // namespace android::test diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp index a8d5fe7371..4874dbde9c 100644 --- a/libs/graphicsenv/GraphicsEnv.cpp +++ b/libs/graphicsenv/GraphicsEnv.cpp @@ -596,7 +596,7 @@ bool GraphicsEnv::shouldUseAngle() { // If path is set to nonempty and shouldUseNativeDriver is true, ANGLE will be used regardless. void GraphicsEnv::setAngleInfo(const std::string& path, const bool shouldUseNativeDriver, const std::string& packageName, - const std::vector<std::string> eglFeatures) { + const std::vector<std::string>& eglFeatures) { if (mShouldUseAngle) { // ANGLE is already set up for this application process, even if the application // needs to switch from apk to system or vice versa, the application process must @@ -606,11 +606,11 @@ void GraphicsEnv::setAngleInfo(const std::string& path, const bool shouldUseNati return; } - mAngleEglFeatures = std::move(eglFeatures); + mAngleEglFeatures = eglFeatures; ALOGV("setting ANGLE path to '%s'", path.c_str()); - mAnglePath = std::move(path); + mAnglePath = path; ALOGV("setting app package name to '%s'", packageName.c_str()); - mPackageName = std::move(packageName); + mPackageName = packageName; if (mAnglePath == "system") { mShouldUseSystemAngle = true; } diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h index b0ab0b9d22..452e48bb75 100644 --- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h +++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h @@ -114,7 +114,7 @@ public: // If shouldUseNativeDriver is true, it means native GLES drivers must be used for the process. // If path is set to nonempty and shouldUseNativeDriver is true, ANGLE will be used regardless. void setAngleInfo(const std::string& path, const bool shouldUseNativeDriver, - const std::string& packageName, const std::vector<std::string> eglFeatures); + const std::string& packageName, const std::vector<std::string>& eglFeatures); // Get the ANGLE driver namespace. android_namespace_t* getAngleNamespace(); // Get the app package name. diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index c1a03fcfea..44aac9bfae 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -66,6 +66,8 @@ layer_state_t::layer_state_t() mask(0), reserved(0), cornerRadius(0.0f), + clientDrawnCornerRadius(0.0f), + clientDrawnShadowRadius(0.0f), backgroundBlurRadius(0), color(0), bufferTransform(0), @@ -140,6 +142,8 @@ status_t layer_state_t::write(Parcel& output) const SAFE_PARCEL(output.write, colorTransform.asArray(), 16 * sizeof(float)); SAFE_PARCEL(output.writeFloat, cornerRadius); + SAFE_PARCEL(output.writeFloat, clientDrawnCornerRadius); + SAFE_PARCEL(output.writeFloat, clientDrawnShadowRadius); SAFE_PARCEL(output.writeUint32, backgroundBlurRadius); SAFE_PARCEL(output.writeParcelable, metadata); SAFE_PARCEL(output.writeFloat, bgColor.r); @@ -274,6 +278,8 @@ status_t layer_state_t::read(const Parcel& input) SAFE_PARCEL(input.read, &colorTransform, 16 * sizeof(float)); SAFE_PARCEL(input.readFloat, &cornerRadius); + SAFE_PARCEL(input.readFloat, &clientDrawnCornerRadius); + SAFE_PARCEL(input.readFloat, &clientDrawnShadowRadius); SAFE_PARCEL(input.readUint32, &backgroundBlurRadius); SAFE_PARCEL(input.readParcelable, &metadata); @@ -596,6 +602,14 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eCornerRadiusChanged; cornerRadius = other.cornerRadius; } + if (other.what & eClientDrawnCornerRadiusChanged) { + what |= eClientDrawnCornerRadiusChanged; + clientDrawnCornerRadius = other.clientDrawnCornerRadius; + } + if (other.what & eClientDrawnShadowsChanged) { + what |= eClientDrawnShadowsChanged; + clientDrawnShadowRadius = other.clientDrawnShadowRadius; + } if (other.what & eBackgroundBlurRadiusChanged) { what |= eBackgroundBlurRadiusChanged; backgroundBlurRadius = other.backgroundBlurRadius; @@ -809,6 +823,8 @@ uint64_t layer_state_t::diff(const layer_state_t& other) const { } CHECK_DIFF(diff, eLayerStackChanged, other, layerStack); CHECK_DIFF(diff, eCornerRadiusChanged, other, cornerRadius); + CHECK_DIFF(diff, eClientDrawnCornerRadiusChanged, other, clientDrawnCornerRadius); + CHECK_DIFF(diff, eClientDrawnShadowsChanged, other, clientDrawnShadowRadius); CHECK_DIFF(diff, eBackgroundBlurRadiusChanged, other, backgroundBlurRadius); if (other.what & eBlurRegionsChanged) diff |= eBlurRegionsChanged; if (other.what & eRelativeLayerChanged) { diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index cabde22c6d..5bb8f7f8f7 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -829,9 +829,7 @@ SurfaceComposerClient::Transaction::Transaction() { SurfaceComposerClient::Transaction::Transaction(const Transaction& other) : mId(other.mId), - mAnimation(other.mAnimation), - mEarlyWakeupStart(other.mEarlyWakeupStart), - mEarlyWakeupEnd(other.mEarlyWakeupEnd), + mFlags(other.mFlags), mMayContainBuffer(other.mMayContainBuffer), mDesiredPresentTime(other.mDesiredPresentTime), mIsAutoTimestamp(other.mIsAutoTimestamp), @@ -868,9 +866,7 @@ SurfaceComposerClient::Transaction::createFromParcel(const Parcel* parcel) { status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel) { const uint64_t transactionId = parcel->readUint64(); - const bool animation = parcel->readBool(); - const bool earlyWakeupStart = parcel->readBool(); - const bool earlyWakeupEnd = parcel->readBool(); + const uint32_t flags = parcel->readUint32(); const int64_t desiredPresentTime = parcel->readInt64(); const bool isAutoTimestamp = parcel->readBool(); const bool logCallPoints = parcel->readBool(); @@ -965,9 +961,7 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel // Parsing was successful. Update the object. mId = transactionId; - mAnimation = animation; - mEarlyWakeupStart = earlyWakeupStart; - mEarlyWakeupEnd = earlyWakeupEnd; + mFlags = flags; mDesiredPresentTime = desiredPresentTime; mIsAutoTimestamp = isAutoTimestamp; mFrameTimelineInfo = frameTimelineInfo; @@ -996,9 +990,7 @@ status_t SurfaceComposerClient::Transaction::writeToParcel(Parcel* parcel) const const_cast<SurfaceComposerClient::Transaction*>(this)->cacheBuffers(); parcel->writeUint64(mId); - parcel->writeBool(mAnimation); - parcel->writeBool(mEarlyWakeupStart); - parcel->writeBool(mEarlyWakeupEnd); + parcel->writeUint32(mFlags); parcel->writeInt64(mDesiredPresentTime); parcel->writeBool(mIsAutoTimestamp); parcel->writeBool(mLogCallPoints); @@ -1131,8 +1123,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Tr mInputWindowCommands.merge(other.mInputWindowCommands); mMayContainBuffer |= other.mMayContainBuffer; - mEarlyWakeupStart = mEarlyWakeupStart || other.mEarlyWakeupStart; - mEarlyWakeupEnd = mEarlyWakeupEnd || other.mEarlyWakeupEnd; + mFlags |= other.mFlags; mApplyToken = other.mApplyToken; mergeFrameTimelineInfo(mFrameTimelineInfo, other.mFrameTimelineInfo); @@ -1154,15 +1145,13 @@ void SurfaceComposerClient::Transaction::clear() { mInputWindowCommands.clear(); mUncacheBuffers.clear(); mMayContainBuffer = false; - mAnimation = false; - mEarlyWakeupStart = false; - mEarlyWakeupEnd = false; mDesiredPresentTime = 0; mIsAutoTimestamp = true; mFrameTimelineInfo = {}; mApplyToken = nullptr; mMergedTransactionIds.clear(); mLogCallPoints = false; + mFlags = 0; } uint64_t SurfaceComposerClient::Transaction::getId() { @@ -1333,9 +1322,6 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay displayStates = std::move(mDisplayStates); - if (mAnimation) { - flags |= ISurfaceComposer::eAnimation; - } if (oneWay) { if (synchronous) { ALOGE("Transaction attempted to set synchronous and one way at the same time" @@ -1345,15 +1331,12 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay } } - // If both mEarlyWakeupStart and mEarlyWakeupEnd are set + // If both ISurfaceComposer::eEarlyWakeupStart and ISurfaceComposer::eEarlyWakeupEnd are set // it is equivalent for none - if (mEarlyWakeupStart && !mEarlyWakeupEnd) { - flags |= ISurfaceComposer::eEarlyWakeupStart; - } - if (mEarlyWakeupEnd && !mEarlyWakeupStart) { - flags |= ISurfaceComposer::eEarlyWakeupEnd; + uint32_t wakeupFlags = ISurfaceComposer::eEarlyWakeupStart | ISurfaceComposer::eEarlyWakeupEnd; + if ((flags & wakeupFlags) == wakeupFlags) { + flags &= ~(wakeupFlags); } - sp<IBinder> applyToken = mApplyToken ? mApplyToken : getDefaultApplyToken(); sp<ISurfaceComposer> sf(ComposerService::getComposerService()); @@ -1461,15 +1444,15 @@ std::optional<gui::StalledTransactionInfo> SurfaceComposerClient::getStalledTran } void SurfaceComposerClient::Transaction::setAnimationTransaction() { - mAnimation = true; + mFlags |= ISurfaceComposer::eAnimation; } void SurfaceComposerClient::Transaction::setEarlyWakeupStart() { - mEarlyWakeupStart = true; + mFlags |= ISurfaceComposer::eEarlyWakeupStart; } void SurfaceComposerClient::Transaction::setEarlyWakeupEnd() { - mEarlyWakeupEnd = true; + mFlags |= ISurfaceComposer::eEarlyWakeupEnd; } layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<SurfaceControl>& sc) { @@ -1695,6 +1678,29 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCorne return *this; } +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setClientDrawnCornerRadius( + const sp<SurfaceControl>& sc, float clientDrawnCornerRadius) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eClientDrawnCornerRadiusChanged; + s->clientDrawnCornerRadius = clientDrawnCornerRadius; + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setClientDrawnShadowRadius( + const sp<SurfaceControl>& sc, float clientDrawnShadowRadius) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eClientDrawnShadowsChanged; + s->clientDrawnShadowRadius = clientDrawnShadowRadius; + return *this; +} SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBackgroundBlurRadius( const sp<SurfaceControl>& sc, int backgroundBlurRadius) { layer_state_t* s = getLayerState(sc); diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp index 82d2554340..3fb66d1f33 100644 --- a/libs/gui/WindowInfo.cpp +++ b/libs/gui/WindowInfo.cpp @@ -59,6 +59,32 @@ std::ostream& operator<<(std::ostream& out, const Region& region) { return out; } +status_t writeTransform(android::Parcel* parcel, const ui::Transform& transform) { + return parcel->writeFloat(transform.dsdx()) ?: + parcel->writeFloat(transform.dtdx()) ?: + parcel->writeFloat(transform.tx()) ?: + parcel->writeFloat(transform.dtdy()) ?: + parcel->writeFloat(transform.dsdy()) ?: + parcel->writeFloat(transform.ty()); +} + +status_t readTransform(const android::Parcel* parcel, ui::Transform& transform) { + float dsdx, dtdx, tx, dtdy, dsdy, ty; + + const status_t status = parcel->readFloat(&dsdx) ?: + parcel->readFloat(&dtdx) ?: + parcel->readFloat(&tx) ?: + parcel->readFloat(&dtdy) ?: + parcel->readFloat(&dsdy) ?: + parcel->readFloat(&ty); + if (status != OK) { + return status; + } + + transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1}); + return OK; +} + } // namespace void WindowInfo::setInputConfig(ftl::Flags<InputConfig> config, bool value) { @@ -73,10 +99,6 @@ void WindowInfo::addTouchableRegion(const Rect& region) { touchableRegion.orSelf(region); } -bool WindowInfo::supportsSplitTouch() const { - return !inputConfig.test(InputConfig::PREVENT_SPLITTING); -} - bool WindowInfo::isSpy() const { return inputConfig.test(InputConfig::SPY); } @@ -135,12 +157,7 @@ status_t WindowInfo::writeToParcel(android::Parcel* parcel) const { parcel->writeInt32(surfaceInset) ?: parcel->writeFloat(globalScaleFactor) ?: parcel->writeFloat(alpha) ?: - parcel->writeFloat(transform.dsdx()) ?: - parcel->writeFloat(transform.dtdx()) ?: - parcel->writeFloat(transform.tx()) ?: - parcel->writeFloat(transform.dtdy()) ?: - parcel->writeFloat(transform.dsdy()) ?: - parcel->writeFloat(transform.ty()) ?: + writeTransform(parcel, transform) ?: parcel->writeInt32(static_cast<int32_t>(touchOcclusionMode)) ?: parcel->writeInt32(ownerPid.val()) ?: parcel->writeInt32(ownerUid.val()) ?: @@ -153,8 +170,12 @@ status_t WindowInfo::writeToParcel(android::Parcel* parcel) const { parcel->writeStrongBinder(touchableRegionCropHandle.promote()) ?: parcel->writeStrongBinder(windowToken) ?: parcel->writeStrongBinder(focusTransferTarget) ?: - parcel->writeBool(canOccludePresentation); + parcel->writeBool(canOccludePresentation) ?: + parcel->writeBool(cloneLayerStackTransform.has_value()); // clang-format on + if (cloneLayerStackTransform) { + status = status ?: writeTransform(parcel, *cloneLayerStackTransform); + } return status; } @@ -174,10 +195,10 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) { return status; } - float dsdx, dtdx, tx, dtdy, dsdy, ty; int32_t lpFlags, lpType, touchOcclusionModeInt, inputConfigInt, ownerPidInt, ownerUidInt, displayIdInt; sp<IBinder> touchableRegionCropHandleSp; + bool hasCloneLayerStackTransform = false; // clang-format off status = parcel->readInt32(&lpFlags) ?: @@ -188,12 +209,7 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) { parcel->readInt32(&surfaceInset) ?: parcel->readFloat(&globalScaleFactor) ?: parcel->readFloat(&alpha) ?: - parcel->readFloat(&dsdx) ?: - parcel->readFloat(&dtdx) ?: - parcel->readFloat(&tx) ?: - parcel->readFloat(&dtdy) ?: - parcel->readFloat(&dsdy) ?: - parcel->readFloat(&ty) ?: + readTransform(parcel, /*byRef*/ transform) ?: parcel->readInt32(&touchOcclusionModeInt) ?: parcel->readInt32(&ownerPidInt) ?: parcel->readInt32(&ownerUidInt) ?: @@ -206,8 +222,8 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) { parcel->readNullableStrongBinder(&touchableRegionCropHandleSp) ?: parcel->readNullableStrongBinder(&windowToken) ?: parcel->readNullableStrongBinder(&focusTransferTarget) ?: - parcel->readBool(&canOccludePresentation); - + parcel->readBool(&canOccludePresentation)?: + parcel->readBool(&hasCloneLayerStackTransform); // clang-format on if (status != OK) { @@ -216,7 +232,6 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) { layoutParamsFlags = ftl::Flags<Flag>(lpFlags); layoutParamsType = static_cast<Type>(lpType); - transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1}); touchOcclusionMode = static_cast<TouchOcclusionMode>(touchOcclusionModeInt); inputConfig = ftl::Flags<InputConfig>(inputConfigInt); ownerPid = Pid{ownerPidInt}; @@ -224,6 +239,15 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) { touchableRegionCropHandle = touchableRegionCropHandleSp; displayId = ui::LogicalDisplayId{displayIdInt}; + cloneLayerStackTransform = + hasCloneLayerStackTransform ? std::make_optional<ui::Transform>() : std::nullopt; + if (cloneLayerStackTransform) { + status = readTransform(parcel, /*byRef*/ *cloneLayerStackTransform); + if (status != OK) { + return status; + } + } + return OK; } diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index 64f191b867..16425c9872 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -231,6 +231,8 @@ struct layer_state_t { eBufferReleaseChannelChanged = 0x40000'00000000, ePictureProfileHandleChanged = 0x80000'00000000, eAppContentPriorityChanged = 0x100000'00000000, + eClientDrawnCornerRadiusChanged = 0x200000'00000000, + eClientDrawnShadowsChanged = 0x400000'00000000, }; layer_state_t(); @@ -251,9 +253,9 @@ struct layer_state_t { // Geometry updates. static constexpr uint64_t GEOMETRY_CHANGES = layer_state_t::eBufferCropChanged | layer_state_t::eBufferTransformChanged | layer_state_t::eCornerRadiusChanged | - layer_state_t::eCropChanged | layer_state_t::eDestinationFrameChanged | - layer_state_t::eMatrixChanged | layer_state_t::ePositionChanged | - layer_state_t::eTransformToDisplayInverseChanged | + layer_state_t::eClientDrawnCornerRadiusChanged | layer_state_t::eCropChanged | + layer_state_t::eDestinationFrameChanged | layer_state_t::eMatrixChanged | + layer_state_t::ePositionChanged | layer_state_t::eTransformToDisplayInverseChanged | layer_state_t::eTransparentRegionChanged | layer_state_t::eEdgeExtensionChanged; // Buffer and related updates. @@ -274,8 +276,8 @@ struct layer_state_t { layer_state_t::eColorSpaceAgnosticChanged | layer_state_t::eColorTransformChanged | layer_state_t::eCornerRadiusChanged | layer_state_t::eDimmingEnabledChanged | layer_state_t::eHdrMetadataChanged | layer_state_t::eShadowRadiusChanged | - layer_state_t::eStretchChanged | layer_state_t::ePictureProfileHandleChanged | - layer_state_t::eAppContentPriorityChanged; + layer_state_t::eClientDrawnShadowsChanged | layer_state_t::eStretchChanged | + layer_state_t::ePictureProfileHandleChanged | layer_state_t::eAppContentPriorityChanged; // Changes which invalidates the layer's visible region in CE. static constexpr uint64_t CONTENT_DIRTY = layer_state_t::CONTENT_CHANGES | @@ -328,6 +330,8 @@ struct layer_state_t { uint8_t reserved; matrix22_t matrix; float cornerRadius; + float clientDrawnCornerRadius; + float clientDrawnShadowRadius; uint32_t backgroundBlurRadius; sp<SurfaceControl> relativeLayerSurfaceControl; diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 0f66c8b492..10c51a3e20 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -467,10 +467,7 @@ public: std::vector<uint64_t> mMergedTransactionIds; uint64_t mId; - - bool mAnimation = false; - bool mEarlyWakeupStart = false; - bool mEarlyWakeupEnd = false; + uint32_t mFlags; // Indicates that the Transaction may contain buffers that should be cached. The reason this // is only a guess is that buffers can be removed before cache is called. This is only a @@ -567,6 +564,15 @@ public: Transaction& setCrop(const sp<SurfaceControl>& sc, const Rect& crop); Transaction& setCrop(const sp<SurfaceControl>& sc, const FloatRect& crop); Transaction& setCornerRadius(const sp<SurfaceControl>& sc, float cornerRadius); + // Sets the client drawn corner radius for the layer. If both a corner radius and a client + // radius are sent to SF, the client radius will be used. This indicates that the corner + // radius is drawn by the client and not SurfaceFlinger. + Transaction& setClientDrawnCornerRadius(const sp<SurfaceControl>& sc, + float clientDrawnCornerRadius); + // Sets the client drawn shadow radius for the layer. This indicates that the shadows + // are drawn by the client and not SurfaceFlinger. + Transaction& setClientDrawnShadowRadius(const sp<SurfaceControl>& sc, + float clientDrawnShadowRadius); Transaction& setBackgroundBlurRadius(const sp<SurfaceControl>& sc, int backgroundBlurRadius); Transaction& setBlurRegions(const sp<SurfaceControl>& sc, diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h index eb3be5588a..420dc2103f 100644 --- a/libs/gui/include/gui/WindowInfo.h +++ b/libs/gui/include/gui/WindowInfo.h @@ -150,8 +150,6 @@ struct WindowInfo : public Parcelable { static_cast<uint32_t>(os::InputConfig::NOT_FOCUSABLE), NOT_TOUCHABLE = static_cast<uint32_t>(os::InputConfig::NOT_TOUCHABLE), - PREVENT_SPLITTING = - static_cast<uint32_t>(os::InputConfig::PREVENT_SPLITTING), DUPLICATE_TOUCH_TO_WALLPAPER = static_cast<uint32_t>(os::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER), IS_WALLPAPER = @@ -220,9 +218,14 @@ struct WindowInfo : public Parcelable { // An alpha of 1.0 means fully opaque and 0.0 means fully transparent. float alpha; - // Transform applied to individual windows. + // Transform applied to individual windows for input. + // Maps display coordinates to the window's input coordinate space. ui::Transform transform; + // Transform applied to get to the layer stack space of the cloned window for input. + // Maps display coordinates of the clone window to the layer stack space of the cloned window. + std::optional<ui::Transform> cloneLayerStackTransform; + /* * This is filled in by the WM relative to the frame and then translated * to absolute coordinates by SurfaceFlinger once the frame is computed. diff --git a/libs/gui/tests/WindowInfo_test.cpp b/libs/gui/tests/WindowInfo_test.cpp index ce22082a9f..e3f9a07b34 100644 --- a/libs/gui/tests/WindowInfo_test.cpp +++ b/libs/gui/tests/WindowInfo_test.cpp @@ -40,7 +40,18 @@ TEST(WindowInfo, ParcellingWithoutToken) { ASSERT_EQ(OK, i.writeToParcel(&p)); p.setDataPosition(0); i2.readFromParcel(&p); - ASSERT_TRUE(i2.token == nullptr); + ASSERT_EQ(i2.token, nullptr); +} + +TEST(WindowInfo, ParcellingWithoutCloneTransform) { + WindowInfo i, i2; + i.cloneLayerStackTransform.reset(); + + Parcel p; + ASSERT_EQ(OK, i.writeToParcel(&p)); + p.setDataPosition(0); + i2.readFromParcel(&p); + ASSERT_EQ(i2.cloneLayerStackTransform, std::nullopt); } TEST(WindowInfo, Parcelling) { @@ -71,6 +82,8 @@ TEST(WindowInfo, Parcelling) { i.applicationInfo.token = new BBinder(); i.applicationInfo.dispatchingTimeoutMillis = 0x12345678ABCD; i.focusTransferTarget = new BBinder(); + i.cloneLayerStackTransform = ui::Transform(); + i.cloneLayerStackTransform->set({5, -1, 100, 4, 0, 40, 0, 0, 1}); Parcel p; i.writeToParcel(&p); @@ -100,6 +113,7 @@ TEST(WindowInfo, Parcelling) { ASSERT_EQ(i.touchableRegionCropHandle, i2.touchableRegionCropHandle); ASSERT_EQ(i.applicationInfo, i2.applicationInfo); ASSERT_EQ(i.focusTransferTarget, i2.focusTransferTarget); + ASSERT_EQ(i.cloneLayerStackTransform, i2.cloneLayerStackTransform); } TEST(InputApplicationInfo, Parcelling) { diff --git a/libs/input/android/os/InputConfig.aidl b/libs/input/android/os/InputConfig.aidl index da62e03821..e5f7b56561 100644 --- a/libs/input/android/os/InputConfig.aidl +++ b/libs/input/android/os/InputConfig.aidl @@ -57,16 +57,9 @@ enum InputConfig { NOT_TOUCHABLE = 1 << 3, /** - * Indicates that this window will not accept a touch event that is split between - * more than one window. When set: - * - If this window receives a DOWN event with the first pointer, all successive - * pointers that go down, regardless of their location on the screen, will be - * directed to this window; - * - If the DOWN event lands outside the touchable bounds of this window, no - * successive pointers that go down, regardless of their location on the screen, - * will be directed to this window. - */ - PREVENT_SPLITTING = 1 << 4, + * This flag is now deprecated and should not be used. + */ + DEPRECATED_PREVENT_SPLITTING = 1 << 4, /** * Indicates that this window shows the wallpaper behind it, so all touch events diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig index 6cdd249b9e..bf928f4847 100644 --- a/libs/input/input_flags.aconfig +++ b/libs/input/input_flags.aconfig @@ -37,9 +37,9 @@ flag { } flag { - name: "split_all_touches" + name: "deprecate_split_touch_apis" namespace: "input" - description: "Set FLAG_SPLIT_TOUCHES to true for all windows, regardless of what they specify. This is essentially deprecating this flag by forcefully enabling the split functionality" + description: "Deprecate all public APIs related to split touch because now all windows behave as if split touch is permanently enabled and there's no way for a window to disable split touch." bug: "239934827" } @@ -188,6 +188,16 @@ flag { } flag { + name: "disable_touch_input_mapper_pointer_usage" + namespace: "input" + description: "Disable the PointerUsage concept in TouchInputMapper since the old touchpad stack is no longer used." + bug: "281840344" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "keyboard_repeat_keys" namespace: "input" description: "Allow user to enable key repeats or configure timeout before key repeat and key repeat delay rates." @@ -224,3 +234,13 @@ flag { description: "Allow cursor to transition across multiple connected displays" bug: "362719483" } + +flag { + name: "use_cloned_screen_coordinates_as_raw" + namespace: "input" + description: "Use the cloned window's layer stack (screen) space as the raw coordinate space for input going to clones" + bug: "377846505" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/libs/renderengine/skia/compat/GraphiteGpuContext.cpp b/libs/renderengine/skia/compat/GraphiteGpuContext.cpp index 69f583226b..7a72d09804 100644 --- a/libs/renderengine/skia/compat/GraphiteGpuContext.cpp +++ b/libs/renderengine/skia/compat/GraphiteGpuContext.cpp @@ -110,8 +110,40 @@ bool GraphiteGpuContext::isAbandonedOrDeviceLost() { return mContext->isDeviceLost(); } +void GraphiteGpuContext::setResourceCacheLimit(size_t maxResourceBytes) { + // Graphite has a separate budget for its Context and its Recorder. For now the majority of + // memory that Graphite will allocate will be on the Recorder and minimal amount on the Context. + // The main allocations on the Context are MSAA buffers (not often, if ever used in + // RenderEngine) and stencil buffers. However, both of these should be "memoryless" in Vulkan on + // tiled GPUs, so they don't actually use GPU memory. However, in Vulkan there are scenarios + // where Vulkan could end up using real memory for them. Skia will regularly query the device to + // get the real memory usage and update the budgeted appropriately. Though for all real usage + // patterns we don't expect to ever trigger the device to allocate real memory. + // + // Therefore, we set the full maxResourceBytes budget on the Recorder. However, in the rare + // chance that the devcies does allocate real memory we don't want to immediately kill device + // performance by constantly trashing allocations on the Context. Thus we set the Context's + // budget to be 50% of the total budget to make sure we allow the MSAA or Stencil buffers to be + // allocated in Skia and not immediately discarded. But even with this extra 50% budget, as + // described above, this shouldn't result in actual GPU memory usage. + // + // TODO: We will need to revise this strategy for GLES which does not have the same memoryless + // textures. + // TODO: Work in Graphite has started to move a lot more of its scratch resources to be owned + // by the Context and not on Recorders. This will mean most memory is actually owned by the + // Context and thus the budgeting here will need to be updated. + mContext->setMaxBudgetedBytes(maxResourceBytes / 2); + mRecorder->setMaxBudgetedBytes(maxResourceBytes); +} + +void GraphiteGpuContext::purgeUnlockedScratchResources() { + mContext->freeGpuResources(); + mRecorder->freeGpuResources(); +} + void GraphiteGpuContext::dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const { mContext->dumpMemoryStatistics(traceMemoryDump); + mRecorder->dumpMemoryStatistics(traceMemoryDump); } } // namespace android::renderengine::skia diff --git a/libs/renderengine/skia/compat/GraphiteGpuContext.h b/libs/renderengine/skia/compat/GraphiteGpuContext.h index 413817ffff..57da796af5 100644 --- a/libs/renderengine/skia/compat/GraphiteGpuContext.h +++ b/libs/renderengine/skia/compat/GraphiteGpuContext.h @@ -39,16 +39,10 @@ public: size_t getMaxRenderTargetSize() const override; size_t getMaxTextureSize() const override; bool isAbandonedOrDeviceLost() override; - // No-op (large resources like textures, surfaces, images, etc. created by clients don't count - // towards Graphite's internal caching budgets, so adjusting its limits based on display change - // events should be unnecessary. Additionally, Graphite doesn't expose many cache tweaking - // functions yet, as its design may evolve.) - void setResourceCacheLimit(size_t maxResourceBytes) override{}; - // TODO: b/293371537 - Triple-check and validate that no cleanup is necessary when switching - // contexts. - // No-op (unnecessary during context switch for Graphite's client-budgeted memory model). - void purgeUnlockedScratchResources() override{}; + void setResourceCacheLimit(size_t maxResourceBytes) override; + void purgeUnlockedScratchResources() override; + // No-op (only applicable to GL). void resetContextIfApplicable() override{}; diff --git a/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp b/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp index 750e08fa55..ef57c30563 100644 --- a/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp +++ b/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp @@ -118,9 +118,12 @@ sk_sp<SkImage> KawaseBlurDualFilter::generate(SkiaGpuContext* context, const uin 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)); + const auto newW = ceil(static_cast<float>(blurRect.width() / scale)); + const auto newH = ceil(static_cast<float>(blurRect.height() / scale)); + sk_sp<SkSurface> surface = + context->createRenderTarget(input->imageInfo().makeWH(newW, newH)); + LOG_ALWAYS_FATAL_IF(!surface, "%s: Failed to create surface for blurring!", __func__); + return surface; }; // Render into surfaces downscaled by 1x, 2x, and 4x from the initial downscale. diff --git a/libs/ui/include/ui/ShadowSettings.h b/libs/ui/include/ui/ShadowSettings.h index c0b83b8691..06be6dbbf5 100644 --- a/libs/ui/include/ui/ShadowSettings.h +++ b/libs/ui/include/ui/ShadowSettings.h @@ -46,6 +46,9 @@ struct ShadowSettings { // Length of the cast shadow. If length is <= 0.f no shadows will be drawn. float length = 0.f; + // Length of the cast shadow that is drawn by the client. + float clientDrawnLength = 0.f; + // If true fill in the casting layer is translucent and the shadow needs to fill the bounds. // Otherwise the shadow will only be drawn around the edges of the casting layer. bool casterIsTranslucent = false; @@ -55,6 +58,7 @@ static inline bool operator==(const ShadowSettings& lhs, const ShadowSettings& r return lhs.boundaries == rhs.boundaries && lhs.ambientColor == rhs.ambientColor && lhs.spotColor == rhs.spotColor && lhs.lightPos == rhs.lightPos && lhs.lightRadius == rhs.lightRadius && lhs.length == rhs.length && + lhs.clientDrawnLength == rhs.clientDrawnLength && lhs.casterIsTranslucent == rhs.casterIsTranslucent; } diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index cd4ed5c55b..fcd784d38a 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -46,6 +46,7 @@ #include <ctime> #include <queue> #include <sstream> +#include <variant> #include "../InputDeviceMetricsSource.h" @@ -417,7 +418,7 @@ std::unique_ptr<DispatchEntry> createDispatchEntry(const IdGenerator& idGenerato if (inputTarget.useDefaultPointerTransform() && !zeroCoords) { const ui::Transform& transform = inputTarget.getDefaultPointerTransform(); return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, transform, - inputTarget.displayTransform, + inputTarget.rawTransform, inputTarget.globalScaleFactor, uid, vsyncId, windowId); } @@ -438,7 +439,7 @@ std::unique_ptr<DispatchEntry> createDispatchEntry(const IdGenerator& idGenerato transform = &inputTarget.getTransformForPointer(firstMarkedBit(inputTarget.getPointerIds())); const ui::Transform inverseTransform = transform->inverse(); - displayTransform = &inputTarget.displayTransform; + displayTransform = &inputTarget.rawTransform; // Iterate through all pointers in the event to normalize against the first. for (size_t i = 0; i < motionEntry.getPointerCount(); i++) { @@ -787,38 +788,14 @@ void filterUntrustedTargets(TouchState& touchState, std::vector<InputTarget>& ta }); } -/** - * In general, touch should be always split between windows. Some exceptions: - * 1. Don't split touch if all of the below is true: - * (a) we have an active pointer down *and* - * (b) a new pointer is going down that's from the same device *and* - * (c) the window that's receiving the current pointer does not support split touch. - * 2. Don't split mouse events - */ -bool shouldSplitTouch(const TouchState& touchState, const MotionEntry& entry) { - if (isFromSource(entry.source, AINPUT_SOURCE_MOUSE)) { - // We should never split mouse events - return false; - } - for (const TouchedWindow& touchedWindow : touchState.windows) { - if (touchedWindow.windowHandle->getInfo()->isSpy()) { - // Spy windows should not affect whether or not touch is split. - continue; - } - if (touchedWindow.windowHandle->getInfo()->supportsSplitTouch()) { - continue; - } - if (touchedWindow.windowHandle->getInfo()->inputConfig.test( - gui::WindowInfo::InputConfig::IS_WALLPAPER)) { - // Wallpaper window should not affect whether or not touch is split - continue; - } - - if (touchedWindow.hasTouchingPointers(entry.deviceId)) { - return false; - } - } - return true; +bool shouldSplitTouch(int32_t source) { + // We should never split mouse events. This is because the events that are produced by touchpad + // are sent to InputDispatcher as two fingers (for example, pinch zoom), but they need to be + // dispatched to the same window. In those cases, the behaviour is also slightly different from + // default because the events should be sent to the cursor position rather than the x,y values + // of each of the fingers. + // The "normal" (uncaptured) events produced by touchpad and by mouse have SOURCE_MOUSE. + return !isFromSource(source, AINPUT_SOURCE_MOUSE); } /** @@ -924,6 +901,23 @@ 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& rawTransform, + std::optional<nsecs_t> firstDownTimeInTarget) { + LOG_ALWAYS_FATAL_IF(connection == nullptr); + InputTarget inputTarget{connection}; + inputTarget.windowHandle = windowHandle; + inputTarget.dispatchMode = dispatchMode; + inputTarget.flags = targetFlags; + inputTarget.globalScaleFactor = windowHandle->getInfo()->globalScaleFactor; + inputTarget.rawTransform = rawTransform; + inputTarget.firstDownTimeInTarget = firstDownTimeInTarget; + return inputTarget; +} + } // namespace // --- InputDispatcher --- @@ -980,7 +974,7 @@ InputDispatcher::~InputDispatcher() { while (!mConnectionsByToken.empty()) { std::shared_ptr<Connection> connection = mConnectionsByToken.begin()->second; - removeInputChannelLocked(connection->getToken(), /*notify=*/false); + removeInputChannelLocked(connection, /*notify=*/false); } } @@ -1128,7 +1122,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 +1327,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 +1340,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 +1447,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 +1474,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,28 +1494,18 @@ 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))) { - // Generally, we would skip any pointer that's outside of the window. However, if the - // spy prevents splitting, and already has some of the pointers from this device, then - // it should get more pointers from the same device, even if they are outside of that - // window - if (info.supportsSplitTouch()) { - continue; - } - - // We know that split touch is not supported. Skip this window only if it doesn't have - // any touching pointers for this device already. - if (!windowHasTouchingPointersLocked(windowHandle, deviceId)) { - continue; - } - // If it already has pointers down for this device, then give it this pointer, too. + if (!windowAcceptsTouchAt(info, displayId, x, y, isStylus, + getDisplayTransform(displayId))) { + // Skip if the pointer is outside of the window. + continue; } if (!info.isSpy()) { // The first touched non-spy window was found, so return the spy windows touched so far. @@ -1828,7 +1813,7 @@ void InputDispatcher::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 +2201,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 @@ -2427,7 +2412,7 @@ InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const Motio tempTouchState = *oldState; } - bool isSplit = shouldSplitTouch(tempTouchState, entry); + bool isSplit = shouldSplitTouch(entry.source); const bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE || maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER || @@ -2472,7 +2457,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); @@ -2480,17 +2465,6 @@ InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const Motio LOG_IF(INFO, newTouchedWindowHandle == nullptr) << "No new touched window at (" << std::format("{:.1f}, {:.1f}", x, y) << ") in display " << displayId; - // Handle the case where we did not find a window. - if (!input_flags::split_all_touches()) { - // If we are force splitting all touches, then touches outside of the window should - // be dropped, even if this device already has pointers down in another window. - if (newTouchedWindowHandle == nullptr) { - // Try to assign the pointer to the first foreground window we find, if there is - // one. - newTouchedWindowHandle = - tempTouchState.getFirstForegroundWindowHandle(entry.deviceId); - } - } // Verify targeted injection. if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) { @@ -2498,27 +2472,11 @@ InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const Motio return injectionError(InputEventInjectionResult::TARGET_MISMATCH); } - // Figure out whether splitting will be allowed for this window. - if (newTouchedWindowHandle != nullptr) { - if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) { - // New window supports splitting, but we should never split mouse events. - isSplit = !isFromMouse; - } else if (isSplit) { - // New window does not support splitting but we have already split events. - // Ignore the new window. - LOG(INFO) << "Skipping " << newTouchedWindowHandle->getName() - << " because it doesn't support split touch"; - newTouchedWindowHandle = nullptr; - } - } else { - // No window is touched, so set split to true. This will allow the next pointer down to - // be delivered to a new window which supports split touch. Pointers from a mouse device - // should never be split. - isSplit = !isFromMouse; - } + isSplit = !isFromMouse; std::vector<sp<WindowInfoHandle>> newTouchedWindows = - findTouchedSpyWindowsAtLocked(displayId, x, y, isStylus, entry.deviceId); + mWindowInfos.findTouchedSpyWindowsAt(displayId, x, y, isStylus, entry.deviceId, + mTouchStatesByDisplay); if (newTouchedWindowHandle != nullptr) { // Process the foreground window first so that it is the first to receive the event. newTouchedWindows.insert(newTouchedWindows.begin(), newTouchedWindowHandle); @@ -2655,7 +2613,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) { @@ -2689,9 +2647,7 @@ InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const Motio targets); // Make a slippery entrance into the new window. - if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) { - isSplit = !isFromMouse; - } + isSplit = !isFromMouse; ftl::Flags<InputTarget::Flags> targetFlags; if (canReceiveForegroundTouches(*newTouchedWindowHandle->getInfo())) { @@ -2779,7 +2735,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 +2835,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 +2891,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 +2927,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 +2941,16 @@ 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.getRawTransform(*windowHandle->getInfo()), + firstDownTimeInTarget)); it = inputTargets.end() - 1; } @@ -3060,13 +2995,16 @@ 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.getRawTransform(*windowHandle->getInfo()), + firstDownTimeInTarget)); it = inputTargets.end() - 1; } @@ -3098,11 +3036,10 @@ void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& for (const Monitor& monitor : selectResponsiveMonitorsLocked(monitorsIt->second)) { InputTarget target{monitor.connection}; // target.firstDownTimeInTarget is not set for global monitors. It is only required in split - // touch and global monitoring works as intended even without setting firstDownTimeInTarget - if (const auto& it = mDisplayInfos.find(displayId); it != mDisplayInfos.end()) { - target.displayTransform = it->second.transform; - } - target.setDefaultPointerTransform(target.displayTransform); + // touch and global monitoring works as intended even without setting firstDownTimeInTarget. + // Since global monitors don't have windows, use the display transform as the raw transform. + target.rawTransform = mWindowInfos.getDisplayTransform(displayId); + target.setDefaultPointerTransform(target.rawTransform); inputTargets.push_back(target); } } @@ -3164,7 +3101,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 +3114,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 +3187,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 +3205,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) { @@ -3921,7 +3863,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, "event to it, status=%s(%d)", connection->getInputChannelName().c_str(), statusToString(status).c_str(), status); - abortBrokenDispatchCycleLocked(currentTime, connection, /*notify=*/true); + abortBrokenDispatchCycleLocked(connection, /*notify=*/true); } else { // Pipe is full and we are waiting for the app to finish process some events // before sending more events to it. @@ -3936,7 +3878,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, "status=%s(%d)", connection->getInputChannelName().c_str(), statusToString(status).c_str(), status); - abortBrokenDispatchCycleLocked(currentTime, connection, /*notify=*/true); + abortBrokenDispatchCycleLocked(connection, /*notify=*/true); } return; } @@ -4011,8 +3953,7 @@ void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, postCommandLocked(std::move(command)); } -void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime, - const std::shared_ptr<Connection>& connection, +void InputDispatcher::abortBrokenDispatchCycleLocked(const std::shared_ptr<Connection>& connection, bool notify) { if (DEBUG_DISPATCH_CYCLE) { LOG(INFO) << "channel '" << connection->getInputChannelName() << "'~ " << __func__ @@ -4118,7 +4059,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", @@ -4127,7 +4069,7 @@ int InputDispatcher::handleReceiveCallback(int events, sp<IBinder> connectionTok } // Remove the channel. - removeInputChannelLocked(connection->getToken(), notify); + removeInputChannelLocked(connection, notify); return 0; // remove the callback } @@ -4153,11 +4095,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 +4223,11 @@ 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); - } + // Since we don't have a window, use the display transform as the raw transform. + const ui::Transform displayTransform = + mWindowInfos.getDisplayTransform(motionEntry.displayId); + targets.back().rawTransform = displayTransform; + targets.back().setDefaultPointerTransform(displayTransform); } logOutboundMotionDetails("cancel - ", motionEntry); break; @@ -4340,7 +4282,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 +4309,11 @@ 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); - } + // Since we don't have a window, use the display transform as the raw transform. + const ui::Transform displayTransform = + mWindowInfos.getDisplayTransform(motionEntry.displayId); + targets.back().rawTransform = displayTransform; + targets.back().setDefaultPointerTransform(displayTransform); } logOutboundMotionDetails("down - ", motionEntry); break; @@ -4642,10 +4584,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(); @@ -4939,6 +4878,19 @@ InputEventInjectionResult InputDispatcher::injectInputEvent(const InputEvent* ev return InputEventInjectionResult::FAILED; } + if (!(policyFlags & POLICY_FLAG_PASS_TO_USER)) { + // Set the flag anyway if we already have an ongoing motion gesture. That + // would allow us to complete the processing of the current stroke. + const auto touchStateIt = mTouchStatesByDisplay.find(displayId); + if (touchStateIt != mTouchStatesByDisplay.end()) { + const TouchState& touchState = touchStateIt->second; + if (touchState.hasTouchingPointers(resolvedDeviceId) || + touchState.hasHoveringPointers(resolvedDeviceId)) { + policyFlags |= POLICY_FLAG_PASS_TO_USER; + } + } + } + const nsecs_t* sampleEventTimes = motionEvent.getSampleEventTimes(); const size_t pointerCount = motionEvent.getPointerCount(); const std::vector<PointerProperties> @@ -5149,9 +5101,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 +5135,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 +5154,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 +5162,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 +5174,108 @@ 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); } -ui::Transform InputDispatcher::getTransformLocked(ui::LogicalDisplayId displayId) const { +void InputDispatcher::DispatcherWindowInfo::setWindowHandlesForDisplay( + ui::LogicalDisplayId displayId, std::vector<sp<WindowInfoHandle>>&& windowHandles) { + // Insert or replace + mWindowHandlesByDisplay[displayId] = std::move(windowHandles); +} + +void InputDispatcher::DispatcherWindowInfo::setDisplayInfos( + const std::vector<android::gui::DisplayInfo>& displayInfos) { + mDisplayInfos.clear(); + for (const auto& displayInfo : displayInfos) { + mDisplayInfos.emplace(displayInfo.displayId, displayInfo); + } +} + +void InputDispatcher::DispatcherWindowInfo::removeDisplay(ui::LogicalDisplayId displayId) { + mWindowHandlesByDisplay.erase(displayId); +} + +const std::vector<sp<android::gui::WindowInfoHandle>>& +InputDispatcher::DispatcherWindowInfo::getWindowHandlesForDisplay( + ui::LogicalDisplayId displayId) const { + static const std::vector<sp<WindowInfoHandle>> EMPTY_WINDOW_HANDLES; + const auto it = mWindowHandlesByDisplay.find(displayId); + return it != mWindowHandlesByDisplay.end() ? it->second : EMPTY_WINDOW_HANDLES; +} + +void InputDispatcher::DispatcherWindowInfo::forEachWindowHandle( + std::function<void(const sp<android::gui::WindowInfoHandle>&)> f) const { + for (const auto& [_, windowHandles] : mWindowHandlesByDisplay) { + for (const auto& windowHandle : windowHandles) { + f(windowHandle); + } + } +} + +void InputDispatcher::DispatcherWindowInfo::forEachDisplayId( + std::function<void(ui::LogicalDisplayId)> f) const { + for (const auto& [displayId, _] : mWindowHandlesByDisplay) { + f(displayId); + } +} + +ui::Transform InputDispatcher::DispatcherWindowInfo::getDisplayTransform( + ui::LogicalDisplayId displayId) const { auto displayInfoIt = mDisplayInfos.find(displayId); return displayInfoIt != mDisplayInfos.end() ? displayInfoIt->second.transform : kIdentityTransform; } +ui::Transform InputDispatcher::DispatcherWindowInfo::getRawTransform( + const android::gui::WindowInfo& windowInfo) const { + // If the window has a cloneLayerStackTransform, always use it as the transform for the "getRaw" + // APIs. If not, fall back to using the DisplayInfo transform of the window's display. + return (input_flags::use_cloned_screen_coordinates_as_raw() && + windowInfo.cloneLayerStackTransform) + ? *windowInfo.cloneLayerStackTransform + : getDisplayTransform(windowInfo.displayId); +} + +std::string InputDispatcher::DispatcherWindowInfo::dumpDisplayAndWindowInfo() const { + std::string dump; + if (!mWindowHandlesByDisplay.empty()) { + for (const auto& [displayId, windowHandles] : mWindowHandlesByDisplay) { + dump += StringPrintf("Display: %s\n", displayId.toString().c_str()); + if (const auto& it = mDisplayInfos.find(displayId); it != mDisplayInfos.end()) { + const auto& displayInfo = it->second; + dump += StringPrintf(INDENT "logicalSize=%dx%d\n", displayInfo.logicalWidth, + displayInfo.logicalHeight); + displayInfo.transform.dump(dump, "transform", INDENT3); + } else { + dump += INDENT "No DisplayInfo found!\n"; + } + + if (!windowHandles.empty()) { + dump += INDENT "Windows:\n"; + for (size_t i = 0; i < windowHandles.size(); i++) { + dump += StringPrintf(INDENT2 "%zu: %s", i, + streamableToString(*windowHandles[i]).c_str()); + } + } else { + dump += INDENT "Windows: <none>\n"; + } + } + } else { + dump += "Displays: <none>\n"; + } + return dump; +} + bool InputDispatcher::canWindowReceiveMotionLocked(const sp<WindowInfoHandle>& window, const MotionEntry& motionEntry) const { const WindowInfo& info = *window->getInfo(); @@ -5316,13 +5343,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 +5389,7 @@ void InputDispatcher::updateWindowHandlesForDisplayLocked( } } - // Insert or replace - mWindowHandlesByDisplay[displayId] = newHandles; + mWindowInfos.setWindowHandlesForDisplay(displayId, std::move(newHandles)); } /** @@ -5412,12 +5439,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 +5458,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 +5502,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 +5525,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 +5602,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 +5741,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 +5749,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 +5764,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,20 +5778,18 @@ 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); -} +std::tuple<TouchState*, TouchedWindow*, ui::LogicalDisplayId> +InputDispatcher::findTouchStateWindowAndDisplay( + const sp<IBinder>& token, + std::unordered_map<ui::LogicalDisplayId, TouchState>& touchStatesByDisplay) { + auto [constTouchState, constTouchedWindow, displayId] = InputDispatcher:: + findTouchStateWindowAndDisplay(token, + const_cast<const std::unordered_map<ui::LogicalDisplayId, + TouchState>&>( + touchStatesByDisplay)); -bool InputDispatcher::windowHasTouchingPointersLocked(const sp<WindowInfoHandle>& windowHandle, - DeviceId deviceId) const { - const auto& [touchState, touchedWindow, _] = - findTouchStateWindowAndDisplayLocked(windowHandle->getToken()); - if (touchState == nullptr) { - // No touching pointers at all - return false; - } - return touchState->hasTouchingPointers(deviceId); + return std::make_tuple(const_cast<TouchState*>(constTouchState), + const_cast<TouchedWindow*>(constTouchedWindow), displayId); } bool InputDispatcher::transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken, @@ -5775,7 +5805,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 +5821,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 +5898,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 +5930,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 +5990,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 +6039,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) { @@ -6237,8 +6247,14 @@ Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputMonitor( status_t InputDispatcher::removeInputChannel(const sp<IBinder>& connectionToken) { { // acquire lock std::scoped_lock _l(mLock); + std::shared_ptr<Connection> connection = getConnectionLocked(connectionToken); + if (connection == nullptr) { + // Connection can be removed via socket hang up or an explicit call to + // 'removeInputChannel' + return BAD_VALUE; + } - status_t status = removeInputChannelLocked(connectionToken, /*notify=*/false); + status_t status = removeInputChannelLocked(connection, /*notify=*/false); if (status) { return status; } @@ -6250,25 +6266,18 @@ status_t InputDispatcher::removeInputChannel(const sp<IBinder>& connectionToken) return OK; } -status_t InputDispatcher::removeInputChannelLocked(const sp<IBinder>& connectionToken, +status_t InputDispatcher::removeInputChannelLocked(const std::shared_ptr<Connection>& connection, bool notify) { - std::shared_ptr<Connection> connection = getConnectionLocked(connectionToken); - if (connection == nullptr) { - // Connection can be removed via socket hang up or an explicit call to 'removeInputChannel' - return BAD_VALUE; - } - + LOG_ALWAYS_FATAL_IF(connection == nullptr); + abortBrokenDispatchCycleLocked(connection, notify); removeConnectionLocked(connection); if (connection->monitor) { - removeMonitorChannelLocked(connectionToken); + removeMonitorChannelLocked(connection->getToken()); } mLooper->removeFd(connection->inputPublisher.getChannel().getFd()); - nsecs_t currentTime = now(); - abortBrokenDispatchCycleLocked(currentTime, connection, notify); - connection->status = Connection::Status::ZOMBIE; return OK; } @@ -6301,7 +6310,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 +6363,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 +6503,17 @@ 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.getRawTransform( + *windowHandle->getInfo()), + downTime)); } } releaseDispatchEntry(std::move(dispatchEntry)); @@ -6557,7 +6567,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 +6618,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 +6678,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 +6696,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 +6761,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 +6847,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 +6989,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 +7007,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"; } @@ -7098,13 +7111,6 @@ void InputDispatcher::onWindowInfosChanged(const gui::WindowInfosUpdate& update) for (const auto& info : update.windowInfos) { handlesPerDisplay.emplace(info.displayId, std::vector<sp<WindowInfoHandle>>()); handlesPerDisplay[info.displayId].push_back(sp<WindowInfoHandle>::make(info)); - if (input_flags::split_all_touches()) { - handlesPerDisplay[info.displayId] - .back() - ->editInfo() - ->setInputConfig(android::gui::WindowInfo::InputConfig::PREVENT_SPLITTING, - false); - } } { // acquire lock @@ -7112,14 +7118,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 +7268,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..bca1c67a43 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,65 @@ 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; + + // Get the raw transform to use for motion events going to the given window. + ui::Transform getRawTransform(const android::gui::WindowInfo&) const; + + // Lookup for WindowInfoHandle from token and optionally a display-id. In cases where + // display-id is not provided lookup is done for all displays. + sp<android::gui::WindowInfoHandle> findWindowHandle( + const sp<IBinder>& windowHandleToken, + std::optional<ui::LogicalDisplayId> displayId = {}) const; + + bool isWindowPresent(const sp<android::gui::WindowInfoHandle>& windowHandle) const; + + // Returns the touched window at the given location, excluding the ignoreWindow if provided. + sp<android::gui::WindowInfoHandle> findTouchedWindowAt( + ui::LogicalDisplayId displayId, float x, float y, bool isStylus = false, + const sp<android::gui::WindowInfoHandle> ignoreWindow = nullptr) const; + + std::vector<sp<android::gui::WindowInfoHandle>> findTouchedSpyWindowsAt( + ui::LogicalDisplayId displayId, float x, float y, bool isStylus, DeviceId deviceId, + const std::unordered_map<ui::LogicalDisplayId, TouchState>& touchStatesByDisplay) + const; + + std::string dumpDisplayAndWindowInfo() const; + + private: + std::unordered_map<ui::LogicalDisplayId /*displayId*/, + std::vector<sp<android::gui::WindowInfoHandle>>> + mWindowHandlesByDisplay; + std::unordered_map<ui::LogicalDisplayId /*displayId*/, android::gui::DisplayInfo> + mDisplayInfos; + }; + + DispatcherWindowInfo mWindowInfos GUARDED_BY(mLock); + void setInputWindowsLocked( const std::vector<sp<android::gui::WindowInfoHandle>>& inputWindowHandles, ui::LogicalDisplayId displayId) REQUIRES(mLock); - // Get a reference to window handles by display, return an empty vector if not found. - const std::vector<sp<android::gui::WindowInfoHandle>>& getWindowHandlesLocked( - ui::LogicalDisplayId displayId) const REQUIRES(mLock); - ui::Transform getTransformLocked(ui::LogicalDisplayId displayId) const REQUIRES(mLock); - sp<android::gui::WindowInfoHandle> getWindowHandleLocked( - const sp<IBinder>& windowHandleToken, - std::optional<ui::LogicalDisplayId> displayId = {}) const REQUIRES(mLock); - sp<android::gui::WindowInfoHandle> getWindowHandleLocked( - const sp<android::gui::WindowInfoHandle>& windowHandle) const REQUIRES(mLock); sp<android::gui::WindowInfoHandle> getFocusedWindowHandleLocked( ui::LogicalDisplayId displayId) const REQUIRES(mLock); bool canWindowReceiveMotionLocked(const sp<android::gui::WindowInfoHandle>& window, @@ -550,10 +585,6 @@ 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, @@ -618,8 +649,7 @@ private: void finishDispatchCycleLocked(nsecs_t currentTime, const std::shared_ptr<Connection>& connection, uint32_t seq, bool handled, nsecs_t consumeTime) REQUIRES(mLock); - void abortBrokenDispatchCycleLocked(nsecs_t currentTime, - const std::shared_ptr<Connection>& connection, bool notify) + void abortBrokenDispatchCycleLocked(const std::shared_ptr<Connection>& connection, bool notify) REQUIRES(mLock); void drainDispatchQueue(std::deque<std::unique_ptr<DispatchEntry>>& queue); void releaseDispatchEntry(std::unique_ptr<DispatchEntry> dispatchEntry); @@ -665,7 +695,7 @@ private: // Registration. void removeMonitorChannelLocked(const sp<IBinder>& connectionToken) REQUIRES(mLock); - status_t removeInputChannelLocked(const sp<IBinder>& connectionToken, bool notify) + status_t removeInputChannelLocked(const std::shared_ptr<Connection>& connection, bool notify) REQUIRES(mLock); // Interesting events that we might like to log or tell the framework about. @@ -696,13 +726,15 @@ 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); // Statistics gathering. nsecs_t mLastStatisticPushTime = 0; diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h index 90374f1481..76f3fe0d0c 100644 --- a/services/inputflinger/dispatcher/InputTarget.h +++ b/services/inputflinger/dispatcher/InputTarget.h @@ -77,8 +77,8 @@ public: // (ignored for KeyEvents) float globalScaleFactor = 1.0f; - // Current display transform. Used for compatibility for raw coordinates. - ui::Transform displayTransform; + // The raw coordinate transform that's used for compatibility for MotionEvent's getRaw APIs. + ui::Transform rawTransform; // Event time for the first motion event (ACTION_DOWN) dispatched to this input target if // FLAG_SPLIT is set. diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h index 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/dispatcher/trace/AndroidInputEventProtoConverter.cpp b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp index 0b17507c2c..cc0468464e 100644 --- a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp +++ b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp @@ -115,13 +115,17 @@ void AndroidInputEventProtoConverter::toProtoWindowDispatchEvent( for (size_t i = 0; i < motion->pointerProperties.size(); i++) { auto* pointerProto = outProto.add_dispatched_pointer(); pointerProto->set_pointer_id(motion->pointerProperties[i].id); + const auto& coords = motion->pointerCoords[i]; const auto rawXY = MotionEvent::calculateTransformedXY(motion->source, args.rawTransform, - motion->pointerCoords[i].getXYValue()); - pointerProto->set_x_in_display(rawXY.x); - pointerProto->set_y_in_display(rawXY.y); + coords.getXYValue()); + if (coords.getXYValue() != rawXY) { + // These values are only traced if they were modified by the raw transform + // to save space. Trace consumers should be aware of this optimization. + pointerProto->set_x_in_display(rawXY.x); + pointerProto->set_y_in_display(rawXY.y); + } - const auto& coords = motion->pointerCoords[i]; const auto coordsInWindow = MotionEvent::calculateTransformedCoords(motion->source, motion->flags, args.transform, coords); @@ -129,6 +133,7 @@ void AndroidInputEventProtoConverter::toProtoWindowDispatchEvent( for (int32_t axisIndex = 0; !bits.isEmpty(); axisIndex++) { const uint32_t axis = bits.clearFirstMarkedBit(); const float axisValueInWindow = coordsInWindow.values[axisIndex]; + // Only values that are modified by the window transform are traced. if (coords.values[axisIndex] != axisValueInWindow) { auto* axisEntry = pointerProto->add_axis_value_in_window(); axisEntry->set_axis(axis); diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index 404a509247..f54b76b3bf 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -143,7 +143,7 @@ struct InputReaderConfiguration { // 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; + std::set<ui::LogicalDisplayId> displaysWithMouseScalingDisabled; // True if the connected mouse should exhibit pointer acceleration. If false, // a flat acceleration curve (linear scaling) is used, but the user's pointer @@ -282,7 +282,7 @@ struct InputReaderConfiguration { : virtualKeyQuietTime(0), defaultPointerDisplayId(ui::LogicalDisplayId::DEFAULT), mousePointerSpeed(0), - displaysWithMousePointerAccelerationDisabled(), + displaysWithMouseScalingDisabled(), mousePointerAccelerationEnabled(true), pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f, static_cast<float>( diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp index b3cd35c936..3934e783d1 100644 --- a/services/inputflinger/reader/Android.bp +++ b/services/inputflinger/reader/Android.bp @@ -79,25 +79,25 @@ cc_defaults { srcs: [":libinputreader_sources"], shared_libs: [ "android.companion.virtualdevice.flags-aconfig-cc", + "libPlatformProperties", "libbase", "libcap", "libcrypto", "libcutils", - "libjsoncpp", "libinput", + "libjsoncpp", "liblog", - "libPlatformProperties", "libstatslog", "libstatspull", - "libutils", "libstatssocket", + "libutils", ], static_libs: [ "libchrome-gestures", - "libui-types", "libexpresslog", - "libtextclassifier_hash_static", "libstatslog_express", + "libtextclassifier_hash_static", + "libui-types", ], header_libs: [ "libbatteryservice_headers", diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index e9f17e796f..9f584a0251 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -484,7 +484,7 @@ void CursorInputMapper::configureOnChangePointerSpeed(const InputReaderConfigura return; } - bool disableAllScaling = config.displaysWithMousePointerAccelerationDisabled.count( + bool disableAllScaling = config.displaysWithMouseScalingDisabled.count( mDisplayId.value_or(ui::LogicalDisplayId::INVALID)) != 0; mPointerVelocityControl.setAccelerationEnabled(!disableAllScaling); diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 5c90cbb6ce..6efaecaaa8 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -30,6 +30,7 @@ #include <android-base/stringprintf.h> #include <android/input.h> +#include <com_android_input_flags.h> #include <ftl/enum.h> #include <input/PrintTools.h> #include <input/PropertyMap.h> @@ -47,6 +48,8 @@ namespace android { +namespace input_flags = com::android::input::flags; + // --- Constants --- // Artificial latency on synthetic events created from stylus data without corresponding touch @@ -1575,7 +1578,8 @@ std::list<NotifyArgs> TouchInputMapper::cookAndDispatch(nsecs_t when, nsecs_t re mLastCookedState.buttonState, mCurrentCookedState.buttonState); // Dispatch the touches either directly or by translation through a pointer on screen. - if (mDeviceMode == DeviceMode::POINTER) { + if (!input_flags::disable_touch_input_mapper_pointer_usage() && + mDeviceMode == DeviceMode::POINTER) { for (BitSet32 idBits(mCurrentRawState.rawPointerData.touchingIdBits); !idBits.isEmpty();) { uint32_t id = idBits.clearFirstMarkedBit(); const RawPointerData::Pointer& pointer = @@ -1613,7 +1617,9 @@ std::list<NotifyArgs> TouchInputMapper::cookAndDispatch(nsecs_t when, nsecs_t re } out += dispatchPointerUsage(when, readTime, policyFlags, pointerUsage); - } else { + } + if (input_flags::disable_touch_input_mapper_pointer_usage() || + mDeviceMode != DeviceMode::POINTER) { if (!mCurrentMotionAborted) { out += dispatchButtonRelease(when, readTime, policyFlags); out += dispatchHoverExit(when, readTime, policyFlags); @@ -2251,6 +2257,23 @@ void TouchInputMapper::cookPointerData() { for (uint32_t i = 0; i < currentPointerCount; i++) { const RawPointerData::Pointer& in = mCurrentRawState.rawPointerData.pointers[i]; + bool isHovering = in.isHovering; + + // A tool MOUSE pointer is only down/touching when a mouse button is pressed. + if (input_flags::disable_touch_input_mapper_pointer_usage() && + in.toolType == ToolType::MOUSE && + !mCurrentRawState.rawPointerData.canceledIdBits.hasBit(in.id)) { + if (isPointerDown(mCurrentRawState.buttonState)) { + isHovering = false; + mCurrentCookedState.cookedPointerData.touchingIdBits.markBit(in.id); + mCurrentCookedState.cookedPointerData.hoveringIdBits.clearBit(in.id); + } else { + isHovering = true; + mCurrentCookedState.cookedPointerData.touchingIdBits.clearBit(in.id); + mCurrentCookedState.cookedPointerData.hoveringIdBits.markBit(in.id); + } + } + // Size float touchMajor, touchMinor, toolMajor, toolMinor, size; switch (mCalibration.sizeCalibration) { @@ -2340,7 +2363,7 @@ void TouchInputMapper::cookPointerData() { pressure = in.pressure * mPressureScale; break; default: - pressure = in.isHovering ? 0 : 1; + pressure = isHovering ? 0 : 1; break; } @@ -3476,7 +3499,7 @@ std::list<NotifyArgs> TouchInputMapper::dispatchPointerMouse(nsecs_t when, nsecs } return dispatchPointerSimple(when, readTime, policyFlags, down, hovering, - ui::LogicalDisplayId::INVALID); + getAssociatedDisplayId().value_or(ui::LogicalDisplayId::INVALID)); } std::list<NotifyArgs> TouchInputMapper::abortPointerMouse(nsecs_t when, nsecs_t readTime, @@ -3697,7 +3720,10 @@ NotifyMotionArgs TouchInputMapper::dispatchMotion( float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; if (mDeviceMode == DeviceMode::POINTER) { - xCursorPosition = yCursorPosition = 0.f; + ALOGW_IF(pointerCount != 1, + "Only single pointer events are fully supported in POINTER mode"); + xCursorPosition = pointerCoords[0].getX(); + yCursorPosition = pointerCoords[0].getY(); } const DeviceId deviceId = getDeviceId(); std::vector<TouchVideoFrame> frames = getDeviceContext().getVideoFrames(); @@ -3967,14 +3993,8 @@ bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask, } std::optional<ui::LogicalDisplayId> TouchInputMapper::getAssociatedDisplayId() { - if (mParameters.hasAssociatedDisplay) { - if (mDeviceMode == DeviceMode::POINTER) { - return ui::LogicalDisplayId::INVALID; - } else { - return std::make_optional(mViewport.displayId); - } - } - return std::nullopt; + return mParameters.hasAssociatedDisplay ? std::make_optional(mViewport.displayId) + : std::nullopt; } } // namespace android diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h index ef0e02f40c..eb4326fa56 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchInputMapper.h @@ -215,7 +215,7 @@ protected: DISABLED, // input is disabled DIRECT, // direct mapping (touchscreen) NAVIGATION, // unscaled mapping with assist gesture (touch navigation) - POINTER, // pointer mapping (e.g. uncaptured touchpad, drawing tablet) + POINTER, // pointer mapping (e.g. absolute mouse, drawing tablet) ftl_last = POINTER }; @@ -234,6 +234,9 @@ protected: ftl_last = POINTER }; + // TouchInputMapper will configure devices with INPUT_PROP_DIRECT as + // DeviceType::TOUCH_SCREEN, and will otherwise use DeviceType::POINTER by default. + // This can be overridden by IDC files, using the `touch.deviceType` config. DeviceType deviceType; bool hasAssociatedDisplay; bool associatedDisplayIsExternal; diff --git a/services/inputflinger/tests/CursorInputMapper_test.cpp b/services/inputflinger/tests/CursorInputMapper_test.cpp index a31c4e96d7..18e0b309ba 100644 --- a/services/inputflinger/tests/CursorInputMapper_test.cpp +++ b/services/inputflinger/tests/CursorInputMapper_test.cpp @@ -1078,7 +1078,7 @@ TEST_F(CursorInputMapperUnitTest, ConfigureAccelerationWithAssociatedViewport) { ASSERT_GT(coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y), 20.f); // Disable acceleration for the display, and verify that acceleration is no longer applied. - mReaderConfiguration.displaysWithMousePointerAccelerationDisabled.emplace(DISPLAY_ID); + mReaderConfiguration.displaysWithMouseScalingDisabled.emplace(DISPLAY_ID); args += mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, InputReaderConfiguration::Change::POINTER_SPEED); args.clear(); @@ -1097,7 +1097,7 @@ TEST_F(CursorInputMapperUnitTest, ConfigureAccelerationOnDisplayChange) { DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation0); mReaderConfiguration.setDisplayViewports({primaryViewport}); // Disable acceleration for the display. - mReaderConfiguration.displaysWithMousePointerAccelerationDisabled.emplace(DISPLAY_ID); + mReaderConfiguration.displaysWithMouseScalingDisabled.emplace(DISPLAY_ID); // Don't associate the device with the display yet. EXPECT_CALL((*mDevice), getAssociatedViewport).WillRepeatedly(Return(std::nullopt)); diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp b/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp index 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/FakeWindows.h b/services/inputflinger/tests/FakeWindows.h index 3a3238a6ad..54dc25aaa0 100644 --- a/services/inputflinger/tests/FakeWindows.h +++ b/services/inputflinger/tests/FakeWindows.h @@ -144,10 +144,6 @@ public: mInfo.setInputConfig(InputConfig::PAUSE_DISPATCHING, paused); } - inline void setPreventSplitting(bool preventSplitting) { - mInfo.setInputConfig(InputConfig::PREVENT_SPLITTING, preventSplitting); - } - inline void setSlippery(bool slippery) { mInfo.setInputConfig(InputConfig::SLIPPERY, slippery); } diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 3413caa1f0..b6e27a87f8 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -19,6 +19,7 @@ #include "FakeInputDispatcherPolicy.h" #include "FakeInputTracingBackend.h" #include "FakeWindows.h" +#include "ScopedFlagOverride.h" #include "TestEventMatchers.h" #include <NotifyArgsBuilders.h> @@ -117,8 +118,12 @@ 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; @@ -134,40 +139,6 @@ static KeyEvent getTestKeyEvent() { return event; } -/** - * Provide a local override for a flag value. The value is restored when the object of this class - * goes out of scope. - * This class is not intended to be used directly, because its usage is cumbersome. - * Instead, a wrapper macro SCOPED_FLAG_OVERRIDE is provided. - */ -class ScopedFlagOverride { -public: - ScopedFlagOverride(std::function<bool()> read, std::function<void(bool)> write, bool value) - : mInitialValue(read()), mWriteValue(write) { - mWriteValue(value); - } - ~ScopedFlagOverride() { mWriteValue(mInitialValue); } - -private: - const bool mInitialValue; - std::function<void(bool)> mWriteValue; -}; - -typedef bool (*readFlagValueFunction)(); -typedef void (*writeFlagValueFunction)(bool); - -/** - * Use this macro to locally override a flag value. - * Example usage: - * SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false); - * Note: this works by creating a local variable in your current scope. Don't call this twice for - * the same flag, because the variable names will clash! - */ -#define SCOPED_FLAG_OVERRIDE(NAME, VALUE) \ - readFlagValueFunction read##NAME = com::android::input::flags::NAME; \ - writeFlagValueFunction write##NAME = com::android::input::flags::NAME; \ - ScopedFlagOverride override##NAME(read##NAME, write##NAME, (VALUE)) - } // namespace // --- InputDispatcherTest --- @@ -1211,22 +1182,17 @@ TEST_F(InputDispatcherTest, MultiDeviceTouchTransferWithWallpaperWindows) { WithFlags(EXPECTED_WALLPAPER_FLAGS | AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE))); } -class ShouldSplitTouchFixture : public InputDispatcherTest, - public ::testing::WithParamInterface<bool> {}; -INSTANTIATE_TEST_SUITE_P(InputDispatcherTest, ShouldSplitTouchFixture, - ::testing::Values(true, false)); /** * A single window that receives touch (on top), and a wallpaper window underneath it. * The top window gets a multitouch gesture. * Ensure that wallpaper gets the same gesture. */ -TEST_P(ShouldSplitTouchFixture, WallpaperWindowReceivesMultiTouch) { +TEST_F(InputDispatcherTest, WallpaperWindowReceivesMultiTouch) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> foregroundWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Foreground", ui::LogicalDisplayId::DEFAULT); foregroundWindow->setDupTouchToWallpaper(true); - foregroundWindow->setPreventSplitting(GetParam()); sp<FakeWindowHandle> wallpaperWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper", @@ -1571,6 +1537,60 @@ TEST_F(InputDispatcherTest, HoverEventInconsistentPolicy) { window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT)); } +// Still send inject motion events to window which already be touched. +TEST_F(InputDispatcherTest, AlwaysDispatchInjectMotionEventWhenAlreadyDownForWindow) { + std::shared_ptr<FakeApplicationHandle> application1 = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window1 = + sp<FakeWindowHandle>::make(application1, mDispatcher, "window1", + ui::LogicalDisplayId::DEFAULT); + window1->setFrame(Rect(0, 0, 100, 100)); + window1->setWatchOutsideTouch(false); + + std::shared_ptr<FakeApplicationHandle> application2 = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window2 = + sp<FakeWindowHandle>::make(application2, mDispatcher, "window2", + ui::LogicalDisplayId::DEFAULT); + window2->setFrame(Rect(50, 50, 100, 100)); + window2->setWatchOutsideTouch(true); + mDispatcher->onWindowInfosChanged({{*window2->getInfo(), *window1->getInfo()}, {}, 0, 0}); + + std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT; + InputEventInjectionSync injectionMode = InputEventInjectionSync::WAIT_FOR_RESULT; + std::optional<gui::Uid> targetUid = {}; + uint32_t policyFlags = DEFAULT_POLICY_FLAGS; + + const MotionEvent eventDown1 = MotionEventBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(60).y(60)).deviceId(-1) + .build(); + injectMotionEvent(*mDispatcher, eventDown1, injectionTimeout, injectionMode, targetUid, + policyFlags); + window2->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + + const MotionEvent eventUp1 = MotionEventBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(60).y(60)).deviceId(-1) + .downTime(eventDown1.getDownTime()).build(); + // Inject UP event, without the POLICY_FLAG_PASS_TO_USER (to simulate policy behaviour + // when screen is off). + injectMotionEvent(*mDispatcher, eventUp1, injectionTimeout, injectionMode, targetUid, + /*policyFlags=*/0); + window2->consumeMotionEvent(WithMotionAction(ACTION_UP)); + const MotionEvent eventDown2 = MotionEventBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(40)).deviceId(-1) + .build(); + injectMotionEvent(*mDispatcher, eventDown2, injectionTimeout, injectionMode, targetUid, + policyFlags); + window1->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + window2->consumeMotionEvent(WithMotionAction(ACTION_OUTSIDE)); + + const MotionEvent eventUp2 = MotionEventBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(60).y(60)).deviceId(-1) + .downTime(eventDown2.getDownTime()).build(); + injectMotionEvent(*mDispatcher, eventUp2, injectionTimeout, injectionMode, targetUid, + /*policyFlags=*/0); + window1->consumeMotionEvent(WithMotionAction(ACTION_UP)); + window2->assertNoEvents(); +} + /** * Two windows: a window on the left and a window on the right. * Mouse is hovered from the right window into the left window. @@ -4256,17 +4276,15 @@ TEST_F(InputDispatcherTest, SplitTouchesSendCorrectActionDownTime) { } /** - * When events are not split, the downTime should be adjusted such that the downTime corresponds + * When events are split, the downTime should be adjusted such that the downTime corresponds * to the event time of the first ACTION_DOWN. If a new window appears, it should not affect - * the event routing because the first window prevents splitting. + * the event routing unless pointers are delivered to the new window. */ TEST_F(InputDispatcherTest, SplitTouchesSendCorrectActionDownTimeForNewWindow) { - SCOPED_FLAG_OVERRIDE(split_all_touches, false); std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window1 = sp<FakeWindowHandle>::make(application, mDispatcher, "Window1", DISPLAY_ID); window1->setTouchableRegion(Region{{0, 0, 100, 100}}); - window1->setPreventSplitting(true); sp<FakeWindowHandle> window2 = sp<FakeWindowHandle>::make(application, mDispatcher, "Window2", DISPLAY_ID); @@ -4286,13 +4304,18 @@ TEST_F(InputDispatcherTest, SplitTouchesSendCorrectActionDownTimeForNewWindow) { // Second window is added mDispatcher->onWindowInfosChanged({{*window1->getInfo(), *window2->getInfo()}, {}, 0, 0}); - // Now touch down on the window with another pointer - mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) - .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) - .pointer(PointerBuilder(1, ToolType::FINGER).x(150).y(50)) - .downTime(downArgs.downTime) - .build()); - window1->consumeMotionPointerDown(1, AllOf(WithDownTime(downArgs.downTime))); + // Now touch down on the new window with another pointer + NotifyMotionArgs pointerDownArgs = + MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) + .pointer(PointerBuilder(1, ToolType::FINGER).x(150).y(50)) + .downTime(downArgs.downTime) + .build(); + mDispatcher->notifyMotion(pointerDownArgs); + window1->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithPointerCount(1), + WithDownTime(downArgs.downTime))); + window2->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_DOWN), WithDownTime(pointerDownArgs.eventTime))); // Finish the gesture mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN) @@ -4300,11 +4323,16 @@ TEST_F(InputDispatcherTest, SplitTouchesSendCorrectActionDownTimeForNewWindow) { .pointer(PointerBuilder(1, ToolType::FINGER).x(150).y(50)) .downTime(downArgs.downTime) .build()); + window1->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_MOVE), WithDownTime(downArgs.downTime))); + window2->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_UP), WithDownTime(pointerDownArgs.eventTime))); + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN) .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) .downTime(downArgs.downTime) .build()); - window1->consumeMotionPointerUp(1, AllOf(WithDownTime(downArgs.downTime))); + window1->consumeMotionEvent( AllOf(WithMotionAction(ACTION_UP), WithDownTime(downArgs.downTime))); window2->assertNoEvents(); @@ -4313,13 +4341,12 @@ TEST_F(InputDispatcherTest, SplitTouchesSendCorrectActionDownTimeForNewWindow) { /** * When splitting touch events, the downTime should be adjusted such that the downTime corresponds * to the event time of the first ACTION_DOWN sent to the new window. - * If a new window that does not support split appears on the screen and gets touched with the - * second finger, it should not get any events because it doesn't want split touches. At the same - * time, the first window should not get the pointer_down event because it supports split touches - * (and the touch occurred outside of the bounds of window1). + * If a new window appears on the screen and gets touched with the + * second finger, it should get the new event. At the same + * time, the first window should not get the pointer_down event because + * the touch occurred outside of its bounds. */ -TEST_F(InputDispatcherTest, SplitTouchesDropsEventForNonSplittableSecondWindow) { - SCOPED_FLAG_OVERRIDE(split_all_touches, false); +TEST_F(InputDispatcherTest, SplitTouchesWhenWindowIsAdded) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window1 = sp<FakeWindowHandle>::make(application, mDispatcher, "Window1", DISPLAY_ID); @@ -4341,16 +4368,16 @@ TEST_F(InputDispatcherTest, SplitTouchesDropsEventForNonSplittableSecondWindow) AllOf(WithMotionAction(ACTION_DOWN), WithDownTime(downArgs.downTime))); // Second window is added - window2->setPreventSplitting(true); mDispatcher->onWindowInfosChanged({{*window1->getInfo(), *window2->getInfo()}, {}, 0, 0}); - // Now touch down on the window with another pointer + // Now touch down on the new window with another pointer mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) .pointer(PointerBuilder(1, ToolType::FINGER).x(150).y(50)) .downTime(downArgs.downTime) .build()); - // Event is dropped because window2 doesn't support split touch, and window1 does. + window1->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithPointerCount(1))); + window2->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); // Complete the gesture mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN) @@ -4361,6 +4388,8 @@ TEST_F(InputDispatcherTest, SplitTouchesDropsEventForNonSplittableSecondWindow) // A redundant MOVE event is generated that doesn't carry any new information window1->consumeMotionEvent( AllOf(WithMotionAction(ACTION_MOVE), WithDownTime(downArgs.downTime))); + window2->consumeMotionEvent(WithMotionAction(ACTION_UP)); + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN) .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) .downTime(downArgs.downTime) @@ -4580,22 +4609,20 @@ TEST_F(InputDispatcherTest, TwoPointersDownMouseClick) { * A spy window sits above a window with NO_INPUT_CHANNEL. Ensure that the spy receives events even * though the window underneath should not get any events. */ -TEST_F(InputDispatcherTest, NonSplittableSpyAboveNoInputChannelWindowSinglePointer) { +TEST_F(InputDispatcherTest, SpyAboveNoInputChannelWindowSinglePointer) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> spyWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Spy", ui::LogicalDisplayId::DEFAULT); spyWindow->setFrame(Rect(0, 0, 100, 100)); spyWindow->setTrustedOverlay(true); - spyWindow->setPreventSplitting(true); spyWindow->setSpy(true); - // Another window below spy that has both NO_INPUT_CHANNEL and PREVENT_SPLITTING + // Another window below spy that has NO_INPUT_CHANNEL sp<FakeWindowHandle> inputSinkWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Input sink below spy", ui::LogicalDisplayId::DEFAULT); inputSinkWindow->setFrame(Rect(0, 0, 100, 100)); inputSinkWindow->setTrustedOverlay(true); - inputSinkWindow->setPreventSplitting(true); inputSinkWindow->setNoInputChannel(true); mDispatcher->onWindowInfosChanged( @@ -4620,22 +4647,20 @@ TEST_F(InputDispatcherTest, NonSplittableSpyAboveNoInputChannelWindowSinglePoint * though the window underneath should not get any events. * Same test as above, but with two pointers touching instead of one. */ -TEST_F(InputDispatcherTest, NonSplittableSpyAboveNoInputChannelWindowTwoPointers) { +TEST_F(InputDispatcherTest, SpyAboveNoInputChannelWindowTwoPointers) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> spyWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Spy", ui::LogicalDisplayId::DEFAULT); spyWindow->setFrame(Rect(0, 0, 100, 100)); spyWindow->setTrustedOverlay(true); - spyWindow->setPreventSplitting(true); spyWindow->setSpy(true); - // Another window below spy that would have both NO_INPUT_CHANNEL and PREVENT_SPLITTING + // Another window below spy that would have NO_INPUT_CHANNEL sp<FakeWindowHandle> inputSinkWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Input sink below spy", ui::LogicalDisplayId::DEFAULT); inputSinkWindow->setFrame(Rect(0, 0, 100, 100)); inputSinkWindow->setTrustedOverlay(true); - inputSinkWindow->setPreventSplitting(true); inputSinkWindow->setNoInputChannel(true); mDispatcher->onWindowInfosChanged( @@ -4667,15 +4692,10 @@ TEST_F(InputDispatcherTest, NonSplittableSpyAboveNoInputChannelWindowTwoPointers inputSinkWindow->assertNoEvents(); } -/** Check the behaviour for cases where input sink prevents or doesn't prevent splitting. */ -class SpyThatPreventsSplittingWithApplicationFixture : public InputDispatcherTest, - public ::testing::WithParamInterface<bool> { -}; - /** * Three windows: * - An application window (app window) - * - A spy window that does not overlap the app window. Has PREVENT_SPLITTING flag + * - A spy window that does not overlap the app window. * - A window below the spy that has NO_INPUT_CHANNEL (call it 'inputSink') * * The spy window is side-by-side with the app window. The inputSink is below the spy. @@ -4683,34 +4703,31 @@ class SpyThatPreventsSplittingWithApplicationFixture : public InputDispatcherTes * Only the SPY window should get the DOWN event. * The spy pilfers after receiving the first DOWN event. * Next, we touch the app window. - * The spy should receive POINTER_DOWN(1) (since spy is preventing splits). - * Also, since the spy is already pilfering the first pointer, it will be sent the remaining new - * pointers automatically, as well. + * The spy should not receive POINTER_DOWN(1) (since the pointer is outside of the spy). * Next, the first pointer (from the spy) is lifted. - * Spy should get POINTER_UP(0). + * Spy should get ACTION_UP. * This event should not go to the app because the app never received this pointer to begin with. - * Now, lift the remaining pointer and check that the spy receives UP event. + * However, due to the current implementation, this would still cause an ACTION_MOVE event in the + * app. + * Now, lift the remaining pointer and check that the app receives UP event. * - * Finally, send a new ACTION_DOWN event to the spy and check that it's received. + * Finally, send a new ACTION_DOWN event to the spy and check that it gets received. * This test attempts to reproduce a crash in the dispatcher. */ -TEST_P(SpyThatPreventsSplittingWithApplicationFixture, SpyThatPreventsSplittingWithApplication) { - SCOPED_FLAG_OVERRIDE(split_all_touches, false); +TEST_F(InputDispatcherTest, SpyThatPilfersAfterFirstPointerWithTwoOtherWindows) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> spyWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Spy", ui::LogicalDisplayId::DEFAULT); spyWindow->setFrame(Rect(100, 100, 200, 200)); spyWindow->setTrustedOverlay(true); - spyWindow->setPreventSplitting(true); spyWindow->setSpy(true); - // Another window below spy that has both NO_INPUT_CHANNEL and PREVENT_SPLITTING + // Another window below spy that has NO_INPUT_CHANNEL sp<FakeWindowHandle> inputSinkWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Input sink below spy", ui::LogicalDisplayId::DEFAULT); inputSinkWindow->setFrame(Rect(100, 100, 200, 200)); // directly below the spy inputSinkWindow->setTrustedOverlay(true); - inputSinkWindow->setPreventSplitting(GetParam()); inputSinkWindow->setNoInputChannel(true); sp<FakeWindowHandle> appWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "App", @@ -4732,16 +4749,15 @@ TEST_P(SpyThatPreventsSplittingWithApplicationFixture, SpyThatPreventsSplittingW mDispatcher->pilferPointers(spyWindow->getToken()); - // Second finger lands in the app, and goes to the spy window. It doesn't go to the app because - // the spy is already pilfering the first pointer, and this automatically grants the remaining - // new pointers to the spy, as well. + // Second finger lands in the app. It goes to the app as ACTION_DOWN. mDispatcher->notifyMotion( MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(150).y(150)) .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50)) .build()); - spyWindow->consumeMotionPointerDown(1, WithPointerCount(2)); + spyWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithPointerCount(1))); + appWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); // Now lift up the first pointer mDispatcher->notifyMotion( @@ -4749,14 +4765,15 @@ TEST_P(SpyThatPreventsSplittingWithApplicationFixture, SpyThatPreventsSplittingW .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(150).y(150)) .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50)) .build()); - spyWindow->consumeMotionPointerUp(0, WithPointerCount(2)); + spyWindow->consumeMotionEvent(WithMotionAction(ACTION_UP)); + appWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithPointerCount(1))); // And lift the remaining pointer! mDispatcher->notifyMotion( MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN) .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50)) .build()); - spyWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP), WithPointerCount(1))); + appWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP), WithPointerCount(1))); // Now send a new DOWN, which should again go to spy. mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) @@ -4768,10 +4785,6 @@ TEST_P(SpyThatPreventsSplittingWithApplicationFixture, SpyThatPreventsSplittingW appWindow->assertNoEvents(); } -// Behaviour should be the same regardless of whether inputSink supports splitting. -INSTANTIATE_TEST_SUITE_P(SpyThatPreventsSplittingWithApplication, - SpyThatPreventsSplittingWithApplicationFixture, testing::Bool()); - TEST_F(InputDispatcherTest, HoverWithSpyWindows) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); @@ -5725,14 +5738,12 @@ TEST_F(InputDispatcherTest, OnWindowInfosChanged_RemoveAllWindowsOnDisplay) { window->assertNoEvents(); } -TEST_F(InputDispatcherTest, NonSplitTouchableWindowReceivesMultiTouch) { - SCOPED_FLAG_OVERRIDE(split_all_touches, false); +TEST_F(InputDispatcherTest, WindowDoesNotReceiveSecondPointerOutsideOfItsBounds) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window", ui::LogicalDisplayId::DEFAULT); - // Ensure window is non-split and have some transform. - window->setPreventSplitting(true); + // Ensure window has a non-trivial transform. window->setWindowOffset(20, 40); mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); @@ -5740,7 +5751,10 @@ TEST_F(InputDispatcherTest, NonSplitTouchableWindowReceivesMultiTouch) { injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT, {50, 50})) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - window->consumeMotionDown(ui::LogicalDisplayId::DEFAULT); + window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), + WithCoords(70, // 50 + 20 + 90 // 50 + 40 + ))); const MotionEvent secondFingerDownEvent = MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) @@ -5749,45 +5763,32 @@ TEST_F(InputDispatcherTest, NonSplitTouchableWindowReceivesMultiTouch) { .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(50)) .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(-30).y(-50)) .build(); - ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + ASSERT_EQ(InputEventInjectionResult::FAILED, injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT, InputEventInjectionSync::WAIT_FOR_RESULT)) - << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - - std::unique_ptr<MotionEvent> event = window->consumeMotionEvent(); - ASSERT_NE(nullptr, event); - EXPECT_EQ(POINTER_1_DOWN, event->getAction()); - EXPECT_EQ(70, event->getX(0)); // 50 + 20 - EXPECT_EQ(90, event->getY(0)); // 50 + 40 - EXPECT_EQ(-10, event->getX(1)); // -30 + 20 - EXPECT_EQ(-10, event->getY(1)); // -50 + 40 + << "Injection should fail because the second finger is outside of any window on the " + "screen."; } /** - * Two windows: a splittable and a non-splittable. - * The non-splittable window shouldn't receive any "incomplete" gestures. - * Send the first pointer to the splittable window, and then touch the non-splittable window. - * The second pointer should be dropped because the initial window is splittable, so it won't get - * any pointers outside of it, and the second window is non-splittable, so it shouldn't get any - * "incomplete" gestures. + * Two windows. + * Send the first pointer to the left window, and then touch the right window. + * The second pointer should generate an ACTION_DOWN in the right window. */ -TEST_F(InputDispatcherTest, SplittableAndNonSplittableWindows) { - SCOPED_FLAG_OVERRIDE(split_all_touches, false); +TEST_F(InputDispatcherTest, TwoWindowsTwoPointers) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> leftWindow = - sp<FakeWindowHandle>::make(application, mDispatcher, "Left splittable Window", + sp<FakeWindowHandle>::make(application, mDispatcher, "Left Window", ui::LogicalDisplayId::DEFAULT); - leftWindow->setPreventSplitting(false); leftWindow->setFrame(Rect(0, 0, 100, 100)); sp<FakeWindowHandle> rightWindow = - sp<FakeWindowHandle>::make(application, mDispatcher, "Right non-splittable Window", + sp<FakeWindowHandle>::make(application, mDispatcher, "Right Window", ui::LogicalDisplayId::DEFAULT); - rightWindow->setPreventSplitting(true); rightWindow->setFrame(Rect(100, 100, 200, 200)); mDispatcher->onWindowInfosChanged( {{*leftWindow->getInfo(), *rightWindow->getInfo()}, {}, 0, 0}); - // Touch down on left, splittable window + // Touch down on left window mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) .build()); @@ -5798,8 +5799,8 @@ TEST_F(InputDispatcherTest, SplittableAndNonSplittableWindows) { .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(50)) .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(150).y(150)) .build()); - leftWindow->assertNoEvents(); - rightWindow->assertNoEvents(); + leftWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE)); + rightWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); } /** @@ -5822,7 +5823,6 @@ TEST_F(InputDispatcherTest, WallpaperWindowWhenSlipperyAndMultiWindowMultiTouch) sp<FakeWindowHandle>::make(application1, mDispatcher, "wallpaper", ui::LogicalDisplayId::DEFAULT); wallpaper->setIsWallpaper(true); - wallpaper->setPreventSplitting(true); wallpaper->setTouchable(false); sp<FakeWindowHandle> leftWindow = sp<FakeWindowHandle>::make(application2, mDispatcher, "Left", @@ -5956,7 +5956,6 @@ TEST_F(InputDispatcherTest, WallpaperWindowWhenSlipperyAndMultiWindowMultiTouch_ sp<FakeWindowHandle>::make(application1, mDispatcher, "wallpaper", ui::LogicalDisplayId::DEFAULT); wallpaper->setIsWallpaper(true); - wallpaper->setPreventSplitting(true); wallpaper->setTouchable(false); sp<FakeWindowHandle> leftWindow = sp<FakeWindowHandle>::make(application2, mDispatcher, "Left", @@ -6070,20 +6069,18 @@ TEST_F(InputDispatcherTest, WallpaperWindowWhenSlipperyAndMultiWindowMultiTouch_ } /** - * Two windows: left and right. The left window has PREVENT_SPLITTING input config. Device A sends a - * down event to the right window. Device B sends a down event to the left window, and then a - * POINTER_DOWN event to the right window. However, since the left window prevents splitting, the - * POINTER_DOWN event should only go to the left window, and not to the right window. + * Two windows: left and right. Device A sends a DOWN event to the right window. Device B sends a + * DOWN event to the left window, and then a POINTER_DOWN event outside of all windows. + * The POINTER_DOWN event should be dropped. * This test attempts to reproduce a crash. */ -TEST_F(InputDispatcherTest, MultiDeviceTwoWindowsPreventSplitting) { - SCOPED_FLAG_OVERRIDE(split_all_touches, false); +TEST_F(InputDispatcherTest, MultiDeviceTwoWindowsTwoPointers) { + SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, true); std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> leftWindow = - sp<FakeWindowHandle>::make(application, mDispatcher, "Left window (prevent splitting)", + sp<FakeWindowHandle>::make(application, mDispatcher, "Left window", ui::LogicalDisplayId::DEFAULT); leftWindow->setFrame(Rect(0, 0, 100, 100)); - leftWindow->setPreventSplitting(true); sp<FakeWindowHandle> rightWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Right window", @@ -6107,14 +6104,14 @@ TEST_F(InputDispatcherTest, MultiDeviceTwoWindowsPreventSplitting) { .deviceId(deviceB) .build()); leftWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceB))); - // Send a second pointer from device B to the right window. It shouldn't go to the right window - // because the left window prevents splitting. + + // Send a second pointer from device B to an area outside of all windows. mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) .deviceId(deviceB) .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) .pointer(PointerBuilder(1, ToolType::FINGER).x(120).y(120)) .build()); - leftWindow->consumeMotionPointerDown(1, WithDeviceId(deviceB)); + // This is dropped because there's no touchable window at the location (120, 120) // Finish the gesture for both devices mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN) @@ -6122,7 +6119,8 @@ TEST_F(InputDispatcherTest, MultiDeviceTwoWindowsPreventSplitting) { .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) .pointer(PointerBuilder(1, ToolType::FINGER).x(120).y(120)) .build()); - leftWindow->consumeMotionPointerUp(1, WithDeviceId(deviceB)); + leftWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithPointerId(0, 0))); + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN) .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) .deviceId(deviceB) @@ -6501,19 +6499,47 @@ TEST_F(InputDispatcherDisplayProjectionTest, WindowGetsEventsInCorrectCoordinate ui::LogicalDisplayId::DEFAULT, {PointF{150, 220}})); firstWindow->assertNoEvents(); - std::unique_ptr<MotionEvent> event = secondWindow->consumeMotionEvent(); - ASSERT_NE(nullptr, event); - EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, event->getAction()); + secondWindow->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_DOWN), + // Ensure that the events from the "getRaw" API are in logical display + // coordinates, which has an x-scale of 2 and y-scale of 4. + WithRawCoords(300, 880), + // Ensure that the x and y values are in the window's coordinate space. + // The left-top of the second window is at (100, 200) in display space, which is + // (200, 800) in the logical display space. This will be the origin of the window + // space. + WithCoords(100, 80))); +} - // Ensure that the events from the "getRaw" API are in logical display coordinates. - EXPECT_EQ(300, event->getRawX(0)); - EXPECT_EQ(880, event->getRawY(0)); +TEST_F(InputDispatcherDisplayProjectionTest, UseCloneLayerStackTransformForRawCoordinates) { + SCOPED_FLAG_OVERRIDE(use_cloned_screen_coordinates_as_raw, true); - // Ensure that the x and y values are in the window's coordinate space. - // The left-top of the second window is at (100, 200) in display space, which is (200, 800) in - // the logical display space. This will be the origin of the window space. - EXPECT_EQ(100, event->getX(0)); - EXPECT_EQ(80, event->getY(0)); + auto [firstWindow, secondWindow] = setupScaledDisplayScenario(); + + const std::array<float, 9> matrix = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 0.0, 0.0, 1.0}; + ui::Transform secondDisplayTransform; + secondDisplayTransform.set(matrix); + addDisplayInfo(SECOND_DISPLAY_ID, secondDisplayTransform); + + // When a clone layer stack transform is provided for a window, we should use that as the + // "display transform" for input going to that window. + sp<FakeWindowHandle> secondWindowClone = secondWindow->clone(SECOND_DISPLAY_ID); + secondWindowClone->editInfo()->cloneLayerStackTransform = ui::Transform(); + secondWindowClone->editInfo()->cloneLayerStackTransform->set(0.5, 0, 0, 0.25); + addWindow(secondWindowClone); + + // Touch down on the clone window, and ensure its raw coordinates use + // the clone layer stack transform. + mDispatcher->notifyMotion(generateMotionArgs(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + SECOND_DISPLAY_ID, {PointF{150, 220}})); + secondWindowClone->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_DOWN), + // Ensure the x and y coordinates are in the window's coordinate space. + // See previous test case for calculation. + WithCoords(100, 80), + // Ensure the "getRaw" API uses the clone layer stack transform when it is + // provided for the window. It has an x-scale of 0.5 and y-scale of 0.25. + WithRawCoords(75, 55))); } TEST_F(InputDispatcherDisplayProjectionTest, CancelMotionWithCorrectCoordinates) { @@ -6949,7 +6975,7 @@ TEST_P(TransferTouchFixture, TransferTouch_MultipleWindowsWithSpy) { AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); } -TEST_P(TransferTouchFixture, TransferTouch_TwoPointersNonSplitTouch) { +TEST_P(TransferTouchFixture, TransferTouch_TwoPointers) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); PointF touchPoint = {10, 10}; @@ -6958,11 +6984,9 @@ TEST_P(TransferTouchFixture, TransferTouch_TwoPointersNonSplitTouch) { sp<FakeWindowHandle> firstWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "First Window", ui::LogicalDisplayId::DEFAULT); - firstWindow->setPreventSplitting(true); sp<FakeWindowHandle> secondWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Second Window", ui::LogicalDisplayId::DEFAULT); - secondWindow->setPreventSplitting(true); // Add the windows to the dispatcher mDispatcher->onWindowInfosChanged( @@ -8819,12 +8843,10 @@ TEST_F(InputDispatcherTest, HoverEnterExitSynthesisUsesNewEventId) { } /** - * When a device reports a DOWN event, which lands in a window that supports splits, and then the - * device then reports a POINTER_DOWN, which lands in the location of a non-existing window, then - * the previous window should receive this event and not be dropped. + * First finger lands into a window, and then the second finger lands in the location of a + * non-existent window. The second finger should be dropped. */ TEST_F(InputDispatcherMultiDeviceTest, SingleDevicePointerDownEventRetentionWithoutWindowTarget) { - SCOPED_FLAG_OVERRIDE(split_all_touches, false); std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ui::LogicalDisplayId::DEFAULT); @@ -8842,13 +8864,13 @@ TEST_F(InputDispatcherMultiDeviceTest, SingleDevicePointerDownEventRetentionWith .pointer(PointerBuilder(1, ToolType::FINGER).x(200).y(200)) .build()); - window->consumeMotionEvent(AllOf(WithMotionAction(POINTER_1_DOWN))); + window->assertNoEvents(); } /** * When deviceA reports a DOWN event, which lands in a window that supports splits, and then deviceB * also reports a DOWN event, which lands in the location of a non-existing window, then the - * previous window should receive deviceB's event and it should be dropped. + * previous window should not receive deviceB's event and it should be dropped. */ TEST_F(InputDispatcherMultiDeviceTest, SecondDeviceDownEventDroppedWithoutWindowTarget) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); @@ -12649,9 +12671,6 @@ TEST_F(InputDispatcherDragTests, DragAndDropOnInvalidWindow) { } TEST_F(InputDispatcherDragTests, NoDragAndDropWhenMultiFingers) { - // Ensure window could track pointerIds if it didn't support split touch. - mWindow->setPreventSplitting(true); - ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT, {50, 50})) @@ -13551,14 +13570,10 @@ TEST_F(InputDispatcherSpyWindowTest, ReceivesSecondPointerAsDown) { } /** - * The spy window should not be able to affect whether or not touches are split. Only the foreground - * windows should be allowed to control split touch. + * The spy window should not be able to affect whether or not touches are split. */ TEST_F(InputDispatcherSpyWindowTest, SplitIfNoForegroundWindowTouched) { - // This spy window prevents touch splitting. However, we still expect to split touches - // because a foreground window has not disabled splitting. auto spy = createSpy(); - spy->setPreventSplitting(true); auto window = createForeground(); window->setFrame(Rect(0, 0, 100, 100)); @@ -14684,4 +14699,219 @@ 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..470e65b8b5 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -28,6 +28,7 @@ #include <MultiTouchInputMapper.h> #include <NotifyArgsBuilders.h> #include <PeripheralController.h> +#include <ScopedFlagOverride.h> #include <SingleTouchInputMapper.h> #include <TestEventMatchers.h> #include <TestInputListener.h> @@ -2474,10 +2475,10 @@ 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)); @@ -4526,6 +4527,10 @@ TEST_F(SingleTouchInputMapperTest, Process_ShouldHandleAllToolTypes) { NotifyMotionArgs motionArgs; + // Hold down the mouse button for the duration of the test, since the mouse tools require + // the button to be pressed to make sure they are not hovering. + processKey(mapper, BTN_MOUSE, 1); + // default tool type is finger processDown(mapper, 100, 200); processSync(mapper); @@ -4533,6 +4538,9 @@ TEST_F(SingleTouchInputMapperTest, Process_ShouldHandleAllToolTypes) { ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS))); + // eraser processKey(mapper, BTN_TOOL_RUBBER, 1); processSync(mapper); @@ -7175,6 +7183,10 @@ TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleAllToolTypes) { NotifyMotionArgs motionArgs; + // Hold down the mouse button for the duration of the test, since the mouse tools require + // the button to be pressed to make sure they are not hovering. + processKey(mapper, BTN_MOUSE, 1); + // default tool type is finger processId(mapper, 1); processPosition(mapper, 100, 200); @@ -7183,6 +7195,9 @@ TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleAllToolTypes) { ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS))); + // eraser processKey(mapper, BTN_TOOL_RUBBER, 1); processSync(mapper); @@ -7520,6 +7535,7 @@ TEST_F(MultiTouchInputMapperTest, Configure_AssignsDisplayUniqueId) { } TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShouldHandleDisplayId) { + SCOPED_FLAG_OVERRIDE(disable_touch_input_mapper_pointer_usage, true); prepareSecondaryDisplay(ViewportType::EXTERNAL); prepareDisplay(ui::ROTATION_0); @@ -7532,9 +7548,9 @@ TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShouldHandleDisplayId) { processPosition(mapper, 100, 100); processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(ui::LogicalDisplayId::INVALID, motionArgs.displayId); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithDisplayId(DISPLAY_ID), + WithSource(AINPUT_SOURCE_MOUSE), WithToolType(ToolType::FINGER)))); } /** @@ -8604,6 +8620,8 @@ protected: * fingers start to move downwards, the gesture should be swipe. */ TEST_F(MultiTouchPointerModeTest, PointerGestureMaxSwipeWidthSwipe) { + SCOPED_FLAG_OVERRIDE(disable_touch_input_mapper_pointer_usage, false); + // The min freeform gesture width is 25units/mm x 30mm = 750 // which is greater than fraction of the diagnal length of the touchpad (349). // Thus, MaxSwipWidth is 750. @@ -8664,6 +8682,8 @@ TEST_F(MultiTouchPointerModeTest, PointerGestureMaxSwipeWidthSwipe) { * the gesture should be swipe. */ TEST_F(MultiTouchPointerModeTest, PointerGestureMaxSwipeWidthLowResolutionSwipe) { + SCOPED_FLAG_OVERRIDE(disable_touch_input_mapper_pointer_usage, false); + // The min freeform gesture width is 5units/mm x 30mm = 150 // which is greater than fraction of the diagnal length of the touchpad (349). // Thus, MaxSwipWidth is the fraction of the diagnal length, 349. @@ -8723,6 +8743,8 @@ TEST_F(MultiTouchPointerModeTest, PointerGestureMaxSwipeWidthLowResolutionSwipe) * freeform gestures after two fingers start to move downwards. */ TEST_F(MultiTouchPointerModeTest, PointerGestureMaxSwipeWidthFreeform) { + SCOPED_FLAG_OVERRIDE(disable_touch_input_mapper_pointer_usage, false); + preparePointerMode(/*xResolution=*/25, /*yResolution=*/25); MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>(); @@ -8818,6 +8840,8 @@ TEST_F(MultiTouchPointerModeTest, PointerGestureMaxSwipeWidthFreeform) { } TEST_F(MultiTouchPointerModeTest, TwoFingerSwipeOffsets) { + SCOPED_FLAG_OVERRIDE(disable_touch_input_mapper_pointer_usage, false); + preparePointerMode(/*xResolution=*/25, /*yResolution=*/25); MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>(); NotifyMotionArgs motionArgs; @@ -8864,6 +8888,8 @@ TEST_F(MultiTouchPointerModeTest, TwoFingerSwipeOffsets) { } TEST_F(MultiTouchPointerModeTest, WhenViewportActiveStatusChanged_PointerGestureIsReset) { + SCOPED_FLAG_OVERRIDE(disable_touch_input_mapper_pointer_usage, false); + preparePointerMode(/*xResolution=*/25, /*yResolution=*/25); mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOOL_PEN, 0, AKEYCODE_UNKNOWN, 0); MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>(); diff --git a/services/inputflinger/tests/MultiTouchInputMapper_test.cpp b/services/inputflinger/tests/MultiTouchInputMapper_test.cpp index 9a6b266b21..d15048dddf 100644 --- a/services/inputflinger/tests/MultiTouchInputMapper_test.cpp +++ b/services/inputflinger/tests/MultiTouchInputMapper_test.cpp @@ -23,6 +23,7 @@ #include "InputMapperTest.h" #include "InterfaceMocks.h" +#include "ScopedFlagOverride.h" #include "TestEventMatchers.h" #define TAG "MultiTouchpadInputMapperUnit_test" @@ -30,6 +31,7 @@ namespace android { using testing::_; +using testing::AllOf; using testing::IsEmpty; using testing::Return; using testing::SetArgPointee; @@ -266,4 +268,94 @@ TEST_F(MultiTouchInputMapperUnitTest, MultiFingerGestureWithUnexpectedReset) { VariantWith<NotifyMotionArgs>(WithMotionAction(AMOTION_EVENT_ACTION_UP)))); } +class MultiTouchInputMapperPointerModeUnitTest : public MultiTouchInputMapperUnitTest { +protected: + void SetUp() override { + MultiTouchInputMapperUnitTest::SetUp(); + + // TouchInputMapper goes into POINTER mode whenever INPUT_PROP_DIRECT is not set. + EXPECT_CALL(mMockEventHub, hasInputProperty(EVENTHUB_ID, INPUT_PROP_DIRECT)) + .WillRepeatedly(Return(false)); + + mMapper = createInputMapper<MultiTouchInputMapper>(*mDeviceContext, + mFakePolicy->getReaderConfiguration()); + } +}; + +TEST_F(MultiTouchInputMapperPointerModeUnitTest, MouseToolOnlyDownWhenMouseButtonsAreDown) { + SCOPED_FLAG_OVERRIDE(disable_touch_input_mapper_pointer_usage, true); + + std::list<NotifyArgs> args; + + // Set the tool type to mouse. + args += processKey(BTN_TOOL_MOUSE, 1); + + args += processPosition(100, 100); + args += processId(1); + ASSERT_THAT(args, IsEmpty()); + + args = processSync(); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithToolType(ToolType::MOUSE))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithToolType(ToolType::MOUSE))))); + + // Setting BTN_TOUCH does not make a mouse pointer go down. + args = processKey(BTN_TOUCH, 1); + args += processSync(); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)))); + + // The mouse button is pressed, so the mouse goes down. + args = processKey(BTN_MOUSE, 1); + args += processSync(); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), + WithToolType(ToolType::MOUSE))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithToolType(ToolType::MOUSE), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithToolType(ToolType::MOUSE), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY))))); + + // The mouse button is released, so the mouse starts hovering. + args = processKey(BTN_MOUSE, 0); + args += processSync(); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithButtonState(0), WithToolType(ToolType::MOUSE), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithToolType(ToolType::MOUSE), WithButtonState(0))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithToolType(ToolType::MOUSE))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithToolType(ToolType::MOUSE))))); + + // Change the tool type so that it is no longer a mouse. + // The default tool type is finger, and the finger is already down. + args = processKey(BTN_TOOL_MOUSE, 0); + args += processSync(); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), + WithToolType(ToolType::MOUSE))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithToolType(ToolType::FINGER))))); +} + } // namespace android diff --git a/services/inputflinger/tests/ScopedFlagOverride.h b/services/inputflinger/tests/ScopedFlagOverride.h new file mode 100644 index 0000000000..883673c2f5 --- /dev/null +++ b/services/inputflinger/tests/ScopedFlagOverride.h @@ -0,0 +1,58 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <com_android_input_flags.h> +#include <functional> + +namespace android { + +/** + * Provide a local override for a flag value. The value is restored when the object of this class + * goes out of scope. + * This class is not intended to be used directly, because its usage is cumbersome. + * Instead, a wrapper macro SCOPED_FLAG_OVERRIDE is provided. + */ +class ScopedFlagOverride { +public: + ScopedFlagOverride(std::function<bool()> read, std::function<void(bool)> write, bool value) + : mInitialValue(read()), mWriteValue(write) { + mWriteValue(value); + } + ~ScopedFlagOverride() { mWriteValue(mInitialValue); } + +private: + const bool mInitialValue; + std::function<void(bool)> mWriteValue; +}; + +typedef bool (*ReadFlagValueFunction)(); +typedef void (*WriteFlagValueFunction)(bool); + +/** + * Use this macro to locally override a flag value. + * Example usage: + * SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false); + * Note: this works by creating a local variable in your current scope. Don't call this twice for + * the same flag, because the variable names will clash! + */ +#define SCOPED_FLAG_OVERRIDE(NAME, VALUE) \ + ReadFlagValueFunction read##NAME = com::android::input::flags::NAME; \ + WriteFlagValueFunction write##NAME = com::android::input::flags::NAME; \ + ScopedFlagOverride override##NAME(read##NAME, write##NAME, (VALUE)) + +} // namespace android diff --git a/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp index 79a5ff6e5f..31db2fedc7 100644 --- a/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp +++ b/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp @@ -76,7 +76,6 @@ void scrambleWindow(FuzzedDataProvider& fdp, FakeWindowHandle& window) { window.setDupTouchToWallpaper(fdp.ConsumeBool()); window.setIsWallpaper(fdp.ConsumeBool()); window.setVisible(fdp.ConsumeBool()); - window.setPreventSplitting(fdp.ConsumeBool()); const bool isTrustedOverlay = fdp.ConsumeBool(); window.setTrustedOverlay(isTrustedOverlay); if (isTrustedOverlay) { diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h index e32cc02974..fd58191ed2 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h @@ -53,7 +53,7 @@ public: createLayerFECompositionState() = 0; virtual HWComposer& getHwComposer() const = 0; - virtual void setHwComposer(std::unique_ptr<HWComposer>) = 0; + virtual void setHwComposer(HWComposer*) = 0; virtual renderengine::RenderEngine& getRenderEngine() const = 0; virtual void setRenderEngine(renderengine::RenderEngine*) = 0; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h index cda4edc216..20aceb1a82 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h @@ -131,6 +131,9 @@ public: // Currently latched frame number, 0 if invalid. uint64_t frameNumber = 0; + + // layer serial number, -1 if invalid. + int32_t sequence = -1; }; // Describes the states of the release fence. Checking the states allows checks diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h index 45208dd3c7..2992b6d843 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h @@ -31,7 +31,7 @@ public: override; HWComposer& getHwComposer() const override; - void setHwComposer(std::unique_ptr<HWComposer>) override; + void setHwComposer(HWComposer*) override; renderengine::RenderEngine& getRenderEngine() const override; void setRenderEngine(renderengine::RenderEngine*) override; @@ -59,7 +59,7 @@ public: void setNeedsAnotherUpdateForTest(bool); private: - std::unique_ptr<HWComposer> mHwComposer; + HWComposer* mHwComposer; renderengine::RenderEngine* mRenderEngine; std::shared_ptr<TimeStats> mTimeStats; bool mNeedsAnotherUpdate = false; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h index a1b728232c..bb1a2227be 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h @@ -37,7 +37,7 @@ public: std::unique_ptr<compositionengine::LayerFECompositionState>()); MOCK_CONST_METHOD0(getHwComposer, HWComposer&()); - MOCK_METHOD1(setHwComposer, void(std::unique_ptr<HWComposer>)); + MOCK_METHOD1(setHwComposer, void(HWComposer*)); MOCK_CONST_METHOD0(getRenderEngine, renderengine::RenderEngine&()); MOCK_METHOD1(setRenderEngine, void(renderengine::RenderEngine*)); diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp index cfcce473a2..989f8e3c5e 100644 --- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp +++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp @@ -58,11 +58,11 @@ CompositionEngine::createLayerFECompositionState() { } HWComposer& CompositionEngine::getHwComposer() const { - return *mHwComposer.get(); + return *mHwComposer; } -void CompositionEngine::setHwComposer(std::unique_ptr<HWComposer> hwComposer) { - mHwComposer = std::move(hwComposer); +void CompositionEngine::setHwComposer(HWComposer* hwComposer) { + mHwComposer = hwComposer; } renderengine::RenderEngine& CompositionEngine::getRenderEngine() const { diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp index 3e0c390a5d..ad65c4422e 100644 --- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp @@ -61,7 +61,7 @@ TEST_F(CompositionEngineTest, canInstantiateCompositionEngine) { TEST_F(CompositionEngineTest, canSetHWComposer) { android::mock::HWComposer* hwc = new StrictMock<android::mock::HWComposer>(); - mEngine.setHwComposer(std::unique_ptr<android::HWComposer>(hwc)); + mEngine.setHwComposer(static_cast<android::HWComposer*>(hwc)); EXPECT_EQ(hwc, &mEngine.getHwComposer()); } diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h index 47d0041a8b..4fdbae1831 100644 --- a/services/surfaceflinger/FrontEnd/LayerHierarchy.h +++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h @@ -102,6 +102,8 @@ public: // Returns true if the node is a clone. bool isClone() const { return !mirrorRootIds.empty(); } + TraversalPath getClonedFrom() const { return {.id = id, .variant = variant}; } + bool operator==(const TraversalPath& other) const { return id == other.id && mirrorRootIds == other.mirrorRootIds; } diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp index 367132c113..42f3202f20 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp @@ -398,9 +398,13 @@ void LayerSnapshot::merge(const RequestedLayerState& requested, bool forceUpdate if (forceUpdate || requested.what & layer_state_t::eSidebandStreamChanged) { sidebandStream = requested.sidebandStream; } - if (forceUpdate || requested.what & layer_state_t::eShadowRadiusChanged) { - shadowSettings.length = requested.shadowRadius; + if (forceUpdate || requested.what & layer_state_t::eShadowRadiusChanged || + requested.what & layer_state_t::eClientDrawnShadowsChanged) { + shadowSettings.length = + requested.clientDrawnShadowRadius > 0 ? 0.f : requested.shadowRadius; + shadowSettings.clientDrawnLength = requested.clientDrawnShadowRadius; } + if (forceUpdate || requested.what & layer_state_t::eFrameRateSelectionPriority) { frameRateSelectionPriority = requested.frameRateSelectionPriority; } diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h index b8df3ed748..68b13959a8 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h +++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h @@ -28,16 +28,23 @@ namespace android::surfaceflinger::frontend { struct RoundedCornerState { RoundedCornerState() = default; - RoundedCornerState(const FloatRect& cropRect, const vec2& radius) - : cropRect(cropRect), radius(radius) {} // Rounded rectangle in local layer coordinate space. FloatRect cropRect = FloatRect(); - // Radius of the rounded rectangle. + // Radius of the rounded rectangle for composition vec2 radius; + // Requested radius of the rounded rectangle + vec2 requestedRadius; + // Radius drawn by client for the rounded rectangle + vec2 clientDrawnRadius; + bool hasClientDrawnRadius() const { + return clientDrawnRadius.x > 0.0f && clientDrawnRadius.y > 0.0f; + } + bool hasRequestedRadius() const { return requestedRadius.x > 0.0f && requestedRadius.y > 0.0f; } bool hasRoundedCorners() const { return radius.x > 0.0f && radius.y > 0.0f; } bool operator==(RoundedCornerState const& rhs) const { - return cropRect == rhs.cropRect && radius == rhs.radius; + return cropRect == rhs.cropRect && radius == rhs.radius && + clientDrawnRadius == rhs.clientDrawnRadius; } }; diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp index 022588deef..0be3a1153d 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp @@ -16,6 +16,8 @@ // #define LOG_NDEBUG 0 #define ATRACE_TAG ATRACE_TAG_GRAPHICS +#include "FrontEnd/LayerSnapshot.h" +#include "ui/Transform.h" #undef LOG_TAG #define LOG_TAG "SurfaceFlinger" @@ -25,6 +27,7 @@ #include <common/FlagManager.h> #include <common/trace.h> #include <ftl/small_map.h> +#include <math/vec2.h> #include <ui/DisplayMap.h> #include <ui/FloatRect.h> @@ -932,7 +935,8 @@ void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& a if (forceUpdate || snapshot.clientChanges & layer_state_t::eCornerRadiusChanged || snapshot.changes.any(RequestedLayerState::Changes::Geometry | - RequestedLayerState::Changes::BufferUsageFlags)) { + RequestedLayerState::Changes::BufferUsageFlags) || + snapshot.clientChanges & layer_state_t::eClientDrawnCornerRadiusChanged) { updateRoundedCorner(snapshot, requested, parentSnapshot, args); } @@ -972,7 +976,7 @@ void LayerSnapshotBuilder::updateRoundedCorner(LayerSnapshot& snapshot, } snapshot.roundedCorner = RoundedCornerState(); RoundedCornerState parentRoundedCorner; - if (parentSnapshot.roundedCorner.hasRoundedCorners()) { + if (parentSnapshot.roundedCorner.hasRequestedRadius()) { parentRoundedCorner = parentSnapshot.roundedCorner; ui::Transform t = snapshot.localTransform.inverse(); parentRoundedCorner.cropRect = t.transform(parentRoundedCorner.cropRect); @@ -981,10 +985,16 @@ void LayerSnapshotBuilder::updateRoundedCorner(LayerSnapshot& snapshot, } FloatRect layerCropRect = snapshot.croppedBufferSize; - const vec2 radius(requested.cornerRadius, requested.cornerRadius); - RoundedCornerState layerSettings(layerCropRect, radius); - const bool layerSettingsValid = layerSettings.hasRoundedCorners() && !layerCropRect.isEmpty(); - const bool parentRoundedCornerValid = parentRoundedCorner.hasRoundedCorners(); + const vec2 requestedRadius(requested.cornerRadius, requested.cornerRadius); + const vec2 clientDrawnRadius(requested.clientDrawnCornerRadius, + requested.clientDrawnCornerRadius); + RoundedCornerState layerSettings; + layerSettings.cropRect = layerCropRect; + layerSettings.requestedRadius = requestedRadius; + layerSettings.clientDrawnRadius = clientDrawnRadius; + + const bool layerSettingsValid = layerSettings.hasRequestedRadius() && !layerCropRect.isEmpty(); + const bool parentRoundedCornerValid = parentRoundedCorner.hasRequestedRadius(); if (layerSettingsValid && parentRoundedCornerValid) { // If the parent and the layer have rounded corner settings, use the parent settings if // the parent crop is entirely inside the layer crop. This has limitations and cause @@ -1002,6 +1012,14 @@ void LayerSnapshotBuilder::updateRoundedCorner(LayerSnapshot& snapshot, } else if (parentRoundedCornerValid) { snapshot.roundedCorner = parentRoundedCorner; } + + if (snapshot.roundedCorner.requestedRadius.x == requested.clientDrawnCornerRadius) { + // If the client drawn radius matches the requested radius, then surfaceflinger + // does not need to draw rounded corners for this layer + snapshot.roundedCorner.radius = vec2(0.f, 0.f); + } else { + snapshot.roundedCorner.radius = snapshot.roundedCorner.requestedRadius; + } } /** @@ -1205,13 +1223,27 @@ void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot, snapshot.inputInfo.contentSize = {snapshot.croppedBufferSize.getHeight(), snapshot.croppedBufferSize.getWidth()}; - // If the layer is a clone, we need to crop the input region to cloned root to prevent - // touches from going outside the cloned area. + snapshot.inputInfo.cloneLayerStackTransform.reset(); + if (path.isClone()) { snapshot.inputInfo.inputConfig |= InputConfig::CLONE; // Cloned layers shouldn't handle watch outside since their z order is not determined by // WM or the client. snapshot.inputInfo.inputConfig.clear(InputConfig::WATCH_OUTSIDE_TOUCH); + + // Compute the transform that maps the clone's display to the layer stack space of the + // cloned window. + const LayerSnapshot* clonedSnapshot = getSnapshot(path.getClonedFrom()); + if (clonedSnapshot != nullptr) { + const auto& [clonedInputBounds, s] = + getInputBounds(*clonedSnapshot, /*fillParentBounds=*/false); + ui::Transform inputToLayer; + inputToLayer.set(clonedInputBounds.left, clonedInputBounds.top); + const ui::Transform& layerToLayerStack = getInputTransform(*clonedSnapshot); + const auto& displayToInput = snapshot.inputInfo.transform; + snapshot.inputInfo.cloneLayerStackTransform = + layerToLayerStack * inputToLayer * displayToInput; + } } } diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp index ee9302b937..591ebb2043 100644 --- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp +++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp @@ -107,6 +107,8 @@ RequestedLayerState::RequestedLayerState(const LayerCreationArgs& args) hdrMetadata.validTypes = 0; surfaceDamageRegion = Region::INVALID_REGION; cornerRadius = 0.0f; + clientDrawnCornerRadius = 0.0f; + clientDrawnShadowRadius = 0.0f; backgroundBlurRadius = 0; api = -1; hasColorTransform = false; @@ -348,6 +350,16 @@ void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerSta requestedFrameRate.category = category; changes |= RequestedLayerState::Changes::FrameRate; } + + if (clientState.what & layer_state_t::eClientDrawnCornerRadiusChanged) { + clientDrawnCornerRadius = clientState.clientDrawnCornerRadius; + changes |= RequestedLayerState::Changes::Geometry; + } + + if (clientState.what & layer_state_t::eClientDrawnShadowsChanged) { + clientDrawnShadowRadius = clientState.clientDrawnShadowRadius; + changes |= RequestedLayerState::Changes::Geometry; + } } ui::Size RequestedLayerState::getUnrotatedBufferSize(uint32_t displayRotationFlags) const { @@ -624,6 +636,8 @@ bool RequestedLayerState::isSimpleBufferUpdate(const layer_state_t& s) const { const uint64_t deniedChanges = layer_state_t::ePositionChanged | layer_state_t::eAlphaChanged | layer_state_t::eColorTransformChanged | layer_state_t::eBackgroundColorChanged | layer_state_t::eMatrixChanged | layer_state_t::eCornerRadiusChanged | + layer_state_t::eClientDrawnCornerRadiusChanged | + layer_state_t::eClientDrawnShadowsChanged | layer_state_t::eBackgroundBlurRadiusChanged | layer_state_t::eBufferTransformChanged | layer_state_t::eTransformToDisplayInverseChanged | layer_state_t::eCropChanged | layer_state_t::eDataspaceChanged | layer_state_t::eHdrMetadataChanged | diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index c234a75693..6af0f59d51 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -516,11 +516,6 @@ private: bool mGetHandleCalled = false; - // The inherited shadow radius after taking into account the layer hierarchy. This is the - // final shadow radius for this layer. If a shadow is specified for a layer, then effective - // shadow radius is the set shadow radius, otherwise its the parent's shadow radius. - float mEffectiveShadowRadius = 0.f; - // Game mode for the layer. Set by WindowManagerShell and recorded by SurfaceFlingerStats. gui::GameMode mGameMode = gui::GameMode::Unsupported; diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp index fea7671af2..dbb1ed3a9f 100644 --- a/services/surfaceflinger/LayerFE.cpp +++ b/services/surfaceflinger/LayerFE.cpp @@ -191,6 +191,7 @@ void LayerFE::prepareClearClientComposition(LayerFE::LayerSettings& layerSetting layerSettings.disableBlending = true; layerSettings.bufferId = 0; layerSettings.frameNumber = 0; + layerSettings.sequence = -1; // If layer is blacked out, force alpha to 1 so that we draw a black color layer. layerSettings.alpha = blackout ? 1.0f : 0.0f; @@ -262,6 +263,7 @@ void LayerFE::prepareBufferStateClientComposition( layerSettings.source.buffer.maxLuminanceNits = maxLuminance; layerSettings.frameNumber = mSnapshot->frameNumber; layerSettings.bufferId = mSnapshot->externalTexture->getId(); + layerSettings.sequence = mSnapshot->sequence; const bool useFiltering = targetSettings.needsFiltering || mSnapshot->geomLayerTransform.needsBilinearFiltering(); diff --git a/services/surfaceflinger/ScreenCaptureOutput.cpp b/services/surfaceflinger/ScreenCaptureOutput.cpp index 41a9a1bb22..5f71b88d01 100644 --- a/services/surfaceflinger/ScreenCaptureOutput.cpp +++ b/services/surfaceflinger/ScreenCaptureOutput.cpp @@ -16,11 +16,13 @@ #include "ScreenCaptureOutput.h" #include "ScreenCaptureRenderSurface.h" +#include "common/include/common/FlagManager.h" #include "ui/Rotation.h" #include <compositionengine/CompositionEngine.h> #include <compositionengine/DisplayColorProfileCreationArgs.h> #include <compositionengine/impl/DisplayColorProfile.h> +#include <ui/HdrRenderTypeUtils.h> #include <ui/Rotation.h> namespace android { @@ -104,14 +106,84 @@ renderengine::DisplaySettings ScreenCaptureOutput::generateClientCompositionDisp return clientCompositionDisplay; } +std::unordered_map<int32_t, aidl::android::hardware::graphics::composer3::Luts> +ScreenCaptureOutput::generateLuts() { + std::unordered_map<int32_t, aidl::android::hardware::graphics::composer3::Luts> lutsMapper; + if (FlagManager::getInstance().luts_api()) { + std::vector<sp<GraphicBuffer>> buffers; + std::vector<int32_t> layerIds; + + for (const auto* layer : getOutputLayersOrderedByZ()) { + const auto& layerState = layer->getState(); + const auto* layerFEState = layer->getLayerFE().getCompositionState(); + auto pixelFormat = layerFEState->buffer + ? std::make_optional( + static_cast<ui::PixelFormat>(layerFEState->buffer->getPixelFormat())) + : std::nullopt; + const auto hdrType = getHdrRenderType(layerState.dataspace, pixelFormat, + layerFEState->desiredHdrSdrRatio); + if (layerFEState->buffer && !layerFEState->luts && + hdrType == HdrRenderType::GENERIC_HDR) { + buffers.push_back(layerFEState->buffer); + layerIds.push_back(layer->getLayerFE().getSequence()); + } + } + + std::vector<aidl::android::hardware::graphics::composer3::Luts> luts; + if (auto displayDevice = mRenderArea.getDisplayDevice()) { + const auto id = PhysicalDisplayId::tryCast(displayDevice->getId()); + if (id) { + auto& hwc = getCompositionEngine().getHwComposer(); + hwc.getLuts(*id, buffers, &luts); + } + } + + if (buffers.size() == luts.size()) { + for (size_t i = 0; i < luts.size(); i++) { + lutsMapper[layerIds[i]] = std::move(luts[i]); + } + } + } + return lutsMapper; +} + std::vector<compositionengine::LayerFE::LayerSettings> ScreenCaptureOutput::generateClientCompositionRequests( bool supportsProtectedContent, ui::Dataspace outputDataspace, std::vector<compositionengine::LayerFE*>& outLayerFEs) { + // This map maps the layer unique id to a Lut + std::unordered_map<int32_t, aidl::android::hardware::graphics::composer3::Luts> lutsMapper = + generateLuts(); + auto clientCompositionLayers = compositionengine::impl::Output:: generateClientCompositionRequests(supportsProtectedContent, outputDataspace, outLayerFEs); + for (auto& layer : clientCompositionLayers) { + if (lutsMapper.find(layer.sequence) != lutsMapper.end()) { + auto& aidlLuts = lutsMapper[layer.sequence]; + if (aidlLuts.pfd.get() >= 0 && aidlLuts.offsets) { + std::vector<int32_t> offsets = *aidlLuts.offsets; + std::vector<int32_t> dimensions; + dimensions.reserve(offsets.size()); + std::vector<int32_t> sizes; + sizes.reserve(offsets.size()); + std::vector<int32_t> keys; + keys.reserve(offsets.size()); + for (size_t j = 0; j < offsets.size(); j++) { + dimensions.emplace_back( + static_cast<int32_t>(aidlLuts.lutProperties[j].dimension)); + sizes.emplace_back(aidlLuts.lutProperties[j].size); + keys.emplace_back( + static_cast<int32_t>(aidlLuts.lutProperties[j].samplingKeys[0])); + } + layer.luts = std::make_shared<gui::DisplayLuts>(base::unique_fd( + aidlLuts.pfd.dup().get()), + offsets, dimensions, sizes, keys); + } + } + } + if (mRegionSampling) { for (auto& layer : clientCompositionLayers) { layer.backgroundBlurRadius = 0; diff --git a/services/surfaceflinger/ScreenCaptureOutput.h b/services/surfaceflinger/ScreenCaptureOutput.h index c233ead575..444a28fbf0 100644 --- a/services/surfaceflinger/ScreenCaptureOutput.h +++ b/services/surfaceflinger/ScreenCaptureOutput.h @@ -20,6 +20,7 @@ #include <compositionengine/RenderSurface.h> #include <compositionengine/impl/Output.h> #include <ui/Rect.h> +#include <unordered_map> #include "RenderArea.h" @@ -65,6 +66,7 @@ protected: const std::shared_ptr<renderengine::ExternalTexture>& buffer) const override; private: + std::unordered_map<int32_t, aidl::android::hardware::graphics::composer3::Luts> generateLuts(); const RenderArea& mRenderArea; const compositionengine::Output::ColorProfile& mColorProfile; const bool mRegionSampling; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index dd1119a5d1..21c1bd7eda 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -879,6 +879,8 @@ renderengine::RenderEngine::BlurAlgorithm chooseBlurAlgorithm(bool supportsBlur) return renderengine::RenderEngine::BlurAlgorithm::GAUSSIAN; } else if (algorithm == "kawase2") { return renderengine::RenderEngine::BlurAlgorithm::KAWASE_DUAL_FILTER; + } else if (algorithm == "kawase") { + return renderengine::RenderEngine::BlurAlgorithm::KAWASE; } else { if (FlagManager::getInstance().window_blur_kawase2()) { return renderengine::RenderEngine::BlurAlgorithm::KAWASE_DUAL_FILTER; @@ -920,7 +922,8 @@ void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) { mCompositionEngine->setTimeStats(mTimeStats); - mCompositionEngine->setHwComposer(getFactory().createHWComposer(mHwcServiceName)); + mHWComposer = getFactory().createHWComposer(mHwcServiceName); + mCompositionEngine->setHwComposer(mHWComposer.get()); auto& composer = mCompositionEngine->getHwComposer(); composer.setCallback(*this); mDisplayModeController.setHwComposer(&composer); @@ -7697,6 +7700,7 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( std::unique_ptr<compositionengine::CompositionEngine> compositionEngine = mFactory.createCompositionEngine(); compositionEngine->setRenderEngine(mRenderEngine.get()); + compositionEngine->setHwComposer(mHWComposer.get()); std::vector<sp<compositionengine::LayerFE>> layerFEs; layerFEs.reserve(layers.size()); diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index c2e687f385..824a55a652 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -1368,6 +1368,7 @@ private: std::atomic<int> mNumTrustedPresentationListeners = 0; std::unique_ptr<compositionengine::CompositionEngine> mCompositionEngine; + std::unique_ptr<HWComposer> mHWComposer; CompositionCoveragePerDisplay mCompositionCoverage; diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp index f247c9f088..151611cad2 100644 --- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp +++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp @@ -585,6 +585,83 @@ TEST_P(LayerTypeAndRenderTypeTransactionTest, ParentCornerRadiusTakesPrecedence) } } +TEST_P(LayerTypeAndRenderTypeTransactionTest, SetClientDrawnCornerRadius) { + sp<SurfaceControl> layer; + const uint8_t size = 64; + const uint8_t testArea = 4; + const float cornerRadius = 20.0f; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", size, size)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, size, size)); + + Transaction() + .setClientDrawnCornerRadius(layer, cornerRadius) + .setCornerRadius(layer, cornerRadius) + .setCrop(layer, Rect(size, size)) + .apply(); + + { + const uint8_t bottom = size - 1; + const uint8_t right = size - 1; + auto shot = getScreenCapture(); + // Solid corners + shot->expectColor(Rect(0, 0, testArea, testArea), Color::RED); + shot->expectColor(Rect(size - testArea, 0, right, testArea), Color::RED); + shot->expectColor(Rect(0, bottom - testArea, testArea, bottom), Color::RED); + shot->expectColor(Rect(size - testArea, bottom - testArea, right, bottom), Color::RED); + // Solid center + shot->expectColor(Rect(size / 2 - testArea / 2, size / 2 - testArea / 2, + size / 2 + testArea / 2, size / 2 + testArea / 2), + Color::RED); + } +} + +// Test if ParentCornerRadiusTakesPrecedence if the parent's client drawn corner radius crop +// is fully contained by the child corner radius crop. +TEST_P(LayerTypeAndRenderTypeTransactionTest, ParentCornerRadiusPrecedenceClientDrawnCornerRadius) { + sp<SurfaceControl> parent; + sp<SurfaceControl> child; + const uint32_t size = 64; + const uint32_t parentSize = size * 3; + const Rect parentCrop(size, size, size, size); + const uint32_t testLength = 4; + const float cornerRadius = 20.0f; + ASSERT_NO_FATAL_FAILURE(parent = createLayer("parent", parentSize, parentSize)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(parent, Color::RED, parentSize, parentSize)); + ASSERT_NO_FATAL_FAILURE(child = createLayer("child", size, size)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::GREEN, size, size)); + + Transaction() + .setCornerRadius(parent, cornerRadius) + .setCrop(parent, parentCrop) + .setClientDrawnCornerRadius(parent, cornerRadius) + .reparent(child, parent) + .setPosition(child, size, size) + .apply(true); + + { + const uint32_t top = size; + const uint32_t left = size; + const uint32_t bottom = size * 2; + const uint32_t right = size * 2; + auto shot = getScreenCapture(); + // Corners are RED because parent's client drawn corner radius is actually 0 + // and the child is fully within the parent's crop + // TL + shot->expectColor(Rect(left, top, testLength, testLength), Color::RED); + // TR + shot->expectColor(Rect(right - testLength, top, testLength, testLength), Color::RED); + // BL + shot->expectColor(Rect(left, bottom - testLength, testLength, testLength), Color::RED); + // BR + shot->expectColor(Rect(right - testLength, bottom - testLength, testLength, testLength), + Color::RED); + // Solid center + shot->expectColor(Rect(parentSize / 2 - testLength, parentSize / 2 - testLength, testLength, + testLength), + Color::GREEN); + } +} + TEST_P(LayerTypeAndRenderTypeTransactionTest, SetBackgroundBlurRadiusSimple) { if (!deviceSupportsBlurs()) GTEST_SKIP(); if (!deviceUsesSkiaRenderEngine()) GTEST_SKIP(); diff --git a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h index a894c418f6..ee5d9193ea 100644 --- a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h +++ b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h @@ -485,6 +485,29 @@ public: mLifecycleManager.applyTransactions(transactions); } + void setClientDrawnCornerRadius(uint32_t id, float clientDrawnCornerRadius) { + std::vector<QueuedTransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = + layer_state_t::eClientDrawnCornerRadiusChanged; + transactions.back().states.front().layerId = id; + transactions.back().states.front().state.clientDrawnCornerRadius = clientDrawnCornerRadius; + mLifecycleManager.applyTransactions(transactions); + } + + void setClientDrawnShadowRadius(uint32_t id, float clientDrawnShadowRadius) { + std::vector<QueuedTransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = layer_state_t::eClientDrawnShadowsChanged; + transactions.back().states.front().layerId = id; + transactions.back().states.front().state.clientDrawnShadowRadius = clientDrawnShadowRadius; + mLifecycleManager.applyTransactions(transactions); + } + void setShadowRadius(uint32_t id, float shadowRadius) { std::vector<QueuedTransactionState> transactions; transactions.emplace_back(); diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp index 1177d1601e..2deb177496 100644 --- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp @@ -1425,6 +1425,59 @@ TEST_F(LayerSnapshotTest, setBufferCrop) { EXPECT_EQ(getSnapshot(1)->geomContentCrop, Rect(0, 0, 100, 100)); } +TEST_F(LayerSnapshotTest, setCornerRadius) { + static constexpr float RADIUS = 123.f; + setRoundedCorners(1, RADIUS); + setCrop(1, Rect{1000, 1000}); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot({.id = 1})->roundedCorner.radius.x, RADIUS); +} + +TEST_F(LayerSnapshotTest, ignoreCornerRadius) { + static constexpr float RADIUS = 123.f; + setClientDrawnCornerRadius(1, RADIUS); + setRoundedCorners(1, RADIUS); + setCrop(1, Rect{1000, 1000}); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_TRUE(getSnapshot({.id = 1})->roundedCorner.hasClientDrawnRadius()); + EXPECT_EQ(getSnapshot({.id = 1})->roundedCorner.radius.x, 0.f); +} + +TEST_F(LayerSnapshotTest, childInheritsParentIntendedCornerRadius) { + static constexpr float RADIUS = 123.f; + + setClientDrawnCornerRadius(1, RADIUS); + setRoundedCorners(1, RADIUS); + setCrop(1, Rect(1, 1, 999, 999)); + + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_TRUE(getSnapshot({.id = 1})->roundedCorner.hasClientDrawnRadius()); + EXPECT_TRUE(getSnapshot({.id = 11})->roundedCorner.hasRoundedCorners()); + EXPECT_EQ(getSnapshot({.id = 11})->roundedCorner.radius.x, RADIUS); +} + +TEST_F(LayerSnapshotTest, childIgnoreCornerRadiusOverridesParent) { + static constexpr float RADIUS = 123.f; + + setRoundedCorners(1, RADIUS); + setCrop(1, Rect(1, 1, 999, 999)); + + setClientDrawnCornerRadius(11, RADIUS); + + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot({.id = 1})->roundedCorner.radius.x, RADIUS); + EXPECT_EQ(getSnapshot({.id = 11})->roundedCorner.radius.x, 0.f); + EXPECT_EQ(getSnapshot({.id = 111})->roundedCorner.radius.x, RADIUS); +} + +TEST_F(LayerSnapshotTest, ignoreShadows) { + static constexpr float SHADOW_RADIUS = 123.f; + setClientDrawnShadowRadius(1, SHADOW_RADIUS); + setShadowRadius(1, SHADOW_RADIUS); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot({.id = 1})->shadowSettings.length, 0.f); +} + TEST_F(LayerSnapshotTest, setShadowRadius) { static constexpr float SHADOW_RADIUS = 123.f; setShadowRadius(1, SHADOW_RADIUS); diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index 3455c13bb7..c2e88688e0 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -184,8 +184,8 @@ public: } void setupComposer(std::unique_ptr<Hwc2::Composer> composer) { - mFlinger->mCompositionEngine->setHwComposer( - std::make_unique<impl::HWComposer>(std::move(composer))); + mFlinger->mHWComposer = std::make_unique<impl::HWComposer>(std::move(composer)); + mFlinger->mCompositionEngine->setHwComposer(mFlinger->mHWComposer.get()); mFlinger->mDisplayModeController.setHwComposer( &mFlinger->mCompositionEngine->getHwComposer()); } @@ -771,7 +771,8 @@ public: mutableCurrentState().displays.clear(); mutableDrawingState().displays.clear(); mFlinger->mScheduler.reset(); - mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>()); + mFlinger->mHWComposer = std::unique_ptr<HWComposer>(); + mFlinger->mCompositionEngine->setHwComposer(mFlinger->mHWComposer.get()); mFlinger->mRenderEngine = std::unique_ptr<renderengine::RenderEngine>(); mFlinger->mCompositionEngine->setRenderEngine(mFlinger->mRenderEngine.get()); mFlinger->mTransactionTracing.reset(); |