diff options
32 files changed, 1023 insertions, 556 deletions
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index 3d4ba6fe48..176c84d1e6 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -722,9 +722,7 @@ binder::Status InstalldNativeService::createAppDataLocked( } } - // TODO(b/220095381): Due to boot time regression, we have omitted call to - // createSdkSandboxDataDirectory from here temporarily (unless it's for testing) - if (uuid_ != nullptr && strcmp(uuid_, "TEST") == 0) { + if (flags & FLAG_STORAGE_SDK) { auto status = createSdkSandboxDataDirectory(uuid, packageName, userId, appId, previousAppId, seInfo, flags); if (!status.isOk()) { diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl index afedcc6adc..428e2b3908 100644 --- a/cmds/installd/binder/android/os/IInstalld.aidl +++ b/cmds/installd/binder/android/os/IInstalld.aidl @@ -131,6 +131,7 @@ interface IInstalld { const int FLAG_STORAGE_DE = 0x1; const int FLAG_STORAGE_CE = 0x2; const int FLAG_STORAGE_EXTERNAL = 0x4; + const int FLAG_STORAGE_SDK = 0x8; const int FLAG_CLEAR_CACHE_ONLY = 0x10; const int FLAG_CLEAR_CODE_CACHE_ONLY = 0x20; diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp index 21ab5b87b1..31f5dcef4a 100644 --- a/cmds/installd/tests/installd_service_test.cpp +++ b/cmds/installd/tests/installd_service_test.cpp @@ -79,6 +79,7 @@ static constexpr const char* kTestPath = "/data/local/tmp/user/0"; static constexpr const uid_t kSystemUid = 1000; static constexpr const int32_t kTestUserId = 0; static constexpr const uid_t kTestAppId = 19999; +static constexpr const int FLAG_STORAGE_SDK = InstalldNativeService::FLAG_STORAGE_SDK; const gid_t kTestAppUid = multiuser_get_uid(kTestUserId, kTestAppId); const uid_t kTestSdkSandboxUid = multiuser_get_sdk_sandbox_uid(kTestUserId, kTestAppId); @@ -971,7 +972,7 @@ public: args.userId = kTestUserId; args.appId = kTestAppId; args.seInfo = "default"; - args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE; + args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE | FLAG_STORAGE_SDK; return args; } @@ -1006,11 +1007,11 @@ private: } }; -TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSupplementalAppData) { +TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkAppLevelData) { android::os::CreateAppDataResult result; android::os::CreateAppDataArgs args = createAppDataArgs(); args.packageName = "com.foo"; - args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE; + args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE | FLAG_STORAGE_SDK; // Create the app user data. ASSERT_BINDER_SUCCESS(service->createAppData(args, &result)); @@ -1030,11 +1031,24 @@ TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSupplementalAppData) { S_IFDIR | S_ISGID | 0771); } -TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSupplementalAppData_WithoutDeFlag) { +TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkAppLeveleData_WithoutSdkFlag) { + android::os::CreateAppDataResult result; + android::os::CreateAppDataArgs args = createAppDataArgs(); + args.packageName = "com.foo"; + args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE; + + // Create the app user data. + ASSERT_BINDER_SUCCESS(service->createAppData(args, &result)); + + ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo")); + ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo")); +} + +TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkAppLeveleData_WithoutDeFlag) { android::os::CreateAppDataResult result; android::os::CreateAppDataArgs args = createAppDataArgs(); args.packageName = "com.foo"; - args.flags = FLAG_STORAGE_CE; + args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_SDK; // Create the app user data. ASSERT_BINDER_SUCCESS(service->createAppData(args, &result)); @@ -1046,11 +1060,11 @@ TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSupplementalAppData_WithoutDeFla ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo")); } -TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSupplementalAppData_WithoutCeFlag) { +TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkAppLeveleData_WithoutCeFlag) { android::os::CreateAppDataResult result; android::os::CreateAppDataArgs args = createAppDataArgs(); args.packageName = "com.foo"; - args.flags = FLAG_STORAGE_DE; + args.flags = FLAG_STORAGE_DE | FLAG_STORAGE_SDK; // Create the app user data. ASSERT_BINDER_SUCCESS(service->createAppData(args, &result)); diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp index 80bd6389a0..2312a8cf0d 100644 --- a/libs/gui/WindowInfo.cpp +++ b/libs/gui/WindowInfo.cpp @@ -51,11 +51,11 @@ bool WindowInfo::supportsSplitTouch() const { } bool WindowInfo::isSpy() const { - return inputFeatures.test(Feature::SPY); + return inputConfig.test(InputConfig::SPY); } bool WindowInfo::interceptsStylus() const { - return inputFeatures.test(Feature::INTERCEPTS_STYLUS); + return inputConfig.test(InputConfig::INTERCEPTS_STYLUS); } bool WindowInfo::overlaps(const WindowInfo* other) const { @@ -73,8 +73,7 @@ bool WindowInfo::operator==(const WindowInfo& info) const { info.touchableRegion.hasSameRects(touchableRegion) && info.touchOcclusionMode == touchOcclusionMode && info.ownerPid == ownerPid && info.ownerUid == ownerUid && info.packageName == packageName && - info.inputFeatures == inputFeatures && info.inputConfig == inputConfig && - info.displayId == displayId && + info.inputConfig == inputConfig && info.displayId == displayId && info.replaceTouchableRegionWithCrop == replaceTouchableRegionWithCrop && info.applicationInfo == applicationInfo && info.layoutParamsType == layoutParamsType && info.layoutParamsFlags == layoutParamsFlags; @@ -92,7 +91,6 @@ status_t WindowInfo::writeToParcel(android::Parcel* parcel) const { parcel->writeInt32(1); // Ensure that the size of the flags that we use is 32 bits for writing into the parcel. - static_assert(sizeof(inputFeatures) == 4u); static_assert(sizeof(inputConfig) == 4u); // clang-format off @@ -120,7 +118,6 @@ status_t WindowInfo::writeToParcel(android::Parcel* parcel) const { parcel->writeInt32(ownerPid) ?: parcel->writeInt32(ownerUid) ?: parcel->writeUtf8AsUtf16(packageName) ?: - parcel->writeInt32(inputFeatures.get()) ?: parcel->writeInt32(inputConfig.get()) ?: parcel->writeInt32(displayId) ?: applicationInfo.writeToParcel(parcel) ?: @@ -148,12 +145,14 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) { return status; } - layoutParamsFlags = Flags<Flag>(parcel->readInt32()); - layoutParamsType = static_cast<Type>(parcel->readInt32()); float dsdx, dtdx, tx, dtdy, dsdy, ty; - int32_t touchOcclusionModeInt; + int32_t lpFlags, lpType, touchOcclusionModeInt, inputConfigInt; + sp<IBinder> touchableRegionCropHandleSp; + // clang-format off - status = parcel->readInt32(&frameLeft) ?: + status = parcel->readInt32(&lpFlags) ?: + parcel->readInt32(&lpType) ?: + parcel->readInt32(&frameLeft) ?: parcel->readInt32(&frameTop) ?: parcel->readInt32(&frameRight) ?: parcel->readInt32(&frameBottom) ?: @@ -169,33 +168,28 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) { parcel->readInt32(&touchOcclusionModeInt) ?: parcel->readInt32(&ownerPid) ?: parcel->readInt32(&ownerUid) ?: - parcel->readUtf8FromUtf16(&packageName); - // clang-format on - - if (status != OK) { - return status; - } - - touchOcclusionMode = static_cast<TouchOcclusionMode>(touchOcclusionModeInt); - - inputFeatures = Flags<Feature>(parcel->readInt32()); - inputConfig = Flags<InputConfig>(parcel->readInt32()); - // clang-format off - status = parcel->readInt32(&displayId) ?: + parcel->readUtf8FromUtf16(&packageName) ?: + parcel->readInt32(&inputConfigInt) ?: + parcel->readInt32(&displayId) ?: applicationInfo.readFromParcel(parcel) ?: parcel->read(touchableRegion) ?: - parcel->readBool(&replaceTouchableRegionWithCrop); + parcel->readBool(&replaceTouchableRegionWithCrop) ?: + parcel->readNullableStrongBinder(&touchableRegionCropHandleSp) ?: + parcel->readNullableStrongBinder(&windowToken); // clang-format on if (status != OK) { return status; } - touchableRegionCropHandle = parcel->readStrongBinder(); + layoutParamsFlags = 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 = Flags<InputConfig>(inputConfigInt); + touchableRegionCropHandle = touchableRegionCropHandleSp; - status = parcel->readNullableStrongBinder(&windowToken); - return status; + return OK; } // --- WindowInfoHandle --- diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h index b9bffaa6a0..ef0b98b5cb 100644 --- a/libs/gui/include/gui/WindowInfo.h +++ b/libs/gui/include/gui/WindowInfo.h @@ -17,7 +17,7 @@ #pragma once #include <android/gui/TouchOcclusionMode.h> -#include <android/os/IInputConstants.h> +#include <android/os/InputConfig.h> #include <binder/Parcel.h> #include <binder/Parcelable.h> #include <ftl/Flags.h> @@ -132,49 +132,45 @@ struct WindowInfo : public Parcelable { ftl_last = FIRST_SYSTEM_WINDOW + 15 }; - // This is a conversion of os::IInputConstants::InputFeature to an enum backed by an unsigned + // Flags used to determine configuration of this input window. + // This is a conversion of os::InputConfig to an enum backed by an unsigned // type. This indicates that they are flags, so it can be used with ftl/enum.h. - enum class Feature : uint32_t { + enum class InputConfig : uint32_t { // clang-format off + DEFAULT = + static_cast<uint32_t>(os::InputConfig::DEFAULT), NO_INPUT_CHANNEL = - static_cast<uint32_t>(os::IInputConstants::InputFeature::NO_INPUT_CHANNEL), + static_cast<uint32_t>(os::InputConfig::NO_INPUT_CHANNEL), + NOT_VISIBLE = + static_cast<uint32_t>(os::InputConfig::NOT_VISIBLE), + NOT_FOCUSABLE = + 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 = + static_cast<uint32_t>(os::InputConfig::IS_WALLPAPER), + PAUSE_DISPATCHING = + static_cast<uint32_t>(os::InputConfig::PAUSE_DISPATCHING), + TRUSTED_OVERLAY = + static_cast<uint32_t>(os::InputConfig::TRUSTED_OVERLAY), + WATCH_OUTSIDE_TOUCH = + static_cast<uint32_t>(os::InputConfig::WATCH_OUTSIDE_TOUCH), + SLIPPERY = + static_cast<uint32_t>(os::InputConfig::SLIPPERY), DISABLE_USER_ACTIVITY = - static_cast<uint32_t>(os::IInputConstants::InputFeature::DISABLE_USER_ACTIVITY), + static_cast<uint32_t>(os::InputConfig::DISABLE_USER_ACTIVITY), DROP_INPUT = - static_cast<uint32_t>(os::IInputConstants::InputFeature::DROP_INPUT), + static_cast<uint32_t>(os::InputConfig::DROP_INPUT), DROP_INPUT_IF_OBSCURED = - static_cast<uint32_t>(os::IInputConstants::InputFeature::DROP_INPUT_IF_OBSCURED), + static_cast<uint32_t>(os::InputConfig::DROP_INPUT_IF_OBSCURED), SPY = - static_cast<uint32_t>(os::IInputConstants::InputFeature::SPY), + static_cast<uint32_t>(os::InputConfig::SPY), INTERCEPTS_STYLUS = - static_cast<uint32_t>(os::IInputConstants::InputFeature::INTERCEPTS_STYLUS), - // clang-format on - }; - - // Flags used to determine configuration of this input window. - // Input windows can be configured with two sets of flags: InputFeature (WindowInfo::Feature - // defined above), and InputConfig. When adding a new configuration for an input window: - // - If you are adding a new flag that's visible and accessible to apps, it should be added - // as an InputFeature. - // - If you are adding an internal behaviour that is used within the system or shell and is - // not exposed to apps, it should be added as an InputConfig. - enum class InputConfig : uint32_t { - // clang-format off - NONE = 0, - NOT_VISIBLE = 1 << 0, - NOT_FOCUSABLE = 1 << 1, - NOT_TOUCHABLE = 1 << 2, - PREVENT_SPLITTING = 1 << 3, - DUPLICATE_TOUCH_TO_WALLPAPER = 1 << 4, - IS_WALLPAPER = 1 << 5, - PAUSE_DISPATCHING = 1 << 6, - // This flag is set when the window is of a trusted type that is allowed to silently - // overlay other windows for the purpose of implementing the secure views feature. - // Trusted overlays, such as IME windows, can partly obscure other windows without causing - // motion events to be delivered to them with AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. - TRUSTED_OVERLAY = 1 << 7, - WATCH_OUTSIDE_TOUCH = 1 << 8, - SLIPPERY = 1 << 9, + static_cast<uint32_t>(os::InputConfig::INTERCEPTS_STYLUS), // clang-format on }; @@ -228,7 +224,6 @@ struct WindowInfo : public Parcelable { int32_t ownerPid = -1; int32_t ownerUid = -1; std::string packageName; - Flags<Feature> inputFeatures; Flags<InputConfig> inputConfig; int32_t displayId = ADISPLAY_ID_NONE; InputApplicationInfo applicationInfo; diff --git a/libs/gui/tests/WindowInfo_test.cpp b/libs/gui/tests/WindowInfo_test.cpp index ff9bae2800..c51b244c50 100644 --- a/libs/gui/tests/WindowInfo_test.cpp +++ b/libs/gui/tests/WindowInfo_test.cpp @@ -64,7 +64,6 @@ TEST(WindowInfo, Parcelling) { i.ownerPid = 19; i.ownerUid = 24; i.packageName = "com.example.package"; - i.inputFeatures = WindowInfo::Feature::DISABLE_USER_ACTIVITY; i.inputConfig = WindowInfo::InputConfig::NOT_FOCUSABLE; i.displayId = 34; i.replaceTouchableRegionWithCrop = true; @@ -97,7 +96,6 @@ TEST(WindowInfo, Parcelling) { ASSERT_EQ(i.ownerPid, i2.ownerPid); ASSERT_EQ(i.ownerUid, i2.ownerUid); ASSERT_EQ(i.packageName, i2.packageName); - ASSERT_EQ(i.inputFeatures, i2.inputFeatures); ASSERT_EQ(i.inputConfig, i2.inputConfig); ASSERT_EQ(i.displayId, i2.displayId); ASSERT_EQ(i.replaceTouchableRegionWithCrop, i2.replaceTouchableRegionWithCrop); diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 930d8194d5..606fe2a59d 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -30,6 +30,7 @@ filegroup { "android/os/IInputConstants.aidl", "android/os/InputEventInjectionResult.aidl", "android/os/InputEventInjectionSync.aidl", + "android/os/InputConfig.aidl", ], } @@ -79,11 +80,8 @@ cc_library { android: { srcs: [ "InputTransport.cpp", - "android/os/BlockUntrustedTouchesMode.aidl", - "android/os/IInputConstants.aidl", "android/os/IInputFlinger.aidl", - "android/os/InputEventInjectionResult.aidl", - "android/os/InputEventInjectionSync.aidl", + ":inputconstants_aidl", ], export_shared_lib_headers: ["libbinder"], @@ -119,6 +117,7 @@ cc_library { "InputTransport.cpp", "android/os/IInputConstants.aidl", "android/os/IInputFlinger.aidl", + "android/os/InputConfig.aidl", ], static_libs: [ "libhostgraphics", diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl index 265cbf0c0b..5ce10a4a50 100644 --- a/libs/input/android/os/IInputConstants.aidl +++ b/libs/input/android/os/IInputConstants.aidl @@ -47,55 +47,6 @@ interface IInputConstants */ const int INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT = 0x800; - @Backing(type="int") - enum InputFeature { - /** - * Does not construct an input channel for this window. The channel will therefore - * be incapable of receiving input. - */ - NO_INPUT_CHANNEL = 0x00000002, - - /** - * When this window has focus, does not call user activity for all input events so - * the application will have to do it itself. Should only be used by - * the keyguard and phone app. - * - * Should only be used by the keyguard and phone app. - */ - DISABLE_USER_ACTIVITY = 0x00000004, - - /** - * Internal flag used to indicate that input should be dropped on this window. - */ - DROP_INPUT = 0x00000008, - - /** - * Internal flag used to indicate that input should be dropped on this window if this window - * is obscured. - */ - DROP_INPUT_IF_OBSCURED = 0x00000010, - - /** - * An input spy window. This window will receive all pointer events within its touchable - * area, but will will not stop events from being sent to other windows below it in z-order. - * An input event will be dispatched to all spy windows above the top non-spy window at the - * event's coordinates. - */ - SPY = 0x00000020, - - /** - * When used with the window flag {@link #FLAG_NOT_TOUCHABLE}, this window will continue - * to receive events from a stylus device within its touchable region. All other pointer - * events, such as from a mouse or touchscreen, will be dispatched to the windows behind it. - * - * This input feature has no effect when the window flag {@link #FLAG_NOT_TOUCHABLE} is - * not set. - * - * The window must be a trusted overlay to use this input feature. - */ - INTERCEPTS_STYLUS = 0x00000040, - } - /* The default pointer acceleration value. */ const int DEFAULT_POINTER_ACCELERATION = 3; } diff --git a/libs/input/android/os/InputConfig.aidl b/libs/input/android/os/InputConfig.aidl new file mode 100644 index 0000000000..6d1b3967f7 --- /dev/null +++ b/libs/input/android/os/InputConfig.aidl @@ -0,0 +1,147 @@ +/** + * Copyright (c) 2022, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + + +/** + * Input configurations flags used to determine the behavior of input windows. + * @hide + */ +@Backing(type="int") +enum InputConfig { + + /** + * The default InputConfig value with no flags set. + */ + DEFAULT = 0, + + /** + * Does not construct an input channel for this window. The channel will therefore + * be incapable of receiving input. + */ + NO_INPUT_CHANNEL = 1 << 0, + + /** + * Indicates that this input window is not visible, and thus will not be considered as + * an input target and will not obscure other windows. + */ + NOT_VISIBLE = 1 << 1, + + /** + * Indicates that this input window cannot be a focus target, and this will not + * receive any input events that can only be directed for the focused window, such + * as key events. + */ + NOT_FOCUSABLE = 1 << 2, + + /** + * Indicates that this input window cannot receive any events directed at a + * specific location on the screen, such as touchscreen, mouse, and stylus events. + * The window will not be considered as a touch target, but can still obscure other + * windows. + */ + 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, + + /** + * Indicates that this window shows the wallpaper behind it, so all touch events + * that it receives should also be sent to the wallpaper. + */ + DUPLICATE_TOUCH_TO_WALLPAPER = 1 << 5, + + /** Indicates that this the wallpaper's input window. */ + IS_WALLPAPER = 1 << 6, + + /** + * Indicates that input events should not be dispatched to this window. When set, + * input events directed towards this window will simply be dropped, and will not + * be dispatched to windows behind it. + */ + PAUSE_DISPATCHING = 1 << 7, + + /** + * This flag is set when the window is of a trusted type that is allowed to silently + * overlay other windows for the purpose of implementing the secure views feature. + * Trusted overlays, such as IME windows, can partly obscure other windows without causing + * motion events to be delivered to them with AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. + */ + TRUSTED_OVERLAY = 1 << 8, + + /** + * Indicates that this window wants to listen for when there is a touch DOWN event + * that occurs outside its touchable bounds. When such an event occurs, this window + * will receive a MotionEvent with ACTION_OUTSIDE. + */ + WATCH_OUTSIDE_TOUCH = 1 << 9, + + /** + * When set, this flag allows touches to leave the current window whenever the finger + * moves above another window. When this happens, the window that touch has just left + * (the current window) will receive ACTION_CANCEL, and the window that touch has entered + * will receive ACTION_DOWN, and the remainder of the touch gesture will only go to the + * new window. Without this flag, the entire gesture is sent to the current window, even + * if the touch leaves the window's bounds. + */ + SLIPPERY = 1 << 10, + + /** + * When this window has focus, does not call user activity for all input events so + * the application will have to do it itself. + */ + DISABLE_USER_ACTIVITY = 1 << 11, + + /** + * Internal flag used to indicate that input should be dropped on this window. + */ + DROP_INPUT = 1 << 12, + + /** + * Internal flag used to indicate that input should be dropped on this window if this window + * is obscured. + */ + DROP_INPUT_IF_OBSCURED = 1 << 13, + + /** + * An input spy window. This window will receive all pointer events within its touchable + * area, but will not stop events from being sent to other windows below it in z-order. + * An input event will be dispatched to all spy windows above the top non-spy window at the + * event's coordinates. + */ + SPY = 1 << 14, + + /** + * When used with {@link #NOT_TOUCHABLE}, this window will continue to receive events from + * a stylus device within its touchable region. All other pointer events, such as from a + * mouse or touchscreen, will be dispatched to the windows behind it. + * + * This configuration has no effect when the config {@link #NOT_TOUCHABLE} is not set. + * + * It is not valid to set this configuration if {@link #TRUSTED_OVERLAY} is not set. + */ + INTERCEPTS_STYLUS = 1 << 15, +} diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp index 07c5dd8a82..84e84dddad 100644 --- a/libs/renderengine/Android.bp +++ b/libs/renderengine/Android.bp @@ -122,6 +122,9 @@ cc_library_static { ":librenderengine_threaded_sources", ":librenderengine_skia_sources", ], + header_libs: [ + "libtonemap_headers", + ], include_dirs: [ "external/skia/src/gpu", ], diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp index a426850350..d91af1ea85 100644 --- a/libs/renderengine/tests/Android.bp +++ b/libs/renderengine/tests/Android.bp @@ -42,6 +42,9 @@ cc_test { "libshaders", "libtonemap", ], + header_libs: [ + "libtonemap_headers", + ], shared_libs: [ "libbase", diff --git a/libs/shaders/Android.bp b/libs/shaders/Android.bp index 390b22821e..1cd143e98d 100644 --- a/libs/shaders/Android.bp +++ b/libs/shaders/Android.bp @@ -38,6 +38,10 @@ cc_library_static { "libui-types", ], + header_libs: [ + "libtonemap_headers", + ], + srcs: [ "shaders.cpp", ], diff --git a/libs/tonemap/Android.bp b/libs/tonemap/Android.bp index 5360fe2b07..99d1b22717 100644 --- a/libs/tonemap/Android.bp +++ b/libs/tonemap/Android.bp @@ -25,7 +25,6 @@ cc_library_static { name: "libtonemap", vendor_available: true, - export_include_dirs: ["include"], local_include_dirs: ["include"], shared_libs: [ @@ -41,3 +40,9 @@ cc_library_static { "tonemap.cpp", ], } + +cc_library_headers { + name: "libtonemap_headers", + vendor_available: true, + export_include_dirs: ["include"], +} diff --git a/libs/tonemap/tests/Android.bp b/libs/tonemap/tests/Android.bp index f46f3fa27d..d519482fe3 100644 --- a/libs/tonemap/tests/Android.bp +++ b/libs/tonemap/tests/Android.bp @@ -27,6 +27,9 @@ cc_test { srcs: [ "tonemap_test.cpp", ], + header_libs: [ + "libtonemap_headers", + ], shared_libs: [ "android.hardware.graphics.common-V3-ndk", ], diff --git a/opengl/OWNERS b/opengl/OWNERS index a9bd4bb2e2..a47fb9a573 100644 --- a/opengl/OWNERS +++ b/opengl/OWNERS @@ -1,6 +1,12 @@ +abdolrashidi@google.com +cclao@google.com chrisforbes@google.com cnorthrop@google.com ianelliott@google.com jessehall@google.com +lfy@google.com lpy@google.com timvp@google.com +romanl@google.com +vantablack@google.com +yuxinhu@google.com diff --git a/services/gpuservice/gpuwork/GpuWork.cpp b/services/gpuservice/gpuwork/GpuWork.cpp index 67ce9f281e..974ae38b13 100644 --- a/services/gpuservice/gpuwork/GpuWork.cpp +++ b/services/gpuservice/gpuwork/GpuWork.cpp @@ -39,17 +39,30 @@ #include <map> #include <mutex> #include <unordered_map> +#include <unordered_set> #include <vector> #include "gpuwork/gpu_work.h" -#define MS_IN_NS (1000000) +#define ONE_MS_IN_NS (10000000) namespace android { namespace gpuwork { namespace { +bool lessThanGpuIdUid(const android::gpuwork::GpuIdUid& l, const android::gpuwork::GpuIdUid& r) { + return std::tie(l.gpu_id, l.uid) < std::tie(r.gpu_id, r.uid); +} + +size_t hashGpuIdUid(const android::gpuwork::GpuIdUid& gpuIdUid) { + return static_cast<size_t>((gpuIdUid.gpu_id << 5U) + gpuIdUid.uid); +} + +bool equalGpuIdUid(const android::gpuwork::GpuIdUid& l, const android::gpuwork::GpuIdUid& r) { + return std::tie(l.gpu_id, l.uid) == std::tie(r.gpu_id, r.uid); +} + // Gets a BPF map from |mapPath|. template <class Key, class Value> bool getBpfMap(const char* mapPath, bpf::BpfMap<Key, Value>* out) { @@ -76,24 +89,6 @@ inline int32_t bitcast_int32<uint32_t>(uint32_t source) { return result; } -template <> -inline int32_t cast_int32<uint64_t>(uint64_t source) { - if (source > std::numeric_limits<int32_t>::max()) { - return std::numeric_limits<int32_t>::max(); - } - return static_cast<int32_t>(source); -} - -template <> -inline int32_t cast_int32<long long>(long long source) { - if (source > std::numeric_limits<int32_t>::max()) { - return std::numeric_limits<int32_t>::max(); - } else if (source < std::numeric_limits<int32_t>::min()) { - return std::numeric_limits<int32_t>::min(); - } - return static_cast<int32_t>(source); -} - } // namespace using base::StringAppendF; @@ -115,7 +110,7 @@ GpuWork::~GpuWork() { { std::scoped_lock<std::mutex> lock(mMutex); if (mStatsdRegistered) { - AStatsManager_clearPullAtomCallback(android::util::GPU_FREQ_TIME_IN_STATE_PER_UID); + AStatsManager_clearPullAtomCallback(android::util::GPU_WORK_PER_UID); } } @@ -144,7 +139,7 @@ void GpuWork::initialize() { mPreviousMapClearTimePoint = std::chrono::steady_clock::now(); } - // Attach the tracepoint ONLY if we got the map above. + // Attach the tracepoint. if (!attachTracepoint("/sys/fs/bpf/prog_gpu_work_tracepoint_power_gpu_work_period", "power", "gpu_work_period")) { return; @@ -157,8 +152,8 @@ void GpuWork::initialize() { { std::lock_guard<std::mutex> lock(mMutex); - AStatsManager_setPullAtomCallback(int32_t{android::util::GPU_FREQ_TIME_IN_STATE_PER_UID}, - nullptr, GpuWork::pullAtomCallback, this); + AStatsManager_setPullAtomCallback(int32_t{android::util::GPU_WORK_PER_UID}, nullptr, + GpuWork::pullAtomCallback, this); mStatsdRegistered = true; } @@ -169,18 +164,18 @@ void GpuWork::initialize() { void GpuWork::dump(const Vector<String16>& /* args */, std::string* result) { if (!mInitialized.load()) { - result->append("GPU time in state information is not available.\n"); + result->append("GPU work information is not available.\n"); return; } - // Ordered map ensures output data is sorted by UID. - std::map<Uid, UidTrackingInfo> dumpMap; + // Ordered map ensures output data is sorted. + std::map<GpuIdUid, UidTrackingInfo, decltype(lessThanGpuIdUid)*> dumpMap(&lessThanGpuIdUid); { std::lock_guard<std::mutex> lock(mMutex); if (!mGpuWorkMap.isValid()) { - result->append("GPU time in state map is not available.\n"); + result->append("GPU work map is not available.\n"); return; } @@ -189,56 +184,42 @@ void GpuWork::dump(const Vector<String16>& /* args */, std::string* result) { // threads. The buckets are all preallocated. Our eBPF program only updates // entries (in-place) or adds entries. |GpuWork| only iterates or clears the // map while holding |mMutex|. Given this, we should be able to iterate over - // all elements reliably. In the worst case, we might see elements more than - // once. + // all elements reliably. Nevertheless, we copy into a map to avoid + // duplicates. // Note that userspace reads of BPF maps make a copy of the value, and // thus the returned value is not being concurrently accessed by the BPF // program (no atomic reads needed below). - mGpuWorkMap.iterateWithValue([&dumpMap](const Uid& key, const UidTrackingInfo& value, - const android::bpf::BpfMap<Uid, UidTrackingInfo>&) - -> base::Result<void> { - dumpMap[key] = value; - return {}; - }); - } - - // Find the largest frequency where some UID has spent time in that frequency. - size_t largestFrequencyWithTime = 0; - for (const auto& uidToUidInfo : dumpMap) { - for (size_t i = largestFrequencyWithTime + 1; i < kNumTrackedFrequencies; ++i) { - if (uidToUidInfo.second.frequency_times_ns[i] > 0) { - largestFrequencyWithTime = i; - } - } + mGpuWorkMap.iterateWithValue( + [&dumpMap](const GpuIdUid& key, const UidTrackingInfo& value, + const android::bpf::BpfMap<GpuIdUid, UidTrackingInfo>&) + -> base::Result<void> { + dumpMap[key] = value; + return {}; + }); } - // Dump time in state information. + // Dump work information. // E.g. - // uid/freq: 0MHz 50MHz 100MHz ... - // 1000: 0 0 0 0 ... - // 1003: 0 0 3456 0 ... - // [errors:3]1006: 0 0 3456 0 ... + // GPU work information. + // gpu_id uid total_active_duration_ns total_inactive_duration_ns + // 0 1000 0 0 + // 0 1003 1234 123 + // [errors:3]0 1006 4567 456 // Header. - result->append("GPU time in frequency state in ms.\n"); - result->append("uid/freq: 0MHz"); - for (size_t i = 1; i <= largestFrequencyWithTime; ++i) { - StringAppendF(result, " %zuMHz", i * 50); - } - result->append("\n"); + result->append("GPU work information.\ngpu_id uid total_active_duration_ns " + "total_inactive_duration_ns\n"); - for (const auto& uidToUidInfo : dumpMap) { - if (uidToUidInfo.second.error_count) { - StringAppendF(result, "[errors:%" PRIu32 "]", uidToUidInfo.second.error_count); + for (const auto& idToUidInfo : dumpMap) { + if (idToUidInfo.second.error_count) { + StringAppendF(result, "[errors:%" PRIu32 "]", idToUidInfo.second.error_count); } - StringAppendF(result, "%" PRIu32 ":", uidToUidInfo.first); - for (size_t i = 0; i <= largestFrequencyWithTime; ++i) { - StringAppendF(result, " %" PRIu64, - uidToUidInfo.second.frequency_times_ns[i] / MS_IN_NS); - } - result->append("\n"); + StringAppendF(result, "%" PRIu32 " %" PRIu32 " %" PRIu64 " %" PRIu64 "\n", + idToUidInfo.first.gpu_id, idToUidInfo.first.uid, + idToUidInfo.second.total_active_duration_ns, + idToUidInfo.second.total_inactive_duration_ns); } } @@ -275,14 +256,14 @@ AStatsManager_PullAtomCallbackReturn GpuWork::pullAtomCallback(int32_t atomTag, ATRACE_CALL(); GpuWork* gpuWork = reinterpret_cast<GpuWork*>(cookie); - if (atomTag == android::util::GPU_FREQ_TIME_IN_STATE_PER_UID) { - return gpuWork->pullFrequencyAtoms(data); + if (atomTag == android::util::GPU_WORK_PER_UID) { + return gpuWork->pullWorkAtoms(data); } return AStatsManager_PULL_SKIP; } -AStatsManager_PullAtomCallbackReturn GpuWork::pullFrequencyAtoms(AStatsEventList* data) { +AStatsManager_PullAtomCallbackReturn GpuWork::pullWorkAtoms(AStatsEventList* data) { ATRACE_CALL(); if (!data || !mInitialized.load()) { @@ -295,96 +276,153 @@ AStatsManager_PullAtomCallbackReturn GpuWork::pullFrequencyAtoms(AStatsEventList return AStatsManager_PULL_SKIP; } - std::unordered_map<Uid, UidTrackingInfo> uidInfos; + std::unordered_map<GpuIdUid, UidTrackingInfo, decltype(hashGpuIdUid)*, decltype(equalGpuIdUid)*> + workMap(32, &hashGpuIdUid, &equalGpuIdUid); // Iteration of BPF hash maps can be unreliable (no data races, but elements // may be repeated), as the map is typically being modified by other // threads. The buckets are all preallocated. Our eBPF program only updates // entries (in-place) or adds entries. |GpuWork| only iterates or clears the // map while holding |mMutex|. Given this, we should be able to iterate over - // all elements reliably. In the worst case, we might see elements more than - // once. + // all elements reliably. Nevertheless, we copy into a map to avoid + // duplicates. // Note that userspace reads of BPF maps make a copy of the value, and thus // the returned value is not being concurrently accessed by the BPF program // (no atomic reads needed below). - mGpuWorkMap.iterateWithValue( - [&uidInfos](const Uid& key, const UidTrackingInfo& value, - const android::bpf::BpfMap<Uid, UidTrackingInfo>&) -> base::Result<void> { - uidInfos[key] = value; - return {}; - }); - - ALOGI("pullFrequencyAtoms: uidInfos.size() == %zu", uidInfos.size()); + mGpuWorkMap.iterateWithValue([&workMap](const GpuIdUid& key, const UidTrackingInfo& value, + const android::bpf::BpfMap<GpuIdUid, UidTrackingInfo>&) + -> base::Result<void> { + workMap[key] = value; + return {}; + }); // Get a list of just the UIDs; the order does not matter. std::vector<Uid> uids; - for (const auto& pair : uidInfos) { - uids.push_back(pair.first); + // Get a list of the GPU IDs, in order. + std::set<uint32_t> gpuIds; + { + // To avoid adding duplicate UIDs. + std::unordered_set<Uid> addedUids; + + for (const auto& workInfo : workMap) { + if (addedUids.insert(workInfo.first.uid).second) { + // Insertion was successful. + uids.push_back(workInfo.first.uid); + } + gpuIds.insert(workInfo.first.gpu_id); + } } + ALOGI("pullWorkAtoms: uids.size() == %zu", uids.size()); + ALOGI("pullWorkAtoms: gpuIds.size() == %zu", gpuIds.size()); + + if (gpuIds.size() > kNumGpusHardLimit) { + // If we observe a very high number of GPUs then something has probably + // gone wrong, so don't log any atoms. + return AStatsManager_PULL_SKIP; + } + + size_t numSampledUids = kNumSampledUids; + + if (gpuIds.size() > kNumGpusSoftLimit) { + // If we observe a high number of GPUs then we just sample 1 UID. + numSampledUids = 1; + } + + // Remove all UIDs that do not have at least |kMinGpuTimeNanoseconds| on at + // least one GPU. + { + auto uidIt = uids.begin(); + while (uidIt != uids.end()) { + bool hasEnoughGpuTime = false; + for (uint32_t gpuId : gpuIds) { + auto infoIt = workMap.find(GpuIdUid{gpuId, *uidIt}); + if (infoIt == workMap.end()) { + continue; + } + if (infoIt->second.total_active_duration_ns + + infoIt->second.total_inactive_duration_ns >= + kMinGpuTimeNanoseconds) { + hasEnoughGpuTime = true; + break; + } + } + if (hasEnoughGpuTime) { + ++uidIt; + } else { + uidIt = uids.erase(uidIt); + } + } + } + + ALOGI("pullWorkAtoms: after removing uids with very low GPU time: uids.size() == %zu", + uids.size()); + std::random_device device; std::default_random_engine random_engine(device()); - // If we have more than |kNumSampledUids| UIDs, choose |kNumSampledUids| + // If we have more than |numSampledUids| UIDs, choose |numSampledUids| // random UIDs. We swap them to the front of the list. Given the list // indices 0..i..n-1, we have the following inclusive-inclusive ranges: // - [0, i-1] == the randomly chosen elements. // - [i, n-1] == the remaining unchosen elements. - if (uids.size() > kNumSampledUids) { - for (size_t i = 0; i < kNumSampledUids; ++i) { + if (uids.size() > numSampledUids) { + for (size_t i = 0; i < numSampledUids; ++i) { std::uniform_int_distribution<size_t> uniform_dist(i, uids.size() - 1); size_t random_index = uniform_dist(random_engine); std::swap(uids[i], uids[random_index]); } - // Only keep the front |kNumSampledUids| elements. - uids.resize(kNumSampledUids); + // Only keep the front |numSampledUids| elements. + uids.resize(numSampledUids); } - ALOGI("pullFrequencyAtoms: uids.size() == %zu", uids.size()); + ALOGI("pullWorkAtoms: after random selection: uids.size() == %zu", uids.size()); auto now = std::chrono::steady_clock::now(); - - int32_t duration = cast_int32( + long long duration = std::chrono::duration_cast<std::chrono::seconds>(now - mPreviousMapClearTimePoint) - .count()); - - for (const Uid uid : uids) { - const UidTrackingInfo& info = uidInfos[uid]; - ALOGI("pullFrequencyAtoms: adding stats for UID %" PRIu32, uid); - android::util::addAStatsEvent(data, int32_t{android::util::GPU_FREQ_TIME_IN_STATE_PER_UID}, - // uid - bitcast_int32(uid), - // time_duration_seconds - int32_t{duration}, - // max_freq_mhz - int32_t{1000}, - // freq_0_mhz_time_millis - cast_int32(info.frequency_times_ns[0] / 1000000), - // freq_50_mhz_time_millis - cast_int32(info.frequency_times_ns[1] / 1000000), - // ... etc. ... - cast_int32(info.frequency_times_ns[2] / 1000000), - cast_int32(info.frequency_times_ns[3] / 1000000), - cast_int32(info.frequency_times_ns[4] / 1000000), - cast_int32(info.frequency_times_ns[5] / 1000000), - cast_int32(info.frequency_times_ns[6] / 1000000), - cast_int32(info.frequency_times_ns[7] / 1000000), - cast_int32(info.frequency_times_ns[8] / 1000000), - cast_int32(info.frequency_times_ns[9] / 1000000), - cast_int32(info.frequency_times_ns[10] / 1000000), - cast_int32(info.frequency_times_ns[11] / 1000000), - cast_int32(info.frequency_times_ns[12] / 1000000), - cast_int32(info.frequency_times_ns[13] / 1000000), - cast_int32(info.frequency_times_ns[14] / 1000000), - cast_int32(info.frequency_times_ns[15] / 1000000), - cast_int32(info.frequency_times_ns[16] / 1000000), - cast_int32(info.frequency_times_ns[17] / 1000000), - cast_int32(info.frequency_times_ns[18] / 1000000), - cast_int32(info.frequency_times_ns[19] / 1000000), - // freq_1000_mhz_time_millis - cast_int32(info.frequency_times_ns[20] / 1000000)); + .count(); + if (duration > std::numeric_limits<int32_t>::max() || duration < 0) { + // This is essentially impossible. If it does somehow happen, give up, + // but still clear the map. + clearMap(); + return AStatsManager_PULL_SKIP; + } + + // Log an atom for each (gpu id, uid) pair for which we have data. + for (uint32_t gpuId : gpuIds) { + for (Uid uid : uids) { + auto it = workMap.find(GpuIdUid{gpuId, uid}); + if (it == workMap.end()) { + continue; + } + const UidTrackingInfo& info = it->second; + + uint64_t total_active_duration_ms = info.total_active_duration_ns / ONE_MS_IN_NS; + uint64_t total_inactive_duration_ms = info.total_inactive_duration_ns / ONE_MS_IN_NS; + + // Skip this atom if any numbers are out of range. |duration| is + // already checked above. + if (total_active_duration_ms > std::numeric_limits<int32_t>::max() || + total_inactive_duration_ms > std::numeric_limits<int32_t>::max()) { + continue; + } + + ALOGI("pullWorkAtoms: adding stats for GPU ID %" PRIu32 "; UID %" PRIu32, gpuId, uid); + android::util::addAStatsEvent(data, int32_t{android::util::GPU_WORK_PER_UID}, + // uid + bitcast_int32(uid), + // gpu_id + bitcast_int32(gpuId), + // time_duration_seconds + static_cast<int32_t>(duration), + // total_active_duration_millis + static_cast<int32_t>(total_active_duration_ms), + // total_inactive_duration_millis + static_cast<int32_t>(total_inactive_duration_ms)); + } } clearMap(); return AStatsManager_PULL_SUCCESS; @@ -435,7 +473,7 @@ void GpuWork::clearMapIfNeeded() { uint64_t numEntries = globalData.value().num_map_entries; // If the map is <=75% full, we do nothing. - if (numEntries <= (kMaxTrackedUids / 4) * 3) { + if (numEntries <= (kMaxTrackedGpuIdUids / 4) * 3) { return; } @@ -456,22 +494,22 @@ void GpuWork::clearMap() { // Iterating BPF maps to delete keys is tricky. If we just repeatedly call // |getFirstKey()| and delete that, we may loop forever (or for a long time) - // because our BPF program might be repeatedly re-adding UID keys. Also, - // even if we limit the number of elements we try to delete, we might only - // delete new entries, leaving old entries in the map. If we delete a key A - // and then call |getNextKey(A)|, the first key in the map is returned, so - // we have the same issue. + // because our BPF program might be repeatedly re-adding keys. Also, even if + // we limit the number of elements we try to delete, we might only delete + // new entries, leaving old entries in the map. If we delete a key A and + // then call |getNextKey(A)|, the first key in the map is returned, so we + // have the same issue. // // Thus, we instead get the next key and then delete the previous key. We // also limit the number of deletions we try, just in case. - base::Result<Uid> key = mGpuWorkMap.getFirstKey(); + base::Result<GpuIdUid> key = mGpuWorkMap.getFirstKey(); - for (size_t i = 0; i < kMaxTrackedUids; ++i) { + for (size_t i = 0; i < kMaxTrackedGpuIdUids; ++i) { if (!key.ok()) { break; } - base::Result<Uid> previousKey = key; + base::Result<GpuIdUid> previousKey = key; key = mGpuWorkMap.getNextKey(previousKey.value()); mGpuWorkMap.deleteValue(previousKey.value()); } diff --git a/services/gpuservice/gpuwork/bpfprogs/gpu_work.c b/services/gpuservice/gpuwork/bpfprogs/gpu_work.c index a0e1b22540..c8e79a2824 100644 --- a/services/gpuservice/gpuwork/bpfprogs/gpu_work.c +++ b/services/gpuservice/gpuwork/bpfprogs/gpu_work.c @@ -27,65 +27,123 @@ #endif #define S_IN_NS (1000000000) -#define MHZ_IN_KHZS (1000) +#define SMALL_TIME_GAP_LIMIT_NS (S_IN_NS) -typedef uint32_t Uid; - -// A map from UID to |UidTrackingInfo|. -DEFINE_BPF_MAP_GRW(gpu_work_map, HASH, Uid, UidTrackingInfo, kMaxTrackedUids, AID_GRAPHICS); +// A map from GpuIdUid (GPU ID and application UID) to |UidTrackingInfo|. +DEFINE_BPF_MAP_GRW(gpu_work_map, HASH, GpuIdUid, UidTrackingInfo, kMaxTrackedGpuIdUids, + AID_GRAPHICS); // A map containing a single entry of |GlobalData|. DEFINE_BPF_MAP_GRW(gpu_work_global_data, ARRAY, uint32_t, GlobalData, 1, AID_GRAPHICS); -// GpuUidWorkPeriodEvent defines the structure of a kernel tracepoint under the -// tracepoint system (also referred to as the group) "power" and name -// "gpu_work_period". In summary, the kernel tracepoint should be -// "power/gpu_work_period", available at: +// Defines the structure of the kernel tracepoint: // // /sys/kernel/tracing/events/power/gpu_work_period/ // -// GpuUidWorkPeriodEvent defines a non-overlapping, non-zero period of time when -// work was running on the GPU for a given application (identified by its UID; -// the persistent, unique ID of the application) from |start_time_ns| to -// |end_time_ns|. Drivers should issue this tracepoint as soon as possible -// (within 1 second) after |end_time_ns|. For a given UID, periods must not -// overlap, but periods from different UIDs can overlap (and should overlap, if -// and only if that is how the work was executed). The period includes -// information such as |frequency_khz|, the frequency that the GPU was running -// at during the period, and |includes_compute_work|, whether the work included -// compute shader work (not just graphics work). GPUs may have multiple -// frequencies that can be adjusted, but the driver should report the frequency -// that most closely estimates power usage (e.g. the frequency of shader cores, -// not a scheduling unit). +// Drivers must define an appropriate gpu_work_period kernel tracepoint (for +// example, using the DECLARE_EVENT_CLASS and DEFINE_EVENT macros) such that the +// arguments/fields match the fields of |GpuWorkPeriodEvent|, excluding the +// initial "common" field. Drivers must invoke the tracepoint (also referred to +// as emitting the event) as described below. Note that the description below +// assumes a single physical GPU and its driver; for devices with multiple GPUs, +// each GPU and its driver should emit events independently, using a different +// value for |gpu_id| per GPU. +// +// |GpuWorkPeriodEvent| defines a non-overlapping, non-zero period of time from +// |start_time_ns| (inclusive) until |end_time_ns| (exclusive) for a given +// |uid|, and includes details of how much work the GPU was performing for |uid| +// during the period. When GPU work for a given |uid| runs on the GPU, the +// driver must track one or more periods that cover the time where the work was +// running, and emit events soon after. The driver should try to emit the event +// for a period at most 1 second after |end_time_ns|, and must emit the event at +// most 2 seconds after |end_time_ns|. A period's duration (|end_time_ns| - +// |start_time_ns|) must be at most 1 second. Periods for different |uids| can +// overlap, but periods for the same |uid| must not overlap. The driver must +// emit events for the same |uid| in strictly increasing order of +// |start_time_ns|, such that it is guaranteed that the tracepoint call for a +// period for |uid| has returned before the tracepoint call for the next period +// for |uid| is made. Note that synchronization may be necessary if the driver +// emits events for the same |uid| from different threads/contexts. Note that +// |end_time_ns| for a period for a |uid| may equal the |start_time_ns| of the +// next period for |uid|. The driver should try to avoid emitting a large number +// of events in a short time period (e.g. 1000 events per second) for a given +// |uid|. +// +// The |total_active_duration_ns| must be set to the approximate total amount of +// time the GPU spent running work for |uid| within the period, without +// "double-counting" parallel GPU work on the same GPU for the same |uid|. "GPU +// work" should correspond to the "GPU slices" shown in the AGI (Android GPU +// Inspector) tool, and so should include work such as fragment and non-fragment +// work/shaders running on the shader cores of the GPU. For example, given the +// following: +// - A period has: +// - |start_time_ns|: 100,000,000 ns +// - |end_time_ns|: 800,000,000 ns +// - Some GPU vertex work (A): +// - started at: 200,000,000 ns +// - ended at: 400,000,000 ns +// - Some GPU fragment work (B): +// - started at: 300,000,000 ns +// - ended at: 500,000,000 ns +// - Some GPU fragment work (C): +// - started at: 300,000,000 ns +// - ended at: 400,000,000 ns +// - Some GPU fragment work (D): +// - started at: 600,000,000 ns +// - ended at: 700,000,000 ns +// +// The |total_active_duration_ns| would be 400,000,000 ns, because GPU work for +// |uid| was executing: +// - from 200,000,000 ns to 500,000,000 ns, giving a duration of 300,000,000 ns +// (encompassing GPU work A, B, and C) +// - from 600,000,000 ns to 700,000,000 ns, giving a duration of 100,000,000 ns +// (GPU work D) +// +// Thus, the |total_active_duration_ns| is the sum of the (non-overlapping) +// durations. Drivers may not have efficient access to the exact start and end +// times of all GPU work, as shown above, but drivers should try to +// approximate/aggregate the value of |total_active_duration_ns| as accurately +// as possible within the limitations of the hardware, without double-counting +// parallel GPU work for the same |uid|. The |total_active_duration_ns| value +// must be less than or equal to the period duration (|end_time_ns| - +// |start_time_ns|); if the aggregation approach might violate this requirement +// then the driver must clamp |total_active_duration_ns| to be at most the +// period duration. +// +// Note that the above description allows for a certain amount of flexibility in +// how the driver tracks periods and emits the events. We list a few examples of +// how drivers might implement the above: // -// If any information changes while work from the UID is running on the GPU -// (e.g. the GPU frequency changes, or the work starts/stops including compute -// work) then the driver must conceptually end the period, issue the tracepoint, -// start tracking a new period, and eventually issue a second tracepoint when -// the work completes or when the information changes again. In this case, the -// |end_time_ns| of the first period must equal the |start_time_ns| of the -// second period. The driver may also end and start a new period (without a -// gap), even if no information changes. For example, this might be convenient -// if there is a collection of work from a UID running on the GPU for a long -// time; ending and starting a period as individual parts of the work complete -// allows the consumer of the tracepoint to be updated about the ongoing work. +// - 1: The driver could track periods for all |uid| values at fixed intervals +// of 1 second. Thus, every period duration would be exactly 1 second, and +// periods from different |uid|s that overlap would have the same +// |start_time_ns| and |end_time_ns| values. // -// For a given UID, the tracepoints must be emitted in order; that is, the -// |start_time_ns| of each subsequent tracepoint for a given UID must be -// monotonically increasing. +// - 2: The driver could track periods with many different durations (up to 1 +// second), as needed in order to cover the GPU work for each |uid|. +// Overlapping periods for different |uid|s may have very different durations, +// as well as different |start_time_ns| and |end_time_ns| values. +// +// - 3: The driver could track fine-grained periods with different durations +// that precisely cover the time where GPU work is running for each |uid|. +// Thus, |total_active_duration_ns| would always equal the period duration. +// For example, if a game was running at 60 frames per second, the driver +// would most likely emit _at least_ 60 events per second (probably more, as +// there would likely be multiple "chunks" of GPU work per frame, with gaps +// between each chunk). However, the driver may sometimes need to resort to +// more coarse-grained periods to avoid emitting thousands of events per +// second for a |uid|, where |total_active_duration_ns| would then be less +// than the period duration. typedef struct { // Actual fields start at offset 8. - uint64_t reserved; + uint64_t common; - // The UID of the process (i.e. persistent, unique ID of the Android app) - // that submitted work to the GPU. - uint32_t uid; + // A value that uniquely identifies the GPU within the system. + uint32_t gpu_id; - // The GPU frequency during the period. GPUs may have multiple frequencies - // that can be adjusted, but the driver should report the frequency that - // would most closely estimate power usage (e.g. the frequency of shader - // cores, not a scheduling unit). - uint32_t frequency_khz; + // The UID of the application (i.e. persistent, unique ID of the Android + // app) that submitted work to the GPU. + uint32_t uid; // The start time of the period in nanoseconds. The clock must be // CLOCK_MONOTONIC, as returned by the ktime_get_ns(void) function. @@ -95,54 +153,44 @@ typedef struct { // CLOCK_MONOTONIC, as returned by the ktime_get_ns(void) function. uint64_t end_time_ns; - // Flags about the work. Reserved for future use. - uint64_t flags; - - // The maximum GPU frequency allowed during the period according to the - // thermal throttling policy. Must be 0 if no thermal throttling was - // enforced during the period. The value must relate to |frequency_khz|; in - // other words, if |frequency_khz| is the frequency of the GPU shader cores, - // then |thermally_throttled_max_frequency_khz| must be the maximum allowed - // frequency of the GPU shader cores (not the maximum allowed frequency of - // some other part of the GPU). - // - // Note: Unlike with other fields of this struct, if the - // |thermally_throttled_max_frequency_khz| value conceptually changes while - // work from a UID is running on the GPU then the GPU driver does NOT need - // to accurately report this by ending the period and starting to track a - // new period; instead, the GPU driver may report any one of the - // |thermally_throttled_max_frequency_khz| values that was enforced during - // the period. The motivation for this relaxation is that we assume the - // maximum frequency will not be changing rapidly, and so capturing the - // exact point at which the change occurs is unnecessary. - uint32_t thermally_throttled_max_frequency_khz; - -} GpuUidWorkPeriodEvent; - -_Static_assert(offsetof(GpuUidWorkPeriodEvent, uid) == 8 && - offsetof(GpuUidWorkPeriodEvent, frequency_khz) == 12 && - offsetof(GpuUidWorkPeriodEvent, start_time_ns) == 16 && - offsetof(GpuUidWorkPeriodEvent, end_time_ns) == 24 && - offsetof(GpuUidWorkPeriodEvent, flags) == 32 && - offsetof(GpuUidWorkPeriodEvent, thermally_throttled_max_frequency_khz) == 40, - "Field offsets of struct GpuUidWorkPeriodEvent must not be changed because they " + // The amount of time the GPU was running GPU work for |uid| during the + // period, in nanoseconds, without double-counting parallel GPU work for the + // same |uid|. For example, this might include the amount of time the GPU + // spent performing shader work (vertex work, fragment work, etc.) for + // |uid|. + uint64_t total_active_duration_ns; + +} GpuWorkPeriodEvent; + +_Static_assert(offsetof(GpuWorkPeriodEvent, gpu_id) == 8 && + offsetof(GpuWorkPeriodEvent, uid) == 12 && + offsetof(GpuWorkPeriodEvent, start_time_ns) == 16 && + offsetof(GpuWorkPeriodEvent, end_time_ns) == 24 && + offsetof(GpuWorkPeriodEvent, total_active_duration_ns) == 32, + "Field offsets of struct GpuWorkPeriodEvent must not be changed because they " "must match the tracepoint field offsets found via adb shell cat " "/sys/kernel/tracing/events/power/gpu_work_period/format"); DEFINE_BPF_PROG("tracepoint/power/gpu_work_period", AID_ROOT, AID_GRAPHICS, tp_gpu_work_period) -(GpuUidWorkPeriodEvent* const args) { - // Note: In BPF programs, |__sync_fetch_and_add| is translated to an atomic +(GpuWorkPeriodEvent* const period) { + // Note: In eBPF programs, |__sync_fetch_and_add| is translated to an atomic // add. - const uint32_t uid = args->uid; + // Return 1 to avoid blocking simpleperf from receiving events. + const int ALLOW = 1; + + GpuIdUid gpu_id_and_uid; + __builtin_memset(&gpu_id_and_uid, 0, sizeof(gpu_id_and_uid)); + gpu_id_and_uid.gpu_id = period->gpu_id; + gpu_id_and_uid.uid = period->uid; - // Get |UidTrackingInfo| for |uid|. - UidTrackingInfo* uid_tracking_info = bpf_gpu_work_map_lookup_elem(&uid); + // Get |UidTrackingInfo|. + UidTrackingInfo* uid_tracking_info = bpf_gpu_work_map_lookup_elem(&gpu_id_and_uid); if (!uid_tracking_info) { // There was no existing entry, so we add a new one. UidTrackingInfo initial_info; __builtin_memset(&initial_info, 0, sizeof(initial_info)); - if (0 == bpf_gpu_work_map_update_elem(&uid, &initial_info, BPF_NOEXIST)) { + if (0 == bpf_gpu_work_map_update_elem(&gpu_id_and_uid, &initial_info, BPF_NOEXIST)) { // We added an entry to the map, so we increment our entry counter in // |GlobalData|. const uint32_t zero = 0; @@ -154,66 +202,80 @@ DEFINE_BPF_PROG("tracepoint/power/gpu_work_period", AID_ROOT, AID_GRAPHICS, tp_g __sync_fetch_and_add(&global_data->num_map_entries, 1); } } - uid_tracking_info = bpf_gpu_work_map_lookup_elem(&uid); + uid_tracking_info = bpf_gpu_work_map_lookup_elem(&gpu_id_and_uid); if (!uid_tracking_info) { // This should never happen, unless entries are getting deleted at // this moment. If so, we just give up. - return 0; + return ALLOW; } } - // The period duration must be non-zero. - if (args->start_time_ns >= args->end_time_ns) { + if ( + // The period duration must be non-zero. + period->start_time_ns >= period->end_time_ns || + // The period duration must be at most 1 second. + (period->end_time_ns - period->start_time_ns) > S_IN_NS) { __sync_fetch_and_add(&uid_tracking_info->error_count, 1); - return 0; + return ALLOW; } - // The frequency must not be 0. - if (args->frequency_khz == 0) { - __sync_fetch_and_add(&uid_tracking_info->error_count, 1); - return 0; + // If |total_active_duration_ns| is 0 then no GPU work occurred and there is + // nothing to do. + if (period->total_active_duration_ns == 0) { + return ALLOW; } - // Calculate the frequency index: see |UidTrackingInfo.frequency_times_ns|. - // Round to the nearest 50MHz bucket. - uint32_t frequency_index = - ((args->frequency_khz / MHZ_IN_KHZS) + (kFrequencyGranularityMhz / 2)) / - kFrequencyGranularityMhz; - if (frequency_index >= kNumTrackedFrequencies) { - frequency_index = kNumTrackedFrequencies - 1; - } + // Update |uid_tracking_info->total_active_duration_ns|. + __sync_fetch_and_add(&uid_tracking_info->total_active_duration_ns, + period->total_active_duration_ns); - // Never round down to 0MHz, as this is a special bucket (see below) and not - // an actual operating point. - if (frequency_index == 0) { - frequency_index = 1; - } + // |small_gap_time_ns| is the time gap between the current and previous + // active period, which could be 0. If the gap is more than + // |SMALL_TIME_GAP_LIMIT_NS| then |small_gap_time_ns| will be set to 0 + // because we want to estimate the small gaps between "continuous" GPU work. + uint64_t small_gap_time_ns = 0; + if (uid_tracking_info->previous_active_end_time_ns > period->start_time_ns) { + // The current period appears to have occurred before the previous + // active period, which must not happen because per-UID periods must not + // overlap and must be emitted in strictly increasing order of + // |start_time_ns|. + __sync_fetch_and_add(&uid_tracking_info->error_count, 1); + } else { + // The current period appears to have been emitted after the previous + // active period, as expected, so we can calculate the gap between the + // current and previous active period. + small_gap_time_ns = period->start_time_ns - uid_tracking_info->previous_active_end_time_ns; - // Update time in state. - __sync_fetch_and_add(&uid_tracking_info->frequency_times_ns[frequency_index], - args->end_time_ns - args->start_time_ns); + // Update |previous_active_end_time_ns|. + uid_tracking_info->previous_active_end_time_ns = period->end_time_ns; - if (uid_tracking_info->previous_end_time_ns > args->start_time_ns) { - // This must not happen because per-UID periods must not overlap and - // must be emitted in order. + // We want to estimate the small gaps between "continuous" GPU work; if + // the gap is more than |SMALL_TIME_GAP_LIMIT_NS| then we don't consider + // this "continuous" GPU work. + if (small_gap_time_ns > SMALL_TIME_GAP_LIMIT_NS) { + small_gap_time_ns = 0; + } + } + + uint64_t period_total_inactive_time_ns = 0; + const uint64_t period_duration_ns = period->end_time_ns - period->start_time_ns; + // |period->total_active_duration_ns| is the active time within the period duration, so + // it must not be larger than |period_duration_ns|. + if (period->total_active_duration_ns > period_duration_ns) { __sync_fetch_and_add(&uid_tracking_info->error_count, 1); } else { - // The period appears to have been emitted after the previous, as - // expected, so we can calculate the gap between this and the previous - // period. - const uint64_t gap_time = args->start_time_ns - uid_tracking_info->previous_end_time_ns; - - // Update |previous_end_time_ns|. - uid_tracking_info->previous_end_time_ns = args->end_time_ns; - - // Update the special 0MHz frequency time, which stores the gaps between - // periods, but only if the gap is < 1 second. - if (gap_time > 0 && gap_time < S_IN_NS) { - __sync_fetch_and_add(&uid_tracking_info->frequency_times_ns[0], gap_time); - } + period_total_inactive_time_ns = period_duration_ns - period->total_active_duration_ns; + } + + // Update |uid_tracking_info->total_inactive_duration_ns| by adding the + // inactive time from this period, plus the small gap between the current + // and previous active period. Either or both of these values could be 0. + if (small_gap_time_ns > 0 || period_total_inactive_time_ns > 0) { + __sync_fetch_and_add(&uid_tracking_info->total_inactive_duration_ns, + small_gap_time_ns + period_total_inactive_time_ns); } - return 0; + return ALLOW; } LICENSE("Apache 2.0"); diff --git a/services/gpuservice/gpuwork/bpfprogs/include/gpuwork/gpu_work.h b/services/gpuservice/gpuwork/bpfprogs/include/gpuwork/gpu_work.h index 4fe84649a0..57338f4c91 100644 --- a/services/gpuservice/gpuwork/bpfprogs/include/gpuwork/gpu_work.h +++ b/services/gpuservice/gpuwork/bpfprogs/include/gpuwork/gpu_work.h @@ -25,18 +25,28 @@ namespace android { namespace gpuwork { #endif +typedef struct { + uint32_t gpu_id; + uint32_t uid; +} GpuIdUid; + typedef struct { - // The end time of the previous period from this UID in nanoseconds. - uint64_t previous_end_time_ns; + // The end time of the previous period where the GPU was active for the UID, + // in nanoseconds. + uint64_t previous_active_end_time_ns; + + // The total amount of time the GPU has spent running work for the UID, in + // nanoseconds. + uint64_t total_active_duration_ns; - // The time spent at each GPU frequency while running GPU work from the UID, - // in nanoseconds. Array index i stores the time for frequency i*50 MHz. So - // index 0 is 0Mhz, index 1 is 50MHz, index 2 is 100MHz, etc., up to index - // |kNumTrackedFrequencies|. - uint64_t frequency_times_ns[21]; + // The total amount of time of the "gaps" between "continuous" GPU work for + // the UID, in nanoseconds. This is estimated by ignoring large gaps between + // GPU work for this UID. + uint64_t total_inactive_duration_ns; - // The number of times we received |GpuUidWorkPeriodEvent| events in an - // unexpected order. See |GpuUidWorkPeriodEvent|. + // The number of errors detected due to |GpuWorkPeriodEvent| events for the + // UID violating the specification in some way. E.g. periods with a zero or + // negative duration. uint32_t error_count; } UidTrackingInfo; @@ -48,14 +58,10 @@ typedef struct { uint64_t num_map_entries; } GlobalData; -static const uint32_t kMaxTrackedUids = 512; -static const uint32_t kFrequencyGranularityMhz = 50; -static const uint32_t kNumTrackedFrequencies = 21; +// The maximum number of tracked GPU ID and UID pairs (|GpuIdUid|). +static const uint32_t kMaxTrackedGpuIdUids = 512; #ifdef __cplusplus -static_assert(kNumTrackedFrequencies == - std::extent<decltype(UidTrackingInfo::frequency_times_ns)>::value); - } // namespace gpuwork } // namespace android #endif diff --git a/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h b/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h index b6f493d557..acecd569aa 100644 --- a/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h +++ b/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h @@ -41,7 +41,7 @@ public: void initialize(); - // Dumps the GPU time in frequency state information. + // Dumps the GPU work information. void dump(const Vector<String16>& args, std::string* result); private: @@ -55,7 +55,7 @@ private: AStatsEventList* data, void* cookie); - AStatsManager_PullAtomCallbackReturn pullFrequencyAtoms(AStatsEventList* data); + AStatsManager_PullAtomCallbackReturn pullWorkAtoms(AStatsEventList* data); // Periodically calls |clearMapIfNeeded| to clear the |mGpuWorkMap| map, if // needed. @@ -88,7 +88,7 @@ private: std::mutex mMutex; // BPF map for per-UID GPU work. - bpf::BpfMap<Uid, UidTrackingInfo> mGpuWorkMap GUARDED_BY(mMutex); + bpf::BpfMap<GpuIdUid, UidTrackingInfo> mGpuWorkMap GUARDED_BY(mMutex); // BPF map containing a single element for global data. bpf::BpfMap<uint32_t, GlobalData> mGpuWorkGlobalDataMap GUARDED_BY(mMutex); @@ -110,7 +110,18 @@ private: bool mStatsdRegistered GUARDED_BY(mMutex) = false; // The number of randomly chosen (i.e. sampled) UIDs to log stats for. - static constexpr int kNumSampledUids = 10; + static constexpr size_t kNumSampledUids = 10; + + // A "large" number of GPUs. If we observe more GPUs than this limit then + // we reduce the amount of stats we log. + static constexpr size_t kNumGpusSoftLimit = 4; + + // A "very large" number of GPUs. If we observe more GPUs than this limit + // then we don't log any stats. + static constexpr size_t kNumGpusHardLimit = 32; + + // The minimum GPU time needed to actually log stats for a UID. + static constexpr uint64_t kMinGpuTimeNanoseconds = 30U * 1000000000U; // 30 seconds. // The previous time point at which |mGpuWorkMap| was cleared. std::chrono::steady_clock::time_point mPreviousMapClearTimePoint GUARDED_BY(mMutex); diff --git a/services/gpuservice/vts/AndroidTest.xml b/services/gpuservice/vts/AndroidTest.xml index 02ca07f968..34dc2eac06 100644 --- a/services/gpuservice/vts/AndroidTest.xml +++ b/services/gpuservice/vts/AndroidTest.xml @@ -14,6 +14,9 @@ limitations under the License. --> <configuration description="Runs GpuServiceVendorTests"> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"> + <option name="force-root" value="false" /> + </target_preparer> <test class="com.android.tradefed.testtype.HostTest" > <option name="jar" value="GpuServiceVendorTests.jar" /> </test> diff --git a/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java b/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java index 4a77d9ab92..9fa901675d 100644 --- a/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java +++ b/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java @@ -78,12 +78,11 @@ public class GpuWorkTracepointTest extends BaseHostJUnit4Test { "\tfield:unsigned char common_flags;\toffset:2;\tsize:1;\tsigned:0;", "\tfield:unsigned char common_preempt_count;\toffset:3;\tsize:1;\tsigned:0;", "\tfield:int common_pid;\toffset:4;\tsize:4;\tsigned:1;", - "\tfield:u32 uid;\toffset:8;\tsize:4;\tsigned:0;", - "\tfield:u32 frequency;\toffset:12;\tsize:4;\tsigned:0;", - "\tfield:u64 start_time;\toffset:16;\tsize:8;\tsigned:0;", - "\tfield:u64 end_time;\toffset:24;\tsize:8;\tsigned:0;", - "\tfield:u64 flags;\toffset:32;\tsize:8;\tsigned:0;", - "\tfield:u32 thermally_throttled_max_frequency_khz;\toffset:40;\tsize:4;\tsigned:0;" + "\tfield:u32 gpu_id;\toffset:8;\tsize:4;\tsigned:0;", + "\tfield:u32 uid;\toffset:12;\tsize:4;\tsigned:0;", + "\tfield:u64 start_time_ns;\toffset:16;\tsize:8;\tsigned:0;", + "\tfield:u64 end_time_ns;\toffset:24;\tsize:8;\tsigned:0;", + "\tfield:u64 total_active_duration_ns;\toffset:32;\tsize:8;\tsigned:0;" ); // We use |fail| rather than |assertEquals| because it allows us to give a clearer message. diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 32c3a12a13..bfdd22c3df 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -66,35 +66,77 @@ namespace android::inputdispatcher { namespace { -// Log detailed debug messages about each inbound event notification to the dispatcher. -constexpr bool DEBUG_INBOUND_EVENT_DETAILS = false; +/** + * Log detailed debug messages about each inbound event notification to the dispatcher. + * Enable this via "adb shell setprop log.tag.InputDispatcherInboundEvent DEBUG" (requires restart) + */ +const bool DEBUG_INBOUND_EVENT_DETAILS = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "InboundEvent", ANDROID_LOG_INFO); -// Log detailed debug messages about each outbound event processed by the dispatcher. -constexpr bool DEBUG_OUTBOUND_EVENT_DETAILS = false; +/** + * Log detailed debug messages about each outbound event processed by the dispatcher. + * Enable this via "adb shell setprop log.tag.InputDispatcherOutboundEvent DEBUG" (requires restart) + */ +const bool DEBUG_OUTBOUND_EVENT_DETAILS = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "OutboundEvent", ANDROID_LOG_INFO); -// Log debug messages about the dispatch cycle. -constexpr bool DEBUG_DISPATCH_CYCLE = false; +/** + * Log debug messages about the dispatch cycle. + * Enable this via "adb shell setprop log.tag.InputDispatcherDispatchCycle DEBUG" (requires restart) + */ +const bool DEBUG_DISPATCH_CYCLE = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "DispatchCycle", ANDROID_LOG_INFO); -// Log debug messages about channel creation -constexpr bool DEBUG_CHANNEL_CREATION = false; +/** + * Log debug messages about channel creation + * Enable this via "adb shell setprop log.tag.InputDispatcherChannelCreation DEBUG" (requires + * restart) + */ +const bool DEBUG_CHANNEL_CREATION = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "ChannelCreation", ANDROID_LOG_INFO); -// Log debug messages about input event injection. -constexpr bool DEBUG_INJECTION = false; +/** + * Log debug messages about input event injection. + * Enable this via "adb shell setprop log.tag.InputDispatcherInjection DEBUG" (requires restart) + */ +const bool DEBUG_INJECTION = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Injection", ANDROID_LOG_INFO); -// Log debug messages about input focus tracking. -constexpr bool DEBUG_FOCUS = false; +/** + * Log debug messages about input focus tracking. + * Enable this via "adb shell setprop log.tag.InputDispatcherFocus DEBUG" (requires restart) + */ +const bool DEBUG_FOCUS = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Focus", ANDROID_LOG_INFO); -// Log debug messages about touch mode event -constexpr bool DEBUG_TOUCH_MODE = false; +/** + * Log debug messages about touch mode event + * Enable this via "adb shell setprop log.tag.InputDispatcherTouchMode DEBUG" (requires restart) + */ +const bool DEBUG_TOUCH_MODE = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "TouchMode", ANDROID_LOG_INFO); -// Log debug messages about touch occlusion -constexpr bool DEBUG_TOUCH_OCCLUSION = true; +/** + * Log debug messages about touch occlusion + * Enable this via "adb shell setprop log.tag.InputDispatcherTouchOcclusion DEBUG" (requires + * restart) + */ +const bool DEBUG_TOUCH_OCCLUSION = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "TouchOcclusion", ANDROID_LOG_INFO); -// Log debug messages about the app switch latency optimization. -constexpr bool DEBUG_APP_SWITCH = false; +/** + * Log debug messages about the app switch latency optimization. + * Enable this via "adb shell setprop log.tag.InputDispatcherAppSwitch DEBUG" (requires restart) + */ +const bool DEBUG_APP_SWITCH = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "AppSwitch", ANDROID_LOG_INFO); -// Log debug messages about hover events. -constexpr bool DEBUG_HOVER = false; +/** + * Log debug messages about hover events. + * Enable this via "adb shell setprop log.tag.InputDispatcherHover DEBUG" (requires restart) + */ +const bool DEBUG_HOVER = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Hover", ANDROID_LOG_INFO); // Temporarily releases a held mutex for the lifetime of the instance. // Named to match std::scoped_lock @@ -2698,18 +2740,16 @@ InputDispatcher::TouchOcclusionInfo InputDispatcher::computeTouchOcclusionInfoLo std::string InputDispatcher::dumpWindowForTouchOcclusion(const WindowInfo* info, bool isTouchedWindow) const { - return StringPrintf(INDENT2 - "* %spackage=%s/%" PRId32 ", id=%" PRId32 ", mode=%s, alpha=%.2f, " - "frame=[%" PRId32 ",%" PRId32 "][%" PRId32 ",%" PRId32 - "], touchableRegion=%s, window={%s}, inputConfig={%s}, inputFeatures={%s}, " - "hasToken=%s, applicationInfo.name=%s, applicationInfo.token=%s\n", + return StringPrintf(INDENT2 "* %spackage=%s/%" PRId32 ", id=%" PRId32 ", mode=%s, alpha=%.2f, " + "frame=[%" PRId32 ",%" PRId32 "][%" PRId32 ",%" PRId32 + "], touchableRegion=%s, window={%s}, inputConfig={%s}, " + "hasToken=%s, applicationInfo.name=%s, applicationInfo.token=%s\n", isTouchedWindow ? "[TOUCHED] " : "", info->packageName.c_str(), info->ownerUid, info->id, toString(info->touchOcclusionMode).c_str(), info->alpha, info->frameLeft, info->frameTop, info->frameRight, info->frameBottom, dumpRegion(info->touchableRegion).c_str(), info->name.c_str(), info->inputConfig.string().c_str(), - info->inputFeatures.string().c_str(), toString(info->token != nullptr), - info->applicationInfo.name.c_str(), + toString(info->token != nullptr), info->applicationInfo.name.c_str(), toString(info->applicationInfo.token).c_str()); } @@ -2787,7 +2827,7 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { sp<WindowInfoHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId); if (focusedWindowHandle != nullptr) { const WindowInfo* info = focusedWindowHandle->getInfo(); - if (info->inputFeatures.test(WindowInfo::Feature::DISABLE_USER_ACTIVITY)) { + if (info->inputConfig.test(WindowInfo::InputConfig::DISABLE_USER_ACTIVITY)) { if (DEBUG_DISPATCH_CYCLE) { ALOGD("Not poking user activity: disabled by window '%s'.", info->name.c_str()); } @@ -4516,7 +4556,7 @@ sp<WindowInfoHandle> InputDispatcher::getFocusedWindowHandleLocked(int displayId bool InputDispatcher::hasResponsiveConnectionLocked(WindowInfoHandle& windowHandle) const { sp<Connection> connection = getConnectionLocked(windowHandle.getToken()); const bool noInputChannel = - windowHandle.getInfo()->inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL); + windowHandle.getInfo()->inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL); if (connection != nullptr && noInputChannel) { ALOGW("%s has feature NO_INPUT_CHANNEL, but it matched to connection %s", windowHandle.getName().c_str(), connection->inputChannel->getName().c_str()); @@ -4566,7 +4606,7 @@ void InputDispatcher::updateWindowHandlesForDisplayLocked( const WindowInfo* info = handle->getInfo(); if (getInputChannelLocked(handle->getToken()) == nullptr) { const bool noInputChannel = - info->inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL); + info->inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL); const bool canReceiveInput = !info->inputConfig.test(WindowInfo::InputConfig::NOT_TOUCHABLE) || !info->inputConfig.test(WindowInfo::InputConfig::NOT_FOCUSABLE); @@ -4632,7 +4672,7 @@ void InputDispatcher::setInputWindowsLocked( const WindowInfo& info = *window->getInfo(); // Ensure all tokens are null if the window has feature NO_INPUT_CHANNEL - const bool noInputWindow = info.inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL); + const bool noInputWindow = info.inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL); if (noInputWindow && window->getToken() != nullptr) { ALOGE("%s has feature NO_INPUT_WINDOW, but a non-null token. Clearing", window->getName().c_str()); @@ -5209,8 +5249,6 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { windowInfo->applicationInfo.name.c_str(), toString(windowInfo->applicationInfo.token).c_str()); dump += dumpRegion(windowInfo->touchableRegion); - dump += StringPrintf(", inputFeatures=%s", - windowInfo->inputFeatures.string().c_str()); dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%" PRId64 "ms, hasToken=%s, " "touchOcclusionMode=%s\n", @@ -6248,13 +6286,14 @@ void InputDispatcher::onWindowInfosChanged(const std::vector<WindowInfo>& window bool InputDispatcher::shouldDropInput( const EventEntry& entry, const sp<android::gui::WindowInfoHandle>& windowHandle) const { - if (windowHandle->getInfo()->inputFeatures.test(WindowInfo::Feature::DROP_INPUT) || - (windowHandle->getInfo()->inputFeatures.test(WindowInfo::Feature::DROP_INPUT_IF_OBSCURED) && + if (windowHandle->getInfo()->inputConfig.test(WindowInfo::InputConfig::DROP_INPUT) || + (windowHandle->getInfo()->inputConfig.test( + WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED) && isWindowObscuredLocked(windowHandle))) { - ALOGW("Dropping %s event targeting %s as requested by input feature %s on display " - "%" PRId32 ".", + ALOGW("Dropping %s event targeting %s as requested by the input configuration {%s} on " + "display %" PRId32 ".", ftl::enum_string(entry.type).c_str(), windowHandle->getName().c_str(), - windowHandle->getInfo()->inputFeatures.string().c_str(), + windowHandle->getInfo()->inputConfig.string().c_str(), windowHandle->getInfo()->displayId); return true; } diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index dbbc734b91..470d2f686d 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -994,7 +994,7 @@ public: mInfo.ownerPid = INJECTOR_PID; mInfo.ownerUid = INJECTOR_UID; mInfo.displayId = displayId; - mInfo.inputConfig = WindowInfo::InputConfig::NONE; + mInfo.inputConfig = WindowInfo::InputConfig::DEFAULT; } sp<FakeWindowHandle> clone( @@ -1038,6 +1038,24 @@ public: mInfo.setInputConfig(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH, watchOutside); } + void setSpy(bool spy) { mInfo.setInputConfig(WindowInfo::InputConfig::SPY, spy); } + + void setInterceptsStylus(bool interceptsStylus) { + mInfo.setInputConfig(WindowInfo::InputConfig::INTERCEPTS_STYLUS, interceptsStylus); + } + + void setDropInput(bool dropInput) { + mInfo.setInputConfig(WindowInfo::InputConfig::DROP_INPUT, dropInput); + } + + void setDropInputIfObscured(bool dropInputIfObscured) { + mInfo.setInputConfig(WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED, dropInputIfObscured); + } + + void setNoInputChannel(bool noInputChannel) { + mInfo.setInputConfig(WindowInfo::InputConfig::NO_INPUT_CHANNEL, noInputChannel); + } + void setAlpha(float alpha) { mInfo.alpha = alpha; } void setTouchOcclusionMode(TouchOcclusionMode mode) { mInfo.touchOcclusionMode = mode; } @@ -1068,8 +1086,6 @@ public: mInfo.setInputConfig(WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER, hasWallpaper); } - void setInputFeatures(Flags<WindowInfo::Feature> features) { mInfo.inputFeatures = features; } - void setTrustedOverlay(bool trustedOverlay) { mInfo.setInputConfig(WindowInfo::InputConfig::TRUSTED_OVERLAY, trustedOverlay); } @@ -1222,7 +1238,7 @@ public: void assertNoEvents() { if (mInputReceiver == nullptr && - mInfo.inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL)) { + mInfo.inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL)) { return; // Can't receive events if the window does not have input channel } ASSERT_NE(nullptr, mInputReceiver) @@ -4333,7 +4349,7 @@ protected: new FakeWindowHandle(mApplication, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT); spy->setTrustedOverlay(true); spy->setFocusable(false); - spy->setInputFeatures(WindowInfo::Feature::SPY); + spy->setSpy(true); spy->setDispatchingTimeout(30ms); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, mWindow}}}); return spy; @@ -5122,7 +5138,7 @@ class InputDispatcherMultiWindowOcclusionTests : public InputDispatcherTest { "Window without input channel", ADISPLAY_ID_DEFAULT, std::make_optional<sp<IBinder>>(nullptr) /*token*/); - mNoInputWindow->setInputFeatures(WindowInfo::Feature::NO_INPUT_CHANNEL); + mNoInputWindow->setNoInputChannel(true); mNoInputWindow->setFrame(Rect(0, 0, 100, 100)); // It's perfectly valid for this window to not have an associated input channel @@ -5164,7 +5180,7 @@ TEST_F(InputDispatcherMultiWindowOcclusionTests, "Window with input channel and NO_INPUT_CHANNEL", ADISPLAY_ID_DEFAULT); - mNoInputWindow->setInputFeatures(WindowInfo::Feature::NO_INPUT_CHANNEL); + mNoInputWindow->setNoInputChannel(true); mNoInputWindow->setFrame(Rect(0, 0, 100, 100)); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mNoInputWindow, mBottomWindow}}}); @@ -6079,7 +6095,7 @@ TEST_F(InputDispatcherDropInputFeatureTest, WindowDropsInput) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT); - window->setInputFeatures(WindowInfo::Feature::DROP_INPUT); + window->setDropInput(true); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); window->setFocusable(true); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); @@ -6098,7 +6114,7 @@ TEST_F(InputDispatcherDropInputFeatureTest, WindowDropsInput) { window->assertNoEvents(); // With the flag cleared, the window should get input - window->setInputFeatures({}); + window->setDropInput(false); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT); @@ -6124,7 +6140,7 @@ TEST_F(InputDispatcherDropInputFeatureTest, ObscuredWindowDropsInput) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT); - window->setInputFeatures(WindowInfo::Feature::DROP_INPUT_IF_OBSCURED); + window->setDropInputIfObscured(true); window->setOwnerInfo(222, 222); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); window->setFocusable(true); @@ -6144,7 +6160,7 @@ TEST_F(InputDispatcherDropInputFeatureTest, ObscuredWindowDropsInput) { window->assertNoEvents(); // With the flag cleared, the window should get input - window->setInputFeatures({}); + window->setDropInputIfObscured(false); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}}); keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT); @@ -6170,7 +6186,7 @@ TEST_F(InputDispatcherDropInputFeatureTest, UnobscuredWindowGetsInput) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT); - window->setInputFeatures(WindowInfo::Feature::DROP_INPUT_IF_OBSCURED); + window->setDropInputIfObscured(true); window->setOwnerInfo(222, 222); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); window->setFocusable(true); @@ -6279,7 +6295,7 @@ public: name += std::to_string(mSpyCount++); sp<FakeWindowHandle> spy = new FakeWindowHandle(application, mDispatcher, name.c_str(), ADISPLAY_ID_DEFAULT); - spy->setInputFeatures(WindowInfo::Feature::SPY); + spy->setSpy(true); spy->setTrustedOverlay(true); return spy; } @@ -6719,7 +6735,7 @@ public: overlay->setFocusable(false); overlay->setOwnerInfo(111, 111); overlay->setTouchable(false); - overlay->setInputFeatures(WindowInfo::Feature::INTERCEPTS_STYLUS); + overlay->setInterceptsStylus(true); overlay->setTrustedOverlay(true); std::shared_ptr<FakeApplicationHandle> application = @@ -6785,7 +6801,7 @@ TEST_F(InputDispatcherStylusInterceptorTest, ConsmesOnlyStylusEvents) { TEST_F(InputDispatcherStylusInterceptorTest, SpyWindowStylusInterceptor) { auto [overlay, window] = setupStylusOverlayScenario(); - overlay->setInputFeatures(overlay->getInfo()->inputFeatures | WindowInfo::Feature::SPY); + overlay->setSpy(true); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}}); sendStylusEvent(AMOTION_EVENT_ACTION_DOWN); diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 4de8dc2ea9..aeaf1e1a14 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -2266,14 +2266,14 @@ gui::DropInputMode Layer::getDropInputMode() const { } void Layer::handleDropInputMode(gui::WindowInfo& info) const { - if (mDrawingState.inputInfo.inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL)) { + if (mDrawingState.inputInfo.inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL)) { return; } // Check if we need to drop input unconditionally gui::DropInputMode dropInputMode = getDropInputMode(); if (dropInputMode == gui::DropInputMode::ALL) { - info.inputFeatures |= WindowInfo::Feature::DROP_INPUT; + info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT; ALOGV("Dropping input for %s as requested by policy.", getDebugName()); return; } @@ -2286,7 +2286,7 @@ void Layer::handleDropInputMode(gui::WindowInfo& info) const { // Check if the parent has set an alpha on the layer sp<Layer> parent = mDrawingParent.promote(); if (parent && parent->getAlpha() != 1.0_hf) { - info.inputFeatures |= WindowInfo::Feature::DROP_INPUT; + info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT; ALOGV("Dropping input for %s as requested by policy because alpha=%f", getDebugName(), static_cast<float>(getAlpha())); } @@ -2294,7 +2294,7 @@ void Layer::handleDropInputMode(gui::WindowInfo& info) const { // Check if the parent has cropped the buffer Rect bufferSize = getCroppedBufferSize(getDrawingState()); if (!bufferSize.isValid()) { - info.inputFeatures |= WindowInfo::Feature::DROP_INPUT_IF_OBSCURED; + info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED; return; } @@ -2306,7 +2306,7 @@ void Layer::handleDropInputMode(gui::WindowInfo& info) const { bool croppedByParent = bufferInScreenSpace != Rect{mScreenBounds}; if (croppedByParent) { - info.inputFeatures |= WindowInfo::Feature::DROP_INPUT; + info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT; ALOGV("Dropping input for %s as requested by policy because buffer is cropped by parent", getDebugName()); } else { @@ -2314,7 +2314,7 @@ void Layer::handleDropInputMode(gui::WindowInfo& info) const { // input if the window is obscured. This check should be done in surfaceflinger but the // logic currently resides in inputflinger. So pass the if_obscured check to input to only // drop input events if the window is obscured. - info.inputFeatures |= WindowInfo::Feature::DROP_INPUT_IF_OBSCURED; + info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED; } } @@ -2323,7 +2323,7 @@ WindowInfo Layer::fillInputInfo(const ui::Transform& displayTransform, bool disp mDrawingState.inputInfo.name = getName(); mDrawingState.inputInfo.ownerUid = mOwnerUid; mDrawingState.inputInfo.ownerPid = mOwnerPid; - mDrawingState.inputInfo.inputFeatures = WindowInfo::Feature::NO_INPUT_CHANNEL; + mDrawingState.inputInfo.inputConfig |= WindowInfo::InputConfig::NO_INPUT_CHANNEL; mDrawingState.inputInfo.displayId = getLayerStack().id; } @@ -2351,7 +2351,7 @@ WindowInfo Layer::fillInputInfo(const ui::Transform& displayTransform, bool disp // If the window will be blacked out on a display because the display does not have the secure // flag and the layer has the secure flag set, then drop input. if (!displayIsSecure && isSecure()) { - info.inputFeatures |= WindowInfo::Feature::DROP_INPUT; + info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT; } auto cropLayer = mDrawingState.touchableRegionCrop.promote(); diff --git a/services/surfaceflinger/OWNERS b/services/surfaceflinger/OWNERS index 43a6e55a09..2ece51c3dc 100644 --- a/services/surfaceflinger/OWNERS +++ b/services/surfaceflinger/OWNERS @@ -3,4 +3,5 @@ alecmouri@google.com chaviw@google.com lpy@google.com racarr@google.com -vishnun@google.com +scroggo@google.com +vishnun@google.com
\ No newline at end of file diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index e8034de52c..350d7096aa 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -6063,7 +6063,6 @@ void SurfaceFlinger::kernelTimerChanged(bool expired) { static bool updateOverlay = property_get_bool("debug.sf.kernel_idle_timer_update_overlay", true); if (!updateOverlay) return; - if (Mutex::Autolock lock(mStateLock); !isRefreshRateOverlayEnabled()) return; // Update the overlay on the main thread to avoid race conditions with // mRefreshRateConfigs->getCurrentRefreshRate() @@ -6073,6 +6072,7 @@ void SurfaceFlinger::kernelTimerChanged(bool expired) { ALOGW("%s: default display is null", __func__); return; } + if (!display->isRefreshRateOverlayEnabled()) return; const auto desiredActiveMode = display->getDesiredActiveMode(); const std::optional<DisplayModeId> desiredModeId = desiredActiveMode diff --git a/services/surfaceflinger/tests/tracing/Android.bp b/services/surfaceflinger/tests/tracing/Android.bp index ff3e1c5f4e..aa6c74eb65 100644 --- a/services/surfaceflinger/tests/tracing/Android.bp +++ b/services/surfaceflinger/tests/tracing/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_test { name: "transactiontrace_testsuite", defaults: [ diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index 58cd7ba96a..1eea023f17 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -89,6 +89,8 @@ cc_test { "LayerHistoryTest.cpp", "LayerInfoTest.cpp", "LayerMetadataTest.cpp", + "LayerTest.cpp", + "LayerTestUtils.cpp", "MessageQueueTest.cpp", "SurfaceFlinger_CreateDisplayTest.cpp", "SurfaceFlinger_DestroyDisplayTest.cpp", diff --git a/services/surfaceflinger/tests/unittests/LayerTest.cpp b/services/surfaceflinger/tests/unittests/LayerTest.cpp new file mode 100644 index 0000000000..4974f90383 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/LayerTest.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#undef LOG_TAG +#define LOG_TAG "LibSurfaceFlingerUnittests" + +#include <EffectLayer.h> +#include <gtest/gtest.h> +#include <ui/FloatRect.h> +#include <ui/Transform.h> +#include <limits> + +#include "LayerTestUtils.h" +#include "TestableSurfaceFlinger.h" + +namespace android { +namespace { + +class LayerTest : public BaseLayerTest { +protected: + static constexpr const float MIN_FLOAT = std::numeric_limits<float>::min(); + static constexpr const float MAX_FLOAT = std::numeric_limits<float>::max(); + static constexpr const FloatRect LARGE_FLOAT_RECT{MIN_FLOAT, MIN_FLOAT, MAX_FLOAT, MAX_FLOAT}; +}; + +INSTANTIATE_TEST_SUITE_P(PerLayerType, LayerTest, + testing::Values(std::make_shared<BufferStateLayerFactory>(), + std::make_shared<EffectLayerFactory>()), + PrintToStringParamName); + +TEST_P(LayerTest, layerVisibleByDefault) { + sp<Layer> layer = GetParam()->createLayer(mFlinger); + layer->updateGeometry(); + layer->computeBounds(LARGE_FLOAT_RECT, ui::Transform(), 0.f); + ASSERT_FALSE(layer->isHiddenByPolicy()); +} + +TEST_P(LayerTest, hideLayerWithZeroMatrix) { + sp<Layer> layer = GetParam()->createLayer(mFlinger); + + layer_state_t::matrix22_t matrix{0, 0, 0, 0}; + layer->setMatrix(matrix); + layer->updateGeometry(); + layer->computeBounds(LARGE_FLOAT_RECT, ui::Transform(), 0.f); + + ASSERT_TRUE(layer->isHiddenByPolicy()); +} + +TEST_P(LayerTest, hideLayerWithInfMatrix) { + sp<Layer> layer = GetParam()->createLayer(mFlinger); + + constexpr const float INF = std::numeric_limits<float>::infinity(); + layer_state_t::matrix22_t matrix{INF, 0, 0, INF}; + layer->setMatrix(matrix); + layer->updateGeometry(); + layer->computeBounds(LARGE_FLOAT_RECT, ui::Transform(), 0.f); + + ASSERT_TRUE(layer->isHiddenByPolicy()); +} + +TEST_P(LayerTest, hideLayerWithNanMatrix) { + sp<Layer> layer = GetParam()->createLayer(mFlinger); + + constexpr const float QUIET_NAN = std::numeric_limits<float>::quiet_NaN(); + layer_state_t::matrix22_t matrix{QUIET_NAN, 0, 0, QUIET_NAN}; + layer->setMatrix(matrix); + layer->updateGeometry(); + layer->computeBounds(LARGE_FLOAT_RECT, ui::Transform(), 0.f); + + ASSERT_TRUE(layer->isHiddenByPolicy()); +} + +} // namespace +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp b/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp new file mode 100644 index 0000000000..5a2c1479bb --- /dev/null +++ b/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "LayerTestUtils.h" + +#include "mock/MockEventThread.h" + +namespace android { + +using testing::_; +using testing::Return; + +using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; + +sp<Layer> BufferStateLayerFactory::createLayer(TestableSurfaceFlinger& flinger) { + sp<Client> client; + LayerCreationArgs args(flinger.flinger(), client, "buffer-state-layer", LAYER_FLAGS, + LayerMetadata()); + return new BufferStateLayer(args); +} + +sp<Layer> EffectLayerFactory::createLayer(TestableSurfaceFlinger& flinger) { + sp<Client> client; + LayerCreationArgs args(flinger.flinger(), client, "color-layer", LAYER_FLAGS, LayerMetadata()); + return new EffectLayer(args); +} + +std::string PrintToStringParamName( + const ::testing::TestParamInfo<std::shared_ptr<LayerFactory>>& info) { + return info.param->name(); +} + +BaseLayerTest::BaseLayerTest() { + setupScheduler(); +} + +void BaseLayerTest::setupScheduler() { + auto eventThread = std::make_unique<mock::EventThread>(); + auto sfEventThread = std::make_unique<mock::EventThread>(); + + EXPECT_CALL(*eventThread, registerDisplayEventConnection(_)); + EXPECT_CALL(*eventThread, createEventConnection(_, _)) + .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0, + ResyncCallback()))); + + EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_)); + EXPECT_CALL(*sfEventThread, createEventConnection(_, _)) + .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0, + ResyncCallback()))); + + auto vsyncController = std::make_unique<mock::VsyncController>(); + auto vsyncTracker = std::make_unique<mock::VSyncTracker>(); + + EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); + EXPECT_CALL(*vsyncTracker, currentPeriod()) + .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)); + EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); + mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker), + std::move(eventThread), std::move(sfEventThread), + TestableSurfaceFlinger::SchedulerCallbackImpl::kNoOp, + TestableSurfaceFlinger::kTwoDisplayModes); +} + +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/LayerTestUtils.h b/services/surfaceflinger/tests/unittests/LayerTestUtils.h new file mode 100644 index 0000000000..fc9b6a27aa --- /dev/null +++ b/services/surfaceflinger/tests/unittests/LayerTestUtils.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <memory> + +#include <gtest/gtest.h> + +// TODO(b/129481165): remove the #pragma below and fix conversion issues +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconversion" +#include "BufferStateLayer.h" +#include "EffectLayer.h" +#include "Layer.h" +// TODO(b/129481165): remove the #pragma below and fix conversion issues +#pragma clang diagnostic pop // ignored "-Wconversion" + +#include "TestableSurfaceFlinger.h" + +namespace android { + +class LayerFactory { +public: + virtual ~LayerFactory() = default; + + virtual std::string name() = 0; + virtual sp<Layer> createLayer(TestableSurfaceFlinger& flinger) = 0; + +protected: + static constexpr uint32_t WIDTH = 100; + static constexpr uint32_t HEIGHT = 100; + static constexpr uint32_t LAYER_FLAGS = 0; +}; + +class BufferStateLayerFactory : public LayerFactory { +public: + std::string name() override { return "BufferStateLayer"; } + sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override; +}; + +class EffectLayerFactory : public LayerFactory { +public: + std::string name() override { return "EffectLayer"; } + sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override; +}; + +std::string PrintToStringParamName( + const ::testing::TestParamInfo<std::shared_ptr<LayerFactory>>& info); + +class BaseLayerTest : public ::testing::TestWithParam<std::shared_ptr<LayerFactory>> { +protected: + BaseLayerTest(); + + void setupScheduler(); + + TestableSurfaceFlinger mFlinger; +}; + +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp index 2b69f13126..825f145ecd 100644 --- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp +++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp @@ -30,73 +30,29 @@ // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic pop // ignored "-Wconversion" #include "FpsOps.h" +#include "LayerTestUtils.h" #include "TestableSurfaceFlinger.h" #include "mock/DisplayHardware/MockComposer.h" -#include "mock/MockEventThread.h" #include "mock/MockVsyncController.h" namespace android { -using testing::_; using testing::DoAll; using testing::Mock; -using testing::Return; using testing::SetArgPointee; using android::Hwc2::IComposer; using android::Hwc2::IComposerClient; -using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; - using scheduler::LayerHistory; using FrameRate = Layer::FrameRate; using FrameRateCompatibility = Layer::FrameRateCompatibility; -class LayerFactory { -public: - virtual ~LayerFactory() = default; - - virtual std::string name() = 0; - virtual sp<Layer> createLayer(TestableSurfaceFlinger& flinger) = 0; - -protected: - static constexpr uint32_t WIDTH = 100; - static constexpr uint32_t HEIGHT = 100; - static constexpr uint32_t LAYER_FLAGS = 0; -}; - -class BufferStateLayerFactory : public LayerFactory { -public: - std::string name() override { return "BufferStateLayer"; } - sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override { - sp<Client> client; - LayerCreationArgs args(flinger.flinger(), client, "buffer-state-layer", LAYER_FLAGS, - LayerMetadata()); - return new BufferStateLayer(args); - } -}; - -class EffectLayerFactory : public LayerFactory { -public: - std::string name() override { return "EffectLayer"; } - sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override { - sp<Client> client; - LayerCreationArgs args(flinger.flinger(), client, "color-layer", LAYER_FLAGS, - LayerMetadata()); - return new EffectLayer(args); - } -}; - -std::string PrintToStringParamName( - const ::testing::TestParamInfo<std::shared_ptr<LayerFactory>>& info) { - return info.param->name(); -} - /** * This class tests the behaviour of Layer::SetFrameRate and Layer::GetFrameRate */ -class SetFrameRateTest : public ::testing::TestWithParam<std::shared_ptr<LayerFactory>> { +class SetFrameRateTest : public BaseLayerTest { protected: const FrameRate FRAME_RATE_VOTE1 = FrameRate(67_Hz, FrameRateCompatibility::Default); const FrameRate FRAME_RATE_VOTE2 = FrameRate(14_Hz, FrameRateCompatibility::ExactOrMultiple); @@ -106,14 +62,10 @@ protected: SetFrameRateTest(); - void setupScheduler(); - void addChild(sp<Layer> layer, sp<Layer> child); void removeChild(sp<Layer> layer, sp<Layer> child); void commitTransaction(); - TestableSurfaceFlinger mFlinger; - std::vector<sp<Layer>> mLayers; }; @@ -122,8 +74,6 @@ SetFrameRateTest::SetFrameRateTest() { ::testing::UnitTest::GetInstance()->current_test_info(); ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); - setupScheduler(); - mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>()); } @@ -142,33 +92,6 @@ void SetFrameRateTest::commitTransaction() { } } -void SetFrameRateTest::setupScheduler() { - auto eventThread = std::make_unique<mock::EventThread>(); - auto sfEventThread = std::make_unique<mock::EventThread>(); - - EXPECT_CALL(*eventThread, registerDisplayEventConnection(_)); - EXPECT_CALL(*eventThread, createEventConnection(_, _)) - .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0, - ResyncCallback()))); - - EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_)); - EXPECT_CALL(*sfEventThread, createEventConnection(_, _)) - .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0, - ResyncCallback()))); - - auto vsyncController = std::make_unique<mock::VsyncController>(); - auto vsyncTracker = std::make_unique<mock::VSyncTracker>(); - - EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); - EXPECT_CALL(*vsyncTracker, currentPeriod()) - .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)); - EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); - mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker), - std::move(eventThread), std::move(sfEventThread), - TestableSurfaceFlinger::SchedulerCallbackImpl::kNoOp, - TestableSurfaceFlinger::kTwoDisplayModes); -} - namespace { TEST_P(SetFrameRateTest, SetAndGet) { |