From 475c4131cf027ed64a53587db0bca6f36b35ecb2 Mon Sep 17 00:00:00 2001 From: Chavi Weingarten Date: Thu, 29 Feb 2024 15:56:15 +0000 Subject: Add types for native Choreographer to ensure ordering For now, added type CALLBACK_INPUT and CALLBACK_ANIMATION. The default callback type will be CALLBACK_ANIMATION for the public APIs. CALLBACK_INPUT will always run before CALLBACK_ANIMATION. Test: ChoreographerTest Bug: 324271765 Change-Id: I25c92b788bb83778d5b0608a47efe9756d42e32f --- libs/gui/Choreographer.cpp | 46 ++++++++++++------ libs/gui/include/gui/Choreographer.h | 10 +++- libs/gui/tests/Android.bp | 2 + libs/gui/tests/Choreographer_test.cpp | 88 +++++++++++++++++++++++++++++++++++ 4 files changed, 131 insertions(+), 15 deletions(-) create mode 100644 libs/gui/tests/Choreographer_test.cpp (limited to 'libs/gui') diff --git a/libs/gui/Choreographer.cpp b/libs/gui/Choreographer.cpp index 4518b67d4c..54290cd629 100644 --- a/libs/gui/Choreographer.cpp +++ b/libs/gui/Choreographer.cpp @@ -143,9 +143,9 @@ Choreographer::~Choreographer() { void Choreographer::postFrameCallbackDelayed(AChoreographer_frameCallback cb, AChoreographer_frameCallback64 cb64, AChoreographer_vsyncCallback vsyncCallback, void* data, - nsecs_t delay) { + nsecs_t delay, CallbackType callbackType) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - FrameCallback callback{cb, cb64, vsyncCallback, data, now + delay}; + FrameCallback callback{cb, cb64, vsyncCallback, data, now + delay, callbackType}; { std::lock_guard _l{mLock}; mFrameCallbacks.push(callback); @@ -285,18 +285,8 @@ void Choreographer::handleRefreshRateUpdates() { } } -void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t, - VsyncEventData vsyncEventData) { - std::vector callbacks{}; - { - std::lock_guard _l{mLock}; - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - while (!mFrameCallbacks.empty() && mFrameCallbacks.top().dueTime < now) { - callbacks.push_back(mFrameCallbacks.top()); - mFrameCallbacks.pop(); - } - } - mLastVsyncEventData = vsyncEventData; +void Choreographer::dispatchCallbacks(const std::vector& callbacks, + VsyncEventData vsyncEventData, nsecs_t timestamp) { for (const auto& cb : callbacks) { if (cb.vsyncCallback != nullptr) { ATRACE_FORMAT("AChoreographer_vsyncCallback %" PRId64, @@ -319,6 +309,34 @@ void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t } } +void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t, + VsyncEventData vsyncEventData) { + std::vector animationCallbacks{}; + std::vector inputCallbacks{}; + { + std::lock_guard _l{mLock}; + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + while (!mFrameCallbacks.empty() && mFrameCallbacks.top().dueTime < now) { + if (mFrameCallbacks.top().callbackType == CALLBACK_INPUT) { + inputCallbacks.push_back(mFrameCallbacks.top()); + } else { + animationCallbacks.push_back(mFrameCallbacks.top()); + } + mFrameCallbacks.pop(); + } + } + mLastVsyncEventData = vsyncEventData; + // Callbacks with type CALLBACK_INPUT should always run first + { + ATRACE_FORMAT("CALLBACK_INPUT"); + dispatchCallbacks(inputCallbacks, vsyncEventData, timestamp); + } + { + ATRACE_FORMAT("CALLBACK_ANIMATION"); + dispatchCallbacks(animationCallbacks, vsyncEventData, timestamp); + } +} + void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) { ALOGV("choreographer %p ~ received hotplug event (displayId=%s, connected=%s), ignoring.", this, to_string(displayId).c_str(), toString(connected)); diff --git a/libs/gui/include/gui/Choreographer.h b/libs/gui/include/gui/Choreographer.h index 55a7aa7ddc..fc79b03c23 100644 --- a/libs/gui/include/gui/Choreographer.h +++ b/libs/gui/include/gui/Choreographer.h @@ -28,12 +28,18 @@ namespace android { using gui::VsyncEventData; +enum CallbackType : int8_t { + CALLBACK_INPUT, + CALLBACK_ANIMATION, +}; + struct FrameCallback { AChoreographer_frameCallback callback; AChoreographer_frameCallback64 callback64; AChoreographer_vsyncCallback vsyncCallback; void* data; nsecs_t dueTime; + CallbackType callbackType; inline bool operator<(const FrameCallback& rhs) const { // Note that this is intentionally flipped because we want callbacks due sooner to be at @@ -78,7 +84,7 @@ public: void postFrameCallbackDelayed(AChoreographer_frameCallback cb, AChoreographer_frameCallback64 cb64, AChoreographer_vsyncCallback vsyncCallback, void* data, - nsecs_t delay); + nsecs_t delay, CallbackType callbackType); void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data) EXCLUDES(gChoreographers.lock); void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data); @@ -109,6 +115,8 @@ private: void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count, VsyncEventData vsyncEventData) override; + void dispatchCallbacks(const std::vector&, VsyncEventData vsyncEventData, + nsecs_t timestamp); void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override; void dispatchHotplugConnectionError(nsecs_t timestamp, int32_t connectionError) override; void dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId, diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp index e606b9941e..0f16f714dc 100644 --- a/libs/gui/tests/Android.bp +++ b/libs/gui/tests/Android.bp @@ -30,6 +30,7 @@ cc_test { "BLASTBufferQueue_test.cpp", "BufferItemConsumer_test.cpp", "BufferQueue_test.cpp", + "Choreographer_test.cpp", "CompositorTiming_test.cpp", "CpuConsumer_test.cpp", "EndToEndNativeInputTest.cpp", @@ -61,6 +62,7 @@ cc_test { "libSurfaceFlingerProp", "libGLESv1_CM", "libinput", + "libnativedisplay", ], static_libs: [ diff --git a/libs/gui/tests/Choreographer_test.cpp b/libs/gui/tests/Choreographer_test.cpp new file mode 100644 index 0000000000..2ac2550f07 --- /dev/null +++ b/libs/gui/tests/Choreographer_test.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "Choreographer_test" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace android { +class ChoreographerTest : public ::testing::Test {}; + +struct VsyncCallback { + std::atomic completePromise{false}; + std::chrono::nanoseconds frameTime{0LL}; + std::chrono::nanoseconds receivedCallbackTime{0LL}; + + void onVsyncCallback(const AChoreographerFrameCallbackData* callbackData) { + frameTime = std::chrono::nanoseconds{ + AChoreographerFrameCallbackData_getFrameTimeNanos(callbackData)}; + receivedCallbackTime = std::chrono::nanoseconds{systemTime(SYSTEM_TIME_MONOTONIC)}; + completePromise.store(true); + } + + bool callbackReceived() { return completePromise.load(); } +}; + +static void vsyncCallback(const AChoreographerFrameCallbackData* callbackData, void* data) { + VsyncCallback* cb = static_cast(data); + cb->onVsyncCallback(callbackData); +} + +TEST_F(ChoreographerTest, InputCallbackBeforeAnimation) { + sp looper = Looper::prepare(0); + Choreographer* choreographer = Choreographer::getForThread(); + VsyncCallback animationCb; + VsyncCallback inputCb; + + choreographer->postFrameCallbackDelayed(nullptr, nullptr, vsyncCallback, &animationCb, 0, + CALLBACK_ANIMATION); + choreographer->postFrameCallbackDelayed(nullptr, nullptr, vsyncCallback, &inputCb, 0, + CALLBACK_INPUT); + + nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t currTime; + int pollResult; + do { + pollResult = looper->pollOnce(16); + currTime = systemTime(SYSTEM_TIME_MONOTONIC); + } while (!(inputCb.callbackReceived() && animationCb.callbackReceived()) && + (pollResult != Looper::POLL_TIMEOUT && pollResult != Looper::POLL_ERROR) && + (currTime - startTime < 3000)); + + ASSERT_TRUE(inputCb.callbackReceived()) << "did not receive input callback"; + ASSERT_TRUE(animationCb.callbackReceived()) << "did not receive animation callback"; + + ASSERT_EQ(inputCb.frameTime, animationCb.frameTime) + << android::base::StringPrintf("input and animation callback frame times don't match. " + "inputFrameTime=%lld animationFrameTime=%lld", + inputCb.frameTime.count(), + animationCb.frameTime.count()); + + ASSERT_LT(inputCb.receivedCallbackTime, animationCb.receivedCallbackTime) + << android::base::StringPrintf("input callback was not called first. " + "inputCallbackTime=%lld animationCallbackTime=%lld", + inputCb.frameTime.count(), + animationCb.frameTime.count()); +} + +} // namespace android \ No newline at end of file -- cgit v1.2.3-59-g8ed1b From 5e5645e860048bcdfef5db01b936e73357174caf Mon Sep 17 00:00:00 2001 From: Linnan Li Date: Tue, 5 Mar 2024 14:43:05 +0000 Subject: Move occlusion detection into logical space Same as our "hit test" to find the target window, since WM works in logical space, we consider the window's frame to be (l, t, r, b), and the portion of the window that can be occluded in the x-direction is [l, r), and the portion of the window that can be occluded in the y-direction is [t, b). When the logical space is different from the physical space, the opening and closing intervals in each of these directions will be inconsistent, leading to abnormal detection of the edge part. Here we move the occlusion detection to the logical space as well, consistent with the "hit test" for finding the target window. Bug: 327712879 Test: atest inputflinger_tests Test: Create a window that can cause occlusion and perform clicks on the edges, making sure that the occlusion range is x = [l, r), y = [t, b) under each direction of the screen. Signed-off-by: Linnan Li (cherry picked from https://partner-android-review.googlesource.com/q/commit:1a0060e9bcd25bee6ce7a9f5d01b9446b0c4d73b) Merged-In: I19b739bc8f1e48d8bc70020a2b07da227eaa6d8b Change-Id: I19b739bc8f1e48d8bc70020a2b07da227eaa6d8b --- libs/gui/WindowInfo.cpp | 8 -- libs/gui/include/gui/WindowInfo.h | 4 - .../inputflinger/dispatcher/InputDispatcher.cpp | 21 +++++- services/inputflinger/dispatcher/InputDispatcher.h | 4 +- .../inputflinger/tests/InputDispatcher_test.cpp | 88 ++++++++++++++++++++++ 5 files changed, 107 insertions(+), 18 deletions(-) (limited to 'libs/gui') diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp index 86bf0ee745..ad0d99d11e 100644 --- a/libs/gui/WindowInfo.cpp +++ b/libs/gui/WindowInfo.cpp @@ -73,14 +73,6 @@ void WindowInfo::addTouchableRegion(const Rect& region) { touchableRegion.orSelf(region); } -bool WindowInfo::touchableRegionContainsPoint(int32_t x, int32_t y) const { - return touchableRegion.contains(x, y); -} - -bool WindowInfo::frameContainsPoint(int32_t x, int32_t y) const { - return x >= frame.left && x < frame.right && y >= frame.top && y < frame.bottom; -} - bool WindowInfo::supportsSplitTouch() const { return !inputConfig.test(InputConfig::PREVENT_SPLITTING); } diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h index 32d60be612..2d1b51a418 100644 --- a/libs/gui/include/gui/WindowInfo.h +++ b/libs/gui/include/gui/WindowInfo.h @@ -254,10 +254,6 @@ struct WindowInfo : public Parcelable { void addTouchableRegion(const Rect& region); - bool touchableRegionContainsPoint(int32_t x, int32_t y) const; - - bool frameContainsPoint(int32_t x, int32_t y) const; - bool supportsSplitTouch() const; bool isSpy() const; diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 7f54bf1945..55976137c5 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -573,6 +573,18 @@ bool windowAcceptsTouchAt(const WindowInfo& windowInfo, int32_t displayId, float return true; } +// Returns true if the given window's frame can occlude pointer events at the given display +// location. +bool windowOccludesTouchAt(const WindowInfo& windowInfo, int displayId, float x, float y, + const ui::Transform& displayTransform) { + if (windowInfo.displayId != displayId) { + return false; + } + const auto frame = displayTransform.transform(windowInfo.frame); + const auto p = floor(displayTransform.transform(x, y)); + return p.x >= frame.left && p.x < frame.right && p.y >= frame.top && p.y < frame.bottom; +} + bool isPointerFromStylus(const MotionEntry& entry, int32_t pointerIndex) { return isFromSource(entry.source, AINPUT_SOURCE_STYLUS) && isStylusToolType(entry.pointerProperties[pointerIndex].toolType); @@ -3056,7 +3068,7 @@ static bool canBeObscuredBy(const sp& windowHandle, * If neither of those is true, then it means the touch can be allowed. */ InputDispatcher::TouchOcclusionInfo InputDispatcher::computeTouchOcclusionInfoLocked( - const sp& windowHandle, int32_t x, int32_t y) const { + const sp& windowHandle, float x, float y) const { const WindowInfo* windowInfo = windowHandle->getInfo(); int32_t displayId = windowInfo->displayId; const std::vector>& windowHandles = getWindowHandlesLocked(displayId); @@ -3070,7 +3082,8 @@ InputDispatcher::TouchOcclusionInfo InputDispatcher::computeTouchOcclusionInfoLo break; // All future windows are below us. Exit early. } const WindowInfo* otherInfo = otherHandle->getInfo(); - if (canBeObscuredBy(windowHandle, otherHandle) && otherInfo->frameContainsPoint(x, y) && + if (canBeObscuredBy(windowHandle, otherHandle) && + windowOccludesTouchAt(*otherInfo, displayId, x, y, getTransformLocked(displayId)) && !haveSameApplicationToken(windowInfo, otherInfo)) { if (DEBUG_TOUCH_OCCLUSION) { info.debugInfo.push_back( @@ -3140,7 +3153,7 @@ bool InputDispatcher::isTouchTrustedLocked(const TouchOcclusionInfo& occlusionIn } bool InputDispatcher::isWindowObscuredAtPointLocked(const sp& windowHandle, - int32_t x, int32_t y) const { + float x, float y) const { int32_t displayId = windowHandle->getInfo()->displayId; const std::vector>& windowHandles = getWindowHandlesLocked(displayId); for (const sp& otherHandle : windowHandles) { @@ -3149,7 +3162,7 @@ bool InputDispatcher::isWindowObscuredAtPointLocked(const sp& } const WindowInfo* otherInfo = otherHandle->getInfo(); if (canBeObscuredBy(windowHandle, otherHandle) && - otherInfo->frameContainsPoint(x, y)) { + windowOccludesTouchAt(*otherInfo, displayId, x, y, getTransformLocked(displayId))) { return true; } } diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 269bfddb8c..9c8d5889d1 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -565,11 +565,11 @@ private: }; TouchOcclusionInfo computeTouchOcclusionInfoLocked( - const sp& windowHandle, int32_t x, int32_t y) const + const sp& windowHandle, float x, float y) const REQUIRES(mLock); bool isTouchTrustedLocked(const TouchOcclusionInfo& occlusionInfo) const REQUIRES(mLock); bool isWindowObscuredAtPointLocked(const sp& windowHandle, - int32_t x, int32_t y) const REQUIRES(mLock); + float x, float y) const REQUIRES(mLock); bool isWindowObscuredLocked(const sp& windowHandle) const REQUIRES(mLock); std::string dumpWindowForTouchOcclusion(const android::gui::WindowInfo* info, diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index f0f4d93ecd..621d9eadb8 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -5361,6 +5361,94 @@ TEST_P(InputDispatcherDisplayOrientationFixture, HitTestInDifferentOrientations) window->assertNoEvents(); } +// This test verifies the occlusion detection for all rotations of the display by tapping +// in different locations on the display, specifically points close to the four corners of a +// window. +TEST_P(InputDispatcherDisplayOrientationFixture, BlockUntrustClickInDifferentOrientations) { + constexpr static int32_t displayWidth = 400; + constexpr static int32_t displayHeight = 800; + + std::shared_ptr untrustedWindowApplication = + std::make_shared(); + std::shared_ptr application = std::make_shared(); + + const auto rotation = GetParam(); + + // Set up the display with the specified rotation. + const bool isRotated = rotation == ui::ROTATION_90 || rotation == ui::ROTATION_270; + const int32_t logicalDisplayWidth = isRotated ? displayHeight : displayWidth; + const int32_t logicalDisplayHeight = isRotated ? displayWidth : displayHeight; + const ui::Transform displayTransform(ui::Transform::toRotationFlags(rotation), + logicalDisplayWidth, logicalDisplayHeight); + addDisplayInfo(ADISPLAY_ID_DEFAULT, displayTransform); + + // Create a window that not trusted. + const Rect untrustedWindowFrameInLogicalDisplay(100, 100, 200, 300); + + const Rect untrustedWindowFrameInDisplay = + displayTransform.inverse().transform(untrustedWindowFrameInLogicalDisplay); + + sp untrustedWindow = + sp::make(untrustedWindowApplication, mDispatcher, "UntrustedWindow", + ADISPLAY_ID_DEFAULT); + untrustedWindow->setFrame(untrustedWindowFrameInDisplay, displayTransform); + untrustedWindow->setTrustedOverlay(false); + untrustedWindow->setTouchOcclusionMode(TouchOcclusionMode::BLOCK_UNTRUSTED); + untrustedWindow->setTouchable(false); + untrustedWindow->setAlpha(1.0f); + untrustedWindow->setOwnerInfo(gui::Pid{1}, gui::Uid{101}); + addWindow(untrustedWindow); + + // Create a simple app window below the untrusted window. + const Rect simpleAppWindowFrameInLogicalDisplay(0, 0, 300, 600); + const Rect simpleAppWindowFrameInDisplay = + displayTransform.inverse().transform(simpleAppWindowFrameInLogicalDisplay); + + sp simpleAppWindow = + sp::make(application, mDispatcher, "SimpleAppWindow", + ADISPLAY_ID_DEFAULT); + simpleAppWindow->setFrame(simpleAppWindowFrameInDisplay, displayTransform); + simpleAppWindow->setOwnerInfo(gui::Pid{2}, gui::Uid{202}); + addWindow(simpleAppWindow); + + // The following points in logical display space should be inside the untrusted window, so + // the simple window could not receive events that coordinate is these point. + static const std::array untrustedPoints{ + {{100, 100}, {199.99, 100}, {100, 299.99}, {199.99, 299.99}}}; + + for (const auto untrustedPoint : untrustedPoints) { + const vec2 p = displayTransform.inverse().transform(untrustedPoint); + const PointF pointInDisplaySpace{p.x, p.y}; + mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, + AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {pointInDisplaySpace})); + mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, + AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {pointInDisplaySpace})); + } + untrustedWindow->assertNoEvents(); + simpleAppWindow->assertNoEvents(); + // The following points in logical display space should be outside the untrusted window, so + // the simple window should receive events that coordinate is these point. + static const std::array trustedPoints{ + {{200, 100}, {100, 300}, {200, 300}, {100, 99.99}, {99.99, 100}}}; + for (const auto trustedPoint : trustedPoints) { + const vec2 p = displayTransform.inverse().transform(trustedPoint); + const PointF pointInDisplaySpace{p.x, p.y}; + mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, + AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {pointInDisplaySpace})); + simpleAppWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, + AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED); + mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, + AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {pointInDisplaySpace})); + simpleAppWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, + AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED); + } + untrustedWindow->assertNoEvents(); +} + // Run the precision tests for all rotations. INSTANTIATE_TEST_SUITE_P(InputDispatcherDisplayOrientationTests, InputDispatcherDisplayOrientationFixture, -- cgit v1.2.3-59-g8ed1b From a6f572f92437f5ace0b99df16af1f49cad8b260b Mon Sep 17 00:00:00 2001 From: Chavi Weingarten Date: Thu, 29 Feb 2024 16:28:21 +0000 Subject: Add native InputTransferToken Added a native class that corresponds to the Java InputTransferToken. Test: SurfaceControlInputReceiverTests Test: AInputTransferTokenTest Bug: 324271765 Change-Id: Ida2a7b34338560dfed9af7f510d64372e41384af --- include/android/input_transfer_token_jni.h | 68 ++++++++++++++++++++++++++++++ libs/gui/include/gui/InputTransferToken.h | 53 +++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 include/android/input_transfer_token_jni.h create mode 100644 libs/gui/include/gui/InputTransferToken.h (limited to 'libs/gui') diff --git a/include/android/input_transfer_token_jni.h b/include/android/input_transfer_token_jni.h new file mode 100644 index 0000000000..ba5f6f2bdd --- /dev/null +++ b/include/android/input_transfer_token_jni.h @@ -0,0 +1,68 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @addtogroup NativeActivity Native Activity + * @{ + */ +/** + * @file input_transfer_token_jni.h + */ +#ifndef ANDROID_INPUT_TRANSFER_TOKEN_JNI_H +#define ANDROID_INPUT_TRANSFER_TOKEN_JNI_H +#include +#include + +__BEGIN_DECLS +struct AInputTransferToken; + +/** + * AInputTransferToken can be used to request focus on or to transfer touch gesture to and from + * an embedded SurfaceControl + */ +typedef struct AInputTransferToken AInputTransferToken; + +/** + * Return the AInputTransferToken wrapped by a Java InputTransferToken object. This must be released + * using AInputTransferToken_release + * + * inputTransferTokenObj must be a non-null instance of android.window.InputTransferToken. + * + * Available since API level 35. + */ +AInputTransferToken* _Nonnull AInputTransferToken_fromJava(JNIEnv* _Nonnull env, + jobject _Nonnull inputTransferTokenObj) __INTRODUCED_IN(__ANDROID_API_V__); +/** + * Return the Java InputTransferToken object that wraps AInputTransferToken + * + * aInputTransferToken must be non null and the returned value is an object of instance + * android.window.InputTransferToken. + * + * Available since API level 35. + */ +jobject _Nonnull AInputTransferToken_toJava(JNIEnv* _Nonnull env, + const AInputTransferToken* _Nonnull aInputTransferToken) __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Removes a reference that was previously acquired in native. + * + * Available since API level 35. + */ +void AInputTransferToken_release(AInputTransferToken* _Nonnull aInputTransferToken) + __INTRODUCED_IN(__ANDROID_API_V__); + +__END_DECLS +#endif // ANDROID_INPUT_TRANSFER_TOKEN_JNI_H +/** @} */ diff --git a/libs/gui/include/gui/InputTransferToken.h b/libs/gui/include/gui/InputTransferToken.h new file mode 100644 index 0000000000..6530b5069a --- /dev/null +++ b/libs/gui/include/gui/InputTransferToken.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace android { +struct InputTransferToken : public RefBase, Parcelable { +public: + InputTransferToken() { mToken = new BBinder(); } + + InputTransferToken(const sp& token) { mToken = token; } + + status_t writeToParcel(Parcel* parcel) const override { + SAFE_PARCEL(parcel->writeStrongBinder, mToken); + return NO_ERROR; + } + + status_t readFromParcel(const Parcel* parcel) { + SAFE_PARCEL(parcel->readStrongBinder, &mToken); + return NO_ERROR; + }; + + sp mToken; +}; + +static inline bool operator==(const sp& token1, + const sp& token2) { + if (token1.get() == token2.get()) { + return true; + } + return token1->mToken == token2->mToken; +} + +} // namespace android \ No newline at end of file -- cgit v1.2.3-59-g8ed1b From 0438ca8a922661b3a870f61ab6fcb5fab4e5ffb8 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Tue, 12 Mar 2024 14:27:25 -0700 Subject: Move InputConsumer into separate files This helps to keep track of things, and it also allows easier deletion in the future. Bug: 311142655 Test: none Change-Id: Iece2bf6857ab05f86072031a4cf3a36f843b1634 --- include/input/InputConsumer.h | 249 ++++++ include/input/InputTransport.h | 225 ----- libs/gui/tests/EndToEndNativeInputTest.cpp | 1 + libs/input/Android.bp | 1 + libs/input/InputConsumer.cpp | 939 +++++++++++++++++++++ libs/input/InputTransport.cpp | 883 +------------------ .../input/tests/InputPublisherAndConsumer_test.cpp | 1 + libs/input/tests/TouchResampling_test.cpp | 1 + services/inputflinger/tests/FakeWindowHandle.h | 1 + .../inputflinger/tests/InputDispatcher_test.cpp | 1 + 10 files changed, 1195 insertions(+), 1107 deletions(-) create mode 100644 include/input/InputConsumer.h create mode 100644 libs/input/InputConsumer.cpp (limited to 'libs/gui') diff --git a/include/input/InputConsumer.h b/include/input/InputConsumer.h new file mode 100644 index 0000000000..560e804c68 --- /dev/null +++ b/include/input/InputConsumer.h @@ -0,0 +1,249 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +/* + * Native input transport. + * + * The InputConsumer is used by the application to receive events from the input dispatcher. + */ + +#include "InputTransport.h" + +namespace android { + +/* + * Consumes input events from an input channel. + */ +class InputConsumer { +public: + /* Create a consumer associated with an input channel. */ + explicit InputConsumer(const std::shared_ptr& channel); + /* Create a consumer associated with an input channel, override resampling system property */ + explicit InputConsumer(const std::shared_ptr& channel, + bool enableTouchResampling); + + /* Destroys the consumer and releases its input channel. */ + ~InputConsumer(); + + /* Gets the underlying input channel. */ + inline std::shared_ptr getChannel() { return mChannel; } + + /* Consumes an input event from the input channel and copies its contents into + * an InputEvent object created using the specified factory. + * + * Tries to combine a series of move events into larger batches whenever possible. + * + * If consumeBatches is false, then defers consuming pending batched events if it + * is possible for additional samples to be added to them later. Call hasPendingBatch() + * to determine whether a pending batch is available to be consumed. + * + * If consumeBatches is true, then events are still batched but they are consumed + * immediately as soon as the input channel is exhausted. + * + * The frameTime parameter specifies the time when the current display frame started + * rendering in the CLOCK_MONOTONIC time base, or -1 if unknown. + * + * The returned sequence number is never 0 unless the operation failed. + * + * Returns OK on success. + * Returns WOULD_BLOCK if there is no event present. + * Returns DEAD_OBJECT if the channel's peer has been closed. + * Returns NO_MEMORY if the event could not be created. + * Other errors probably indicate that the channel is broken. + */ + status_t consume(InputEventFactoryInterface* factory, bool consumeBatches, nsecs_t frameTime, + uint32_t* outSeq, InputEvent** outEvent); + + /* Sends a finished signal to the publisher to inform it that the message + * with the specified sequence number has finished being process and whether + * the message was handled by the consumer. + * + * Returns OK on success. + * Returns BAD_VALUE if seq is 0. + * Other errors probably indicate that the channel is broken. + */ + status_t sendFinishedSignal(uint32_t seq, bool handled); + + status_t sendTimeline(int32_t inputEventId, + std::array timeline); + + /* Returns true if there is a pending batch. + * + * Should be called after calling consume() with consumeBatches == false to determine + * whether consume() should be called again later on with consumeBatches == true. + */ + bool hasPendingBatch() const; + + /* Returns the source of first pending batch if exist. + * + * Should be called after calling consume() with consumeBatches == false to determine + * whether consume() should be called again later on with consumeBatches == true. + */ + int32_t getPendingBatchSource() const; + + /* Returns true when there is *likely* a pending batch or a pending event in the channel. + * + * This is only a performance hint and may return false negative results. Clients should not + * rely on availability of the message based on the return value. + */ + bool probablyHasInput() const; + + std::string dump() const; + +private: + // True if touch resampling is enabled. + const bool mResampleTouch; + + std::shared_ptr mChannel; + + // The current input message. + InputMessage mMsg; + + // True if mMsg contains a valid input message that was deferred from the previous + // call to consume and that still needs to be handled. + bool mMsgDeferred; + + // Batched motion events per device and source. + struct Batch { + std::vector samples; + }; + std::vector mBatches; + + // Touch state per device and source, only for sources of class pointer. + struct History { + nsecs_t eventTime; + BitSet32 idBits; + int32_t idToIndex[MAX_POINTER_ID + 1]; + PointerCoords pointers[MAX_POINTERS]; + + void initializeFrom(const InputMessage& msg) { + eventTime = msg.body.motion.eventTime; + idBits.clear(); + for (uint32_t i = 0; i < msg.body.motion.pointerCount; i++) { + uint32_t id = msg.body.motion.pointers[i].properties.id; + idBits.markBit(id); + idToIndex[id] = i; + pointers[i].copyFrom(msg.body.motion.pointers[i].coords); + } + } + + void initializeFrom(const History& other) { + eventTime = other.eventTime; + idBits = other.idBits; // temporary copy + for (size_t i = 0; i < other.idBits.count(); i++) { + uint32_t id = idBits.clearFirstMarkedBit(); + int32_t index = other.idToIndex[id]; + idToIndex[id] = index; + pointers[index].copyFrom(other.pointers[index]); + } + idBits = other.idBits; // final copy + } + + const PointerCoords& getPointerById(uint32_t id) const { return pointers[idToIndex[id]]; } + + bool hasPointerId(uint32_t id) const { return idBits.hasBit(id); } + }; + struct TouchState { + int32_t deviceId; + int32_t source; + size_t historyCurrent; + size_t historySize; + History history[2]; + History lastResample; + + void initialize(int32_t incomingDeviceId, int32_t incomingSource) { + deviceId = incomingDeviceId; + source = incomingSource; + historyCurrent = 0; + historySize = 0; + lastResample.eventTime = 0; + lastResample.idBits.clear(); + } + + void addHistory(const InputMessage& msg) { + historyCurrent ^= 1; + if (historySize < 2) { + historySize += 1; + } + history[historyCurrent].initializeFrom(msg); + } + + const History* getHistory(size_t index) const { + return &history[(historyCurrent + index) & 1]; + } + + bool recentCoordinatesAreIdentical(uint32_t id) const { + // Return true if the two most recently received "raw" coordinates are identical + if (historySize < 2) { + return false; + } + if (!getHistory(0)->hasPointerId(id) || !getHistory(1)->hasPointerId(id)) { + return false; + } + float currentX = getHistory(0)->getPointerById(id).getX(); + float currentY = getHistory(0)->getPointerById(id).getY(); + float previousX = getHistory(1)->getPointerById(id).getX(); + float previousY = getHistory(1)->getPointerById(id).getY(); + if (currentX == previousX && currentY == previousY) { + return true; + } + return false; + } + }; + std::vector mTouchStates; + + // Chain of batched sequence numbers. When multiple input messages are combined into + // a batch, we append a record here that associates the last sequence number in the + // batch with the previous one. When the finished signal is sent, we traverse the + // chain to individually finish all input messages that were part of the batch. + struct SeqChain { + uint32_t seq; // sequence number of batched input message + uint32_t chain; // sequence number of previous batched input message + }; + std::vector mSeqChains; + + // The time at which each event with the sequence number 'seq' was consumed. + // This data is provided in 'finishInputEvent' so that the receiving end can measure the latency + // This collection is populated when the event is received, and the entries are erased when the + // events are finished. It should not grow infinitely because if an event is not ack'd, ANR + // will be raised for that connection, and no further events will be posted to that channel. + std::unordered_map mConsumeTimes; + + status_t consumeBatch(InputEventFactoryInterface* factory, nsecs_t frameTime, uint32_t* outSeq, + InputEvent** outEvent); + status_t consumeSamples(InputEventFactoryInterface* factory, Batch& batch, size_t count, + uint32_t* outSeq, InputEvent** outEvent); + + void updateTouchState(InputMessage& msg); + void resampleTouchState(nsecs_t frameTime, MotionEvent* event, const InputMessage* next); + + ssize_t findBatch(int32_t deviceId, int32_t source) const; + ssize_t findTouchState(int32_t deviceId, int32_t source) const; + + nsecs_t getConsumeTime(uint32_t seq) const; + void popConsumeTime(uint32_t seq); + status_t sendUnchainedFinishedSignal(uint32_t seq, bool handled); + + static void rewriteMessage(TouchState& state, InputMessage& msg); + static bool canAddSample(const Batch& batch, const InputMessage* msg); + static ssize_t findSampleNoLaterThan(const Batch& batch, nsecs_t time); + + static bool isTouchResamplingEnabled(); +}; + +} // namespace android diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index aca4b622d1..5f9c8f5a5c 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -451,229 +451,4 @@ private: InputVerifier mInputVerifier; }; -/* - * Consumes input events from an input channel. - */ -class InputConsumer { -public: - /* Create a consumer associated with an input channel. */ - explicit InputConsumer(const std::shared_ptr& channel); - /* Create a consumer associated with an input channel, override resampling system property */ - explicit InputConsumer(const std::shared_ptr& channel, - bool enableTouchResampling); - - /* Destroys the consumer and releases its input channel. */ - ~InputConsumer(); - - /* Gets the underlying input channel. */ - inline std::shared_ptr getChannel() { return mChannel; } - - /* Consumes an input event from the input channel and copies its contents into - * an InputEvent object created using the specified factory. - * - * Tries to combine a series of move events into larger batches whenever possible. - * - * If consumeBatches is false, then defers consuming pending batched events if it - * is possible for additional samples to be added to them later. Call hasPendingBatch() - * to determine whether a pending batch is available to be consumed. - * - * If consumeBatches is true, then events are still batched but they are consumed - * immediately as soon as the input channel is exhausted. - * - * The frameTime parameter specifies the time when the current display frame started - * rendering in the CLOCK_MONOTONIC time base, or -1 if unknown. - * - * The returned sequence number is never 0 unless the operation failed. - * - * Returns OK on success. - * Returns WOULD_BLOCK if there is no event present. - * Returns DEAD_OBJECT if the channel's peer has been closed. - * Returns NO_MEMORY if the event could not be created. - * Other errors probably indicate that the channel is broken. - */ - status_t consume(InputEventFactoryInterface* factory, bool consumeBatches, nsecs_t frameTime, - uint32_t* outSeq, InputEvent** outEvent); - - /* Sends a finished signal to the publisher to inform it that the message - * with the specified sequence number has finished being process and whether - * the message was handled by the consumer. - * - * Returns OK on success. - * Returns BAD_VALUE if seq is 0. - * Other errors probably indicate that the channel is broken. - */ - status_t sendFinishedSignal(uint32_t seq, bool handled); - - status_t sendTimeline(int32_t inputEventId, - std::array timeline); - - /* Returns true if there is a pending batch. - * - * Should be called after calling consume() with consumeBatches == false to determine - * whether consume() should be called again later on with consumeBatches == true. - */ - bool hasPendingBatch() const; - - /* Returns the source of first pending batch if exist. - * - * Should be called after calling consume() with consumeBatches == false to determine - * whether consume() should be called again later on with consumeBatches == true. - */ - int32_t getPendingBatchSource() const; - - /* Returns true when there is *likely* a pending batch or a pending event in the channel. - * - * This is only a performance hint and may return false negative results. Clients should not - * rely on availability of the message based on the return value. - */ - bool probablyHasInput() const; - - std::string dump() const; - -private: - // True if touch resampling is enabled. - const bool mResampleTouch; - - std::shared_ptr mChannel; - - // The current input message. - InputMessage mMsg; - - // True if mMsg contains a valid input message that was deferred from the previous - // call to consume and that still needs to be handled. - bool mMsgDeferred; - - // Batched motion events per device and source. - struct Batch { - std::vector samples; - }; - std::vector mBatches; - - // Touch state per device and source, only for sources of class pointer. - struct History { - nsecs_t eventTime; - BitSet32 idBits; - int32_t idToIndex[MAX_POINTER_ID + 1]; - PointerCoords pointers[MAX_POINTERS]; - - void initializeFrom(const InputMessage& msg) { - eventTime = msg.body.motion.eventTime; - idBits.clear(); - for (uint32_t i = 0; i < msg.body.motion.pointerCount; i++) { - uint32_t id = msg.body.motion.pointers[i].properties.id; - idBits.markBit(id); - idToIndex[id] = i; - pointers[i].copyFrom(msg.body.motion.pointers[i].coords); - } - } - - void initializeFrom(const History& other) { - eventTime = other.eventTime; - idBits = other.idBits; // temporary copy - for (size_t i = 0; i < other.idBits.count(); i++) { - uint32_t id = idBits.clearFirstMarkedBit(); - int32_t index = other.idToIndex[id]; - idToIndex[id] = index; - pointers[index].copyFrom(other.pointers[index]); - } - idBits = other.idBits; // final copy - } - - const PointerCoords& getPointerById(uint32_t id) const { - return pointers[idToIndex[id]]; - } - - bool hasPointerId(uint32_t id) const { - return idBits.hasBit(id); - } - }; - struct TouchState { - int32_t deviceId; - int32_t source; - size_t historyCurrent; - size_t historySize; - History history[2]; - History lastResample; - - void initialize(int32_t deviceId, int32_t source) { - this->deviceId = deviceId; - this->source = source; - historyCurrent = 0; - historySize = 0; - lastResample.eventTime = 0; - lastResample.idBits.clear(); - } - - void addHistory(const InputMessage& msg) { - historyCurrent ^= 1; - if (historySize < 2) { - historySize += 1; - } - history[historyCurrent].initializeFrom(msg); - } - - const History* getHistory(size_t index) const { - return &history[(historyCurrent + index) & 1]; - } - - bool recentCoordinatesAreIdentical(uint32_t id) const { - // Return true if the two most recently received "raw" coordinates are identical - if (historySize < 2) { - return false; - } - if (!getHistory(0)->hasPointerId(id) || !getHistory(1)->hasPointerId(id)) { - return false; - } - float currentX = getHistory(0)->getPointerById(id).getX(); - float currentY = getHistory(0)->getPointerById(id).getY(); - float previousX = getHistory(1)->getPointerById(id).getX(); - float previousY = getHistory(1)->getPointerById(id).getY(); - if (currentX == previousX && currentY == previousY) { - return true; - } - return false; - } - }; - std::vector mTouchStates; - - // Chain of batched sequence numbers. When multiple input messages are combined into - // a batch, we append a record here that associates the last sequence number in the - // batch with the previous one. When the finished signal is sent, we traverse the - // chain to individually finish all input messages that were part of the batch. - struct SeqChain { - uint32_t seq; // sequence number of batched input message - uint32_t chain; // sequence number of previous batched input message - }; - std::vector mSeqChains; - - // The time at which each event with the sequence number 'seq' was consumed. - // This data is provided in 'finishInputEvent' so that the receiving end can measure the latency - // This collection is populated when the event is received, and the entries are erased when the - // events are finished. It should not grow infinitely because if an event is not ack'd, ANR - // will be raised for that connection, and no further events will be posted to that channel. - std::unordered_map mConsumeTimes; - - status_t consumeBatch(InputEventFactoryInterface* factory, - nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent); - status_t consumeSamples(InputEventFactoryInterface* factory, - Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent); - - void updateTouchState(InputMessage& msg); - void resampleTouchState(nsecs_t frameTime, MotionEvent* event, - const InputMessage *next); - - ssize_t findBatch(int32_t deviceId, int32_t source) const; - ssize_t findTouchState(int32_t deviceId, int32_t source) const; - - nsecs_t getConsumeTime(uint32_t seq) const; - void popConsumeTime(uint32_t seq); - status_t sendUnchainedFinishedSignal(uint32_t seq, bool handled); - - static void rewriteMessage(TouchState& state, InputMessage& msg); - static bool canAddSample(const Batch& batch, const InputMessage* msg); - static ssize_t findSampleNoLaterThan(const Batch& batch, nsecs_t time); - - static bool isTouchResamplingEnabled(); -}; - } // namespace android diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index a9d6e8d3bf..9791212e07 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 6b8bc01c24..65e93a9a87 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -204,6 +204,7 @@ cc_library { srcs: [ "AccelerationCurve.cpp", "Input.cpp", + "InputConsumer.cpp", "InputDevice.cpp", "InputEventLabels.cpp", "InputTransport.cpp", diff --git a/libs/input/InputConsumer.cpp b/libs/input/InputConsumer.cpp new file mode 100644 index 0000000000..e0d874ef76 --- /dev/null +++ b/libs/input/InputConsumer.cpp @@ -0,0 +1,939 @@ +/** + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "InputTransport" +#define ATRACE_TAG ATRACE_TAG_INPUT + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace input_flags = com::android::input::flags; + +namespace android { + +namespace { + +/** + * Log debug messages relating to the consumer end of the transport channel. + * Enable this via "adb shell setprop log.tag.InputTransportConsumer DEBUG" (requires restart) + */ + +const bool DEBUG_TRANSPORT_CONSUMER = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Consumer", ANDROID_LOG_INFO); + +const bool IS_DEBUGGABLE_BUILD = +#if defined(__ANDROID__) + android::base::GetBoolProperty("ro.debuggable", false); +#else + true; +#endif + +/** + * Log debug messages about touch event resampling. + * + * Enable this via "adb shell setprop log.tag.InputTransportResampling DEBUG". + * This requires a restart on non-debuggable (e.g. user) builds, but should take effect immediately + * on debuggable builds (e.g. userdebug). + */ +bool debugResampling() { + if (!IS_DEBUGGABLE_BUILD) { + static const bool DEBUG_TRANSPORT_RESAMPLING = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling", + ANDROID_LOG_INFO); + return DEBUG_TRANSPORT_RESAMPLING; + } + return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling", ANDROID_LOG_INFO); +} + +void initializeKeyEvent(KeyEvent& event, const InputMessage& msg) { + event.initialize(msg.body.key.eventId, msg.body.key.deviceId, msg.body.key.source, + msg.body.key.displayId, msg.body.key.hmac, msg.body.key.action, + msg.body.key.flags, msg.body.key.keyCode, msg.body.key.scanCode, + msg.body.key.metaState, msg.body.key.repeatCount, msg.body.key.downTime, + msg.body.key.eventTime); +} + +void initializeFocusEvent(FocusEvent& event, const InputMessage& msg) { + event.initialize(msg.body.focus.eventId, msg.body.focus.hasFocus); +} + +void initializeCaptureEvent(CaptureEvent& event, const InputMessage& msg) { + event.initialize(msg.body.capture.eventId, msg.body.capture.pointerCaptureEnabled); +} + +void initializeDragEvent(DragEvent& event, const InputMessage& msg) { + event.initialize(msg.body.drag.eventId, msg.body.drag.x, msg.body.drag.y, + msg.body.drag.isExiting); +} + +void initializeMotionEvent(MotionEvent& event, const InputMessage& msg) { + uint32_t pointerCount = msg.body.motion.pointerCount; + PointerProperties pointerProperties[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + for (uint32_t i = 0; i < pointerCount; i++) { + pointerProperties[i] = msg.body.motion.pointers[i].properties; + pointerCoords[i] = msg.body.motion.pointers[i].coords; + } + + ui::Transform transform; + transform.set({msg.body.motion.dsdx, msg.body.motion.dtdx, msg.body.motion.tx, + msg.body.motion.dtdy, msg.body.motion.dsdy, msg.body.motion.ty, 0, 0, 1}); + ui::Transform displayTransform; + displayTransform.set({msg.body.motion.dsdxRaw, msg.body.motion.dtdxRaw, msg.body.motion.txRaw, + msg.body.motion.dtdyRaw, msg.body.motion.dsdyRaw, msg.body.motion.tyRaw, + 0, 0, 1}); + event.initialize(msg.body.motion.eventId, msg.body.motion.deviceId, msg.body.motion.source, + msg.body.motion.displayId, msg.body.motion.hmac, msg.body.motion.action, + msg.body.motion.actionButton, msg.body.motion.flags, msg.body.motion.edgeFlags, + msg.body.motion.metaState, msg.body.motion.buttonState, + msg.body.motion.classification, transform, msg.body.motion.xPrecision, + msg.body.motion.yPrecision, msg.body.motion.xCursorPosition, + msg.body.motion.yCursorPosition, displayTransform, msg.body.motion.downTime, + msg.body.motion.eventTime, pointerCount, pointerProperties, pointerCoords); +} + +void addSample(MotionEvent& event, const InputMessage& msg) { + uint32_t pointerCount = msg.body.motion.pointerCount; + PointerCoords pointerCoords[pointerCount]; + for (uint32_t i = 0; i < pointerCount; i++) { + pointerCoords[i] = msg.body.motion.pointers[i].coords; + } + + event.setMetaState(event.getMetaState() | msg.body.motion.metaState); + event.addSample(msg.body.motion.eventTime, pointerCoords); +} + +void initializeTouchModeEvent(TouchModeEvent& event, const InputMessage& msg) { + event.initialize(msg.body.touchMode.eventId, msg.body.touchMode.isInTouchMode); +} + +// Nanoseconds per milliseconds. +constexpr nsecs_t NANOS_PER_MS = 1000000; + +// Latency added during resampling. A few milliseconds doesn't hurt much but +// reduces the impact of mispredicted touch positions. +const std::chrono::duration RESAMPLE_LATENCY = 5ms; + +// Minimum time difference between consecutive samples before attempting to resample. +const nsecs_t RESAMPLE_MIN_DELTA = 2 * NANOS_PER_MS; + +// Maximum time difference between consecutive samples before attempting to resample +// by extrapolation. +const nsecs_t RESAMPLE_MAX_DELTA = 20 * NANOS_PER_MS; + +// Maximum time to predict forward from the last known state, to avoid predicting too +// far into the future. This time is further bounded by 50% of the last time delta. +const nsecs_t RESAMPLE_MAX_PREDICTION = 8 * NANOS_PER_MS; + +/** + * System property for enabling / disabling touch resampling. + * Resampling extrapolates / interpolates the reported touch event coordinates to better + * align them to the VSYNC signal, thus resulting in smoother scrolling performance. + * Resampling is not needed (and should be disabled) on hardware that already + * has touch events triggered by VSYNC. + * Set to "1" to enable resampling (default). + * Set to "0" to disable resampling. + * Resampling is enabled by default. + */ +const char* PROPERTY_RESAMPLING_ENABLED = "ro.input.resampling"; + +inline float lerp(float a, float b, float alpha) { + return a + alpha * (b - a); +} + +inline bool isPointerEvent(int32_t source) { + return (source & AINPUT_SOURCE_CLASS_POINTER) == AINPUT_SOURCE_CLASS_POINTER; +} + +bool shouldResampleTool(ToolType toolType) { + return toolType == ToolType::FINGER || toolType == ToolType::UNKNOWN; +} + +} // namespace + +using android::base::Result; +using android::base::StringPrintf; + +// --- InputConsumer --- + +InputConsumer::InputConsumer(const std::shared_ptr& channel) + : InputConsumer(channel, isTouchResamplingEnabled()) {} + +InputConsumer::InputConsumer(const std::shared_ptr& channel, + bool enableTouchResampling) + : mResampleTouch(enableTouchResampling), mChannel(channel), mMsgDeferred(false) {} + +InputConsumer::~InputConsumer() {} + +bool InputConsumer::isTouchResamplingEnabled() { + return property_get_bool(PROPERTY_RESAMPLING_ENABLED, true); +} + +status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consumeBatches, + nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) { + ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, + "channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%" PRId64, + mChannel->getName().c_str(), toString(consumeBatches), frameTime); + + *outSeq = 0; + *outEvent = nullptr; + + // Fetch the next input message. + // Loop until an event can be returned or no additional events are received. + while (!*outEvent) { + if (mMsgDeferred) { + // mMsg contains a valid input message from the previous call to consume + // that has not yet been processed. + mMsgDeferred = false; + } else { + // Receive a fresh message. + status_t result = mChannel->receiveMessage(&mMsg); + if (result == OK) { + const auto [_, inserted] = + mConsumeTimes.emplace(mMsg.header.seq, systemTime(SYSTEM_TIME_MONOTONIC)); + LOG_ALWAYS_FATAL_IF(!inserted, "Already have a consume time for seq=%" PRIu32, + mMsg.header.seq); + + // Trace the event processing timeline - event was just read from the socket + ATRACE_ASYNC_BEGIN("InputConsumer processing", /*cookie=*/mMsg.header.seq); + } + if (result) { + // Consume the next batched event unless batches are being held for later. + if (consumeBatches || result != WOULD_BLOCK) { + result = consumeBatch(factory, frameTime, outSeq, outEvent); + if (*outEvent) { + ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, + "channel '%s' consumer ~ consumed batch event, seq=%u", + mChannel->getName().c_str(), *outSeq); + break; + } + } + return result; + } + } + + switch (mMsg.header.type) { + case InputMessage::Type::KEY: { + KeyEvent* keyEvent = factory->createKeyEvent(); + if (!keyEvent) return NO_MEMORY; + + initializeKeyEvent(*keyEvent, mMsg); + *outSeq = mMsg.header.seq; + *outEvent = keyEvent; + ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, + "channel '%s' consumer ~ consumed key event, seq=%u", + mChannel->getName().c_str(), *outSeq); + break; + } + + case InputMessage::Type::MOTION: { + ssize_t batchIndex = findBatch(mMsg.body.motion.deviceId, mMsg.body.motion.source); + if (batchIndex >= 0) { + Batch& batch = mBatches[batchIndex]; + if (canAddSample(batch, &mMsg)) { + batch.samples.push_back(mMsg); + ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, + "channel '%s' consumer ~ appended to batch event", + mChannel->getName().c_str()); + break; + } else if (isPointerEvent(mMsg.body.motion.source) && + mMsg.body.motion.action == AMOTION_EVENT_ACTION_CANCEL) { + // No need to process events that we are going to cancel anyways + const size_t count = batch.samples.size(); + for (size_t i = 0; i < count; i++) { + const InputMessage& msg = batch.samples[i]; + sendFinishedSignal(msg.header.seq, false); + } + batch.samples.erase(batch.samples.begin(), batch.samples.begin() + count); + mBatches.erase(mBatches.begin() + batchIndex); + } else { + // We cannot append to the batch in progress, so we need to consume + // the previous batch right now and defer the new message until later. + mMsgDeferred = true; + status_t result = consumeSamples(factory, batch, batch.samples.size(), + outSeq, outEvent); + mBatches.erase(mBatches.begin() + batchIndex); + if (result) { + return result; + } + ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, + "channel '%s' consumer ~ consumed batch event and " + "deferred current event, seq=%u", + mChannel->getName().c_str(), *outSeq); + break; + } + } + + // Start a new batch if needed. + if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE || + mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) { + Batch batch; + batch.samples.push_back(mMsg); + mBatches.push_back(batch); + ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, + "channel '%s' consumer ~ started batch event", + mChannel->getName().c_str()); + break; + } + + MotionEvent* motionEvent = factory->createMotionEvent(); + if (!motionEvent) return NO_MEMORY; + + updateTouchState(mMsg); + initializeMotionEvent(*motionEvent, mMsg); + *outSeq = mMsg.header.seq; + *outEvent = motionEvent; + + ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, + "channel '%s' consumer ~ consumed motion event, seq=%u", + mChannel->getName().c_str(), *outSeq); + break; + } + + case InputMessage::Type::FINISHED: + case InputMessage::Type::TIMELINE: { + LOG_ALWAYS_FATAL("Consumed a %s message, which should never be seen by " + "InputConsumer!", + ftl::enum_string(mMsg.header.type).c_str()); + break; + } + + case InputMessage::Type::FOCUS: { + FocusEvent* focusEvent = factory->createFocusEvent(); + if (!focusEvent) return NO_MEMORY; + + initializeFocusEvent(*focusEvent, mMsg); + *outSeq = mMsg.header.seq; + *outEvent = focusEvent; + break; + } + + case InputMessage::Type::CAPTURE: { + CaptureEvent* captureEvent = factory->createCaptureEvent(); + if (!captureEvent) return NO_MEMORY; + + initializeCaptureEvent(*captureEvent, mMsg); + *outSeq = mMsg.header.seq; + *outEvent = captureEvent; + break; + } + + case InputMessage::Type::DRAG: { + DragEvent* dragEvent = factory->createDragEvent(); + if (!dragEvent) return NO_MEMORY; + + initializeDragEvent(*dragEvent, mMsg); + *outSeq = mMsg.header.seq; + *outEvent = dragEvent; + break; + } + + case InputMessage::Type::TOUCH_MODE: { + TouchModeEvent* touchModeEvent = factory->createTouchModeEvent(); + if (!touchModeEvent) return NO_MEMORY; + + initializeTouchModeEvent(*touchModeEvent, mMsg); + *outSeq = mMsg.header.seq; + *outEvent = touchModeEvent; + break; + } + } + } + return OK; +} + +status_t InputConsumer::consumeBatch(InputEventFactoryInterface* factory, nsecs_t frameTime, + uint32_t* outSeq, InputEvent** outEvent) { + status_t result; + for (size_t i = mBatches.size(); i > 0;) { + i--; + Batch& batch = mBatches[i]; + if (frameTime < 0) { + result = consumeSamples(factory, batch, batch.samples.size(), outSeq, outEvent); + mBatches.erase(mBatches.begin() + i); + return result; + } + + nsecs_t sampleTime = frameTime; + if (mResampleTouch) { + sampleTime -= std::chrono::nanoseconds(RESAMPLE_LATENCY).count(); + } + ssize_t split = findSampleNoLaterThan(batch, sampleTime); + if (split < 0) { + continue; + } + + result = consumeSamples(factory, batch, split + 1, outSeq, outEvent); + const InputMessage* next; + if (batch.samples.empty()) { + mBatches.erase(mBatches.begin() + i); + next = nullptr; + } else { + next = &batch.samples[0]; + } + if (!result && mResampleTouch) { + resampleTouchState(sampleTime, static_cast(*outEvent), next); + } + return result; + } + + return WOULD_BLOCK; +} + +status_t InputConsumer::consumeSamples(InputEventFactoryInterface* factory, Batch& batch, + size_t count, uint32_t* outSeq, InputEvent** outEvent) { + MotionEvent* motionEvent = factory->createMotionEvent(); + if (!motionEvent) return NO_MEMORY; + + uint32_t chain = 0; + for (size_t i = 0; i < count; i++) { + InputMessage& msg = batch.samples[i]; + updateTouchState(msg); + if (i) { + SeqChain seqChain; + seqChain.seq = msg.header.seq; + seqChain.chain = chain; + mSeqChains.push_back(seqChain); + addSample(*motionEvent, msg); + } else { + initializeMotionEvent(*motionEvent, msg); + } + chain = msg.header.seq; + } + batch.samples.erase(batch.samples.begin(), batch.samples.begin() + count); + + *outSeq = chain; + *outEvent = motionEvent; + return OK; +} + +void InputConsumer::updateTouchState(InputMessage& msg) { + if (!mResampleTouch || !isPointerEvent(msg.body.motion.source)) { + return; + } + + int32_t deviceId = msg.body.motion.deviceId; + int32_t source = msg.body.motion.source; + + // Update the touch state history to incorporate the new input message. + // If the message is in the past relative to the most recently produced resampled + // touch, then use the resampled time and coordinates instead. + switch (msg.body.motion.action & AMOTION_EVENT_ACTION_MASK) { + case AMOTION_EVENT_ACTION_DOWN: { + ssize_t index = findTouchState(deviceId, source); + if (index < 0) { + mTouchStates.push_back({}); + index = mTouchStates.size() - 1; + } + TouchState& touchState = mTouchStates[index]; + touchState.initialize(deviceId, source); + touchState.addHistory(msg); + break; + } + + case AMOTION_EVENT_ACTION_MOVE: { + ssize_t index = findTouchState(deviceId, source); + if (index >= 0) { + TouchState& touchState = mTouchStates[index]; + touchState.addHistory(msg); + rewriteMessage(touchState, msg); + } + break; + } + + case AMOTION_EVENT_ACTION_POINTER_DOWN: { + ssize_t index = findTouchState(deviceId, source); + if (index >= 0) { + TouchState& touchState = mTouchStates[index]; + touchState.lastResample.idBits.clearBit(msg.body.motion.getActionId()); + rewriteMessage(touchState, msg); + } + break; + } + + case AMOTION_EVENT_ACTION_POINTER_UP: { + ssize_t index = findTouchState(deviceId, source); + if (index >= 0) { + TouchState& touchState = mTouchStates[index]; + rewriteMessage(touchState, msg); + touchState.lastResample.idBits.clearBit(msg.body.motion.getActionId()); + } + break; + } + + case AMOTION_EVENT_ACTION_SCROLL: { + ssize_t index = findTouchState(deviceId, source); + if (index >= 0) { + TouchState& touchState = mTouchStates[index]; + rewriteMessage(touchState, msg); + } + break; + } + + case AMOTION_EVENT_ACTION_UP: + case AMOTION_EVENT_ACTION_CANCEL: { + ssize_t index = findTouchState(deviceId, source); + if (index >= 0) { + TouchState& touchState = mTouchStates[index]; + rewriteMessage(touchState, msg); + mTouchStates.erase(mTouchStates.begin() + index); + } + break; + } + } +} + +/** + * Replace the coordinates in msg with the coordinates in lastResample, if necessary. + * + * If lastResample is no longer valid for a specific pointer (i.e. the lastResample time + * is in the past relative to msg and the past two events do not contain identical coordinates), + * then invalidate the lastResample data for that pointer. + * If the two past events have identical coordinates, then lastResample data for that pointer will + * remain valid, and will be used to replace these coordinates. Thus, if a certain coordinate x0 is + * resampled to the new value x1, then x1 will always be used to replace x0 until some new value + * not equal to x0 is received. + */ +void InputConsumer::rewriteMessage(TouchState& state, InputMessage& msg) { + nsecs_t eventTime = msg.body.motion.eventTime; + for (uint32_t i = 0; i < msg.body.motion.pointerCount; i++) { + uint32_t id = msg.body.motion.pointers[i].properties.id; + if (state.lastResample.idBits.hasBit(id)) { + if (eventTime < state.lastResample.eventTime || + state.recentCoordinatesAreIdentical(id)) { + PointerCoords& msgCoords = msg.body.motion.pointers[i].coords; + const PointerCoords& resampleCoords = state.lastResample.getPointerById(id); + ALOGD_IF(debugResampling(), "[%d] - rewrite (%0.3f, %0.3f), old (%0.3f, %0.3f)", id, + resampleCoords.getX(), resampleCoords.getY(), msgCoords.getX(), + msgCoords.getY()); + msgCoords.setAxisValue(AMOTION_EVENT_AXIS_X, resampleCoords.getX()); + msgCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, resampleCoords.getY()); + msgCoords.isResampled = true; + } else { + state.lastResample.idBits.clearBit(id); + } + } + } +} + +void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event, + const InputMessage* next) { + if (!mResampleTouch || !(isPointerEvent(event->getSource())) || + event->getAction() != AMOTION_EVENT_ACTION_MOVE) { + return; + } + + ssize_t index = findTouchState(event->getDeviceId(), event->getSource()); + if (index < 0) { + ALOGD_IF(debugResampling(), "Not resampled, no touch state for device."); + return; + } + + TouchState& touchState = mTouchStates[index]; + if (touchState.historySize < 1) { + ALOGD_IF(debugResampling(), "Not resampled, no history for device."); + return; + } + + // Ensure that the current sample has all of the pointers that need to be reported. + const History* current = touchState.getHistory(0); + size_t pointerCount = event->getPointerCount(); + for (size_t i = 0; i < pointerCount; i++) { + uint32_t id = event->getPointerId(i); + if (!current->idBits.hasBit(id)) { + ALOGD_IF(debugResampling(), "Not resampled, missing id %d", id); + return; + } + } + + // Find the data to use for resampling. + const History* other; + History future; + float alpha; + if (next) { + // Interpolate between current sample and future sample. + // So current->eventTime <= sampleTime <= future.eventTime. + future.initializeFrom(*next); + other = &future; + nsecs_t delta = future.eventTime - current->eventTime; + if (delta < RESAMPLE_MIN_DELTA) { + ALOGD_IF(debugResampling(), "Not resampled, delta time is too small: %" PRId64 " ns.", + delta); + return; + } + alpha = float(sampleTime - current->eventTime) / delta; + } else if (touchState.historySize >= 2) { + // Extrapolate future sample using current sample and past sample. + // So other->eventTime <= current->eventTime <= sampleTime. + other = touchState.getHistory(1); + nsecs_t delta = current->eventTime - other->eventTime; + if (delta < RESAMPLE_MIN_DELTA) { + ALOGD_IF(debugResampling(), "Not resampled, delta time is too small: %" PRId64 " ns.", + delta); + return; + } else if (delta > RESAMPLE_MAX_DELTA) { + ALOGD_IF(debugResampling(), "Not resampled, delta time is too large: %" PRId64 " ns.", + delta); + return; + } + nsecs_t maxPredict = current->eventTime + std::min(delta / 2, RESAMPLE_MAX_PREDICTION); + if (sampleTime > maxPredict) { + ALOGD_IF(debugResampling(), + "Sample time is too far in the future, adjusting prediction " + "from %" PRId64 " to %" PRId64 " ns.", + sampleTime - current->eventTime, maxPredict - current->eventTime); + sampleTime = maxPredict; + } + alpha = float(current->eventTime - sampleTime) / delta; + } else { + ALOGD_IF(debugResampling(), "Not resampled, insufficient data."); + return; + } + + if (current->eventTime == sampleTime) { + // Prevents having 2 events with identical times and coordinates. + return; + } + + // Resample touch coordinates. + History oldLastResample; + oldLastResample.initializeFrom(touchState.lastResample); + touchState.lastResample.eventTime = sampleTime; + touchState.lastResample.idBits.clear(); + for (size_t i = 0; i < pointerCount; i++) { + uint32_t id = event->getPointerId(i); + touchState.lastResample.idToIndex[id] = i; + touchState.lastResample.idBits.markBit(id); + if (oldLastResample.hasPointerId(id) && touchState.recentCoordinatesAreIdentical(id)) { + // We maintain the previously resampled value for this pointer (stored in + // oldLastResample) when the coordinates for this pointer haven't changed since then. + // This way we don't introduce artificial jitter when pointers haven't actually moved. + // The isResampled flag isn't cleared as the values don't reflect what the device is + // actually reporting. + + // We know here that the coordinates for the pointer haven't changed because we + // would've cleared the resampled bit in rewriteMessage if they had. We can't modify + // lastResample in place because the mapping from pointer ID to index may have changed. + touchState.lastResample.pointers[i] = oldLastResample.getPointerById(id); + continue; + } + + PointerCoords& resampledCoords = touchState.lastResample.pointers[i]; + const PointerCoords& currentCoords = current->getPointerById(id); + resampledCoords = currentCoords; + resampledCoords.isResampled = true; + if (other->idBits.hasBit(id) && shouldResampleTool(event->getToolType(i))) { + const PointerCoords& otherCoords = other->getPointerById(id); + resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X, + lerp(currentCoords.getX(), otherCoords.getX(), alpha)); + resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, + lerp(currentCoords.getY(), otherCoords.getY(), alpha)); + ALOGD_IF(debugResampling(), + "[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f), " + "other (%0.3f, %0.3f), alpha %0.3f", + id, resampledCoords.getX(), resampledCoords.getY(), currentCoords.getX(), + currentCoords.getY(), otherCoords.getX(), otherCoords.getY(), alpha); + } else { + ALOGD_IF(debugResampling(), "[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f)", id, + resampledCoords.getX(), resampledCoords.getY(), currentCoords.getX(), + currentCoords.getY()); + } + } + + event->addSample(sampleTime, touchState.lastResample.pointers); +} + +status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) { + ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, + "channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s", + mChannel->getName().c_str(), seq, toString(handled)); + + if (!seq) { + ALOGE("Attempted to send a finished signal with sequence number 0."); + return BAD_VALUE; + } + + // Send finished signals for the batch sequence chain first. + size_t seqChainCount = mSeqChains.size(); + if (seqChainCount) { + uint32_t currentSeq = seq; + uint32_t chainSeqs[seqChainCount]; + size_t chainIndex = 0; + for (size_t i = seqChainCount; i > 0;) { + i--; + const SeqChain& seqChain = mSeqChains[i]; + if (seqChain.seq == currentSeq) { + currentSeq = seqChain.chain; + chainSeqs[chainIndex++] = currentSeq; + mSeqChains.erase(mSeqChains.begin() + i); + } + } + status_t status = OK; + while (!status && chainIndex > 0) { + chainIndex--; + status = sendUnchainedFinishedSignal(chainSeqs[chainIndex], handled); + } + if (status) { + // An error occurred so at least one signal was not sent, reconstruct the chain. + for (;;) { + SeqChain seqChain; + seqChain.seq = chainIndex != 0 ? chainSeqs[chainIndex - 1] : seq; + seqChain.chain = chainSeqs[chainIndex]; + mSeqChains.push_back(seqChain); + if (!chainIndex) break; + chainIndex--; + } + return status; + } + } + + // Send finished signal for the last message in the batch. + return sendUnchainedFinishedSignal(seq, handled); +} + +status_t InputConsumer::sendTimeline(int32_t inputEventId, + std::array graphicsTimeline) { + ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, + "channel '%s' consumer ~ sendTimeline: inputEventId=%" PRId32 + ", gpuCompletedTime=%" PRId64 ", presentTime=%" PRId64, + mChannel->getName().c_str(), inputEventId, + graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME], + graphicsTimeline[GraphicsTimeline::PRESENT_TIME]); + + InputMessage msg; + msg.header.type = InputMessage::Type::TIMELINE; + msg.header.seq = 0; + msg.body.timeline.eventId = inputEventId; + msg.body.timeline.graphicsTimeline = std::move(graphicsTimeline); + return mChannel->sendMessage(&msg); +} + +nsecs_t InputConsumer::getConsumeTime(uint32_t seq) const { + auto it = mConsumeTimes.find(seq); + // Consume time will be missing if either 'finishInputEvent' is called twice, or if it was + // called for the wrong (synthetic?) input event. Either way, it is a bug that should be fixed. + LOG_ALWAYS_FATAL_IF(it == mConsumeTimes.end(), "Could not find consume time for seq=%" PRIu32, + seq); + return it->second; +} + +void InputConsumer::popConsumeTime(uint32_t seq) { + mConsumeTimes.erase(seq); +} + +status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) { + InputMessage msg; + msg.header.type = InputMessage::Type::FINISHED; + msg.header.seq = seq; + msg.body.finished.handled = handled; + msg.body.finished.consumeTime = getConsumeTime(seq); + status_t result = mChannel->sendMessage(&msg); + if (result == OK) { + // Remove the consume time if the socket write succeeded. We will not need to ack this + // message anymore. If the socket write did not succeed, we will try again and will still + // need consume time. + popConsumeTime(seq); + + // Trace the event processing timeline - event was just finished + ATRACE_ASYNC_END("InputConsumer processing", /*cookie=*/seq); + } + return result; +} + +bool InputConsumer::hasPendingBatch() const { + return !mBatches.empty(); +} + +int32_t InputConsumer::getPendingBatchSource() const { + if (mBatches.empty()) { + return AINPUT_SOURCE_CLASS_NONE; + } + + const Batch& batch = mBatches[0]; + const InputMessage& head = batch.samples[0]; + return head.body.motion.source; +} + +bool InputConsumer::probablyHasInput() const { + return hasPendingBatch() || mChannel->probablyHasInput(); +} + +ssize_t InputConsumer::findBatch(int32_t deviceId, int32_t source) const { + for (size_t i = 0; i < mBatches.size(); i++) { + const Batch& batch = mBatches[i]; + const InputMessage& head = batch.samples[0]; + if (head.body.motion.deviceId == deviceId && head.body.motion.source == source) { + return i; + } + } + return -1; +} + +ssize_t InputConsumer::findTouchState(int32_t deviceId, int32_t source) const { + for (size_t i = 0; i < mTouchStates.size(); i++) { + const TouchState& touchState = mTouchStates[i]; + if (touchState.deviceId == deviceId && touchState.source == source) { + return i; + } + } + return -1; +} + +bool InputConsumer::canAddSample(const Batch& batch, const InputMessage* msg) { + const InputMessage& head = batch.samples[0]; + uint32_t pointerCount = msg->body.motion.pointerCount; + if (head.body.motion.pointerCount != pointerCount || + head.body.motion.action != msg->body.motion.action) { + return false; + } + for (size_t i = 0; i < pointerCount; i++) { + if (head.body.motion.pointers[i].properties != msg->body.motion.pointers[i].properties) { + return false; + } + } + return true; +} + +ssize_t InputConsumer::findSampleNoLaterThan(const Batch& batch, nsecs_t time) { + size_t numSamples = batch.samples.size(); + size_t index = 0; + while (index < numSamples && batch.samples[index].body.motion.eventTime <= time) { + index += 1; + } + return ssize_t(index) - 1; +} + +std::string InputConsumer::dump() const { + std::string out; + out = out + "mResampleTouch = " + toString(mResampleTouch) + "\n"; + out = out + "mChannel = " + mChannel->getName() + "\n"; + out = out + "mMsgDeferred: " + toString(mMsgDeferred) + "\n"; + if (mMsgDeferred) { + out = out + "mMsg : " + ftl::enum_string(mMsg.header.type) + "\n"; + } + out += "Batches:\n"; + for (const Batch& batch : mBatches) { + out += " Batch:\n"; + for (const InputMessage& msg : batch.samples) { + out += android::base::StringPrintf(" Message %" PRIu32 ": %s ", msg.header.seq, + ftl::enum_string(msg.header.type).c_str()); + switch (msg.header.type) { + case InputMessage::Type::KEY: { + out += android::base::StringPrintf("action=%s keycode=%" PRId32, + KeyEvent::actionToString( + msg.body.key.action), + msg.body.key.keyCode); + break; + } + case InputMessage::Type::MOTION: { + out = out + "action=" + MotionEvent::actionToString(msg.body.motion.action); + for (uint32_t i = 0; i < msg.body.motion.pointerCount; i++) { + const float x = msg.body.motion.pointers[i].coords.getX(); + const float y = msg.body.motion.pointers[i].coords.getY(); + out += android::base::StringPrintf("\n Pointer %" PRIu32 + " : x=%.1f y=%.1f", + i, x, y); + } + break; + } + case InputMessage::Type::FINISHED: { + out += android::base::StringPrintf("handled=%s, consumeTime=%" PRId64, + toString(msg.body.finished.handled), + msg.body.finished.consumeTime); + break; + } + case InputMessage::Type::FOCUS: { + out += android::base::StringPrintf("hasFocus=%s", + toString(msg.body.focus.hasFocus)); + break; + } + case InputMessage::Type::CAPTURE: { + out += android::base::StringPrintf("hasCapture=%s", + toString(msg.body.capture + .pointerCaptureEnabled)); + break; + } + case InputMessage::Type::DRAG: { + out += android::base::StringPrintf("x=%.1f y=%.1f, isExiting=%s", + msg.body.drag.x, msg.body.drag.y, + toString(msg.body.drag.isExiting)); + break; + } + case InputMessage::Type::TIMELINE: { + const nsecs_t gpuCompletedTime = + msg.body.timeline + .graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME]; + const nsecs_t presentTime = + msg.body.timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME]; + out += android::base::StringPrintf("inputEventId=%" PRId32 + ", gpuCompletedTime=%" PRId64 + ", presentTime=%" PRId64, + msg.body.timeline.eventId, gpuCompletedTime, + presentTime); + break; + } + case InputMessage::Type::TOUCH_MODE: { + out += android::base::StringPrintf("isInTouchMode=%s", + toString(msg.body.touchMode.isInTouchMode)); + break; + } + } + out += "\n"; + } + } + if (mBatches.empty()) { + out += " \n"; + } + out += "mSeqChains:\n"; + for (const SeqChain& chain : mSeqChains) { + out += android::base::StringPrintf(" chain: seq = %" PRIu32 " chain=%" PRIu32, chain.seq, + chain.chain); + } + if (mSeqChains.empty()) { + out += " \n"; + } + out += "mConsumeTimes:\n"; + for (const auto& [seq, consumeTime] : mConsumeTimes) { + out += android::base::StringPrintf(" seq = %" PRIu32 " consumeTime = %" PRId64, seq, + consumeTime); + } + if (mConsumeTimes.empty()) { + out += " \n"; + } + return out; +} + +} // namespace android diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index b3a36ebf5a..1869483474 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -51,14 +51,6 @@ const bool DEBUG_CHANNEL_MESSAGES = const bool DEBUG_CHANNEL_LIFECYCLE = __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Lifecycle", ANDROID_LOG_INFO); -/** - * Log debug messages relating to the consumer end of the transport channel. - * Enable this via "adb shell setprop log.tag.InputTransportConsumer DEBUG" (requires restart) - */ - -const bool DEBUG_TRANSPORT_CONSUMER = - __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Consumer", ANDROID_LOG_INFO); - const bool IS_DEBUGGABLE_BUILD = #if defined(__ANDROID__) android::base::GetBoolProperty("ro.debuggable", false); @@ -81,23 +73,6 @@ bool debugTransportPublisher() { return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Publisher", ANDROID_LOG_INFO); } -/** - * Log debug messages about touch event resampling. - * - * Enable this via "adb shell setprop log.tag.InputTransportResampling DEBUG". - * This requires a restart on non-debuggable (e.g. user) builds, but should take effect immediately - * on debuggable builds (e.g. userdebug). - */ -bool debugResampling() { - if (!IS_DEBUGGABLE_BUILD) { - static const bool DEBUG_TRANSPORT_RESAMPLING = - __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling", - ANDROID_LOG_INFO); - return DEBUG_TRANSPORT_RESAMPLING; - } - return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling", ANDROID_LOG_INFO); -} - android::base::unique_fd dupChannelFd(int fd) { android::base::unique_fd newFd(::dup(fd)); if (!newFd.ok()) { @@ -113,103 +88,11 @@ android::base::unique_fd dupChannelFd(int fd) { return newFd; } -void initializeKeyEvent(KeyEvent& event, const InputMessage& msg) { - event.initialize(msg.body.key.eventId, msg.body.key.deviceId, msg.body.key.source, - msg.body.key.displayId, msg.body.key.hmac, msg.body.key.action, - msg.body.key.flags, msg.body.key.keyCode, msg.body.key.scanCode, - msg.body.key.metaState, msg.body.key.repeatCount, msg.body.key.downTime, - msg.body.key.eventTime); -} - -void initializeFocusEvent(FocusEvent& event, const InputMessage& msg) { - event.initialize(msg.body.focus.eventId, msg.body.focus.hasFocus); -} - -void initializeCaptureEvent(CaptureEvent& event, const InputMessage& msg) { - event.initialize(msg.body.capture.eventId, msg.body.capture.pointerCaptureEnabled); -} - -void initializeDragEvent(DragEvent& event, const InputMessage& msg) { - event.initialize(msg.body.drag.eventId, msg.body.drag.x, msg.body.drag.y, - msg.body.drag.isExiting); -} - -void initializeMotionEvent(MotionEvent& event, const InputMessage& msg) { - uint32_t pointerCount = msg.body.motion.pointerCount; - PointerProperties pointerProperties[pointerCount]; - PointerCoords pointerCoords[pointerCount]; - for (uint32_t i = 0; i < pointerCount; i++) { - pointerProperties[i] = msg.body.motion.pointers[i].properties; - pointerCoords[i] = msg.body.motion.pointers[i].coords; - } - - ui::Transform transform; - transform.set({msg.body.motion.dsdx, msg.body.motion.dtdx, msg.body.motion.tx, - msg.body.motion.dtdy, msg.body.motion.dsdy, msg.body.motion.ty, 0, 0, 1}); - ui::Transform displayTransform; - displayTransform.set({msg.body.motion.dsdxRaw, msg.body.motion.dtdxRaw, msg.body.motion.txRaw, - msg.body.motion.dtdyRaw, msg.body.motion.dsdyRaw, msg.body.motion.tyRaw, - 0, 0, 1}); - event.initialize(msg.body.motion.eventId, msg.body.motion.deviceId, msg.body.motion.source, - msg.body.motion.displayId, msg.body.motion.hmac, msg.body.motion.action, - msg.body.motion.actionButton, msg.body.motion.flags, msg.body.motion.edgeFlags, - msg.body.motion.metaState, msg.body.motion.buttonState, - msg.body.motion.classification, transform, msg.body.motion.xPrecision, - msg.body.motion.yPrecision, msg.body.motion.xCursorPosition, - msg.body.motion.yCursorPosition, displayTransform, msg.body.motion.downTime, - msg.body.motion.eventTime, pointerCount, pointerProperties, pointerCoords); -} - -void addSample(MotionEvent& event, const InputMessage& msg) { - uint32_t pointerCount = msg.body.motion.pointerCount; - PointerCoords pointerCoords[pointerCount]; - for (uint32_t i = 0; i < pointerCount; i++) { - pointerCoords[i] = msg.body.motion.pointers[i].coords; - } - - event.setMetaState(event.getMetaState() | msg.body.motion.metaState); - event.addSample(msg.body.motion.eventTime, pointerCoords); -} - -void initializeTouchModeEvent(TouchModeEvent& event, const InputMessage& msg) { - event.initialize(msg.body.touchMode.eventId, msg.body.touchMode.isInTouchMode); -} - // Socket buffer size. The default is typically about 128KB, which is much larger than // we really need. So we make it smaller. It just needs to be big enough to hold // a few dozen large multi-finger motion events in the case where an application gets // behind processing touches. -static constexpr size_t SOCKET_BUFFER_SIZE = 32 * 1024; - -// Nanoseconds per milliseconds. -static constexpr nsecs_t NANOS_PER_MS = 1000000; - -// Latency added during resampling. A few milliseconds doesn't hurt much but -// reduces the impact of mispredicted touch positions. -const std::chrono::duration RESAMPLE_LATENCY = 5ms; - -// Minimum time difference between consecutive samples before attempting to resample. -static const nsecs_t RESAMPLE_MIN_DELTA = 2 * NANOS_PER_MS; - -// Maximum time difference between consecutive samples before attempting to resample -// by extrapolation. -static const nsecs_t RESAMPLE_MAX_DELTA = 20 * NANOS_PER_MS; - -// Maximum time to predict forward from the last known state, to avoid predicting too -// far into the future. This time is further bounded by 50% of the last time delta. -static const nsecs_t RESAMPLE_MAX_PREDICTION = 8 * NANOS_PER_MS; - -/** - * System property for enabling / disabling touch resampling. - * Resampling extrapolates / interpolates the reported touch event coordinates to better - * align them to the VSYNC signal, thus resulting in smoother scrolling performance. - * Resampling is not needed (and should be disabled) on hardware that already - * has touch events triggered by VSYNC. - * Set to "1" to enable resampling (default). - * Set to "0" to disable resampling. - * Resampling is enabled by default. - */ -static const char* PROPERTY_RESAMPLING_ENABLED = "ro.input.resampling"; +constexpr size_t SOCKET_BUFFER_SIZE = 32 * 1024; /** * Crash if the events that are getting sent to the InputPublisher are inconsistent. @@ -220,18 +103,6 @@ bool verifyEvents() { __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "VerifyEvents", ANDROID_LOG_INFO); } -inline float lerp(float a, float b, float alpha) { - return a + alpha * (b - a); -} - -inline bool isPointerEvent(int32_t source) { - return (source & AINPUT_SOURCE_CLASS_POINTER) == AINPUT_SOURCE_CLASS_POINTER; -} - -bool shouldResampleTool(ToolType toolType) { - return toolType == ToolType::FINGER || toolType == ToolType::UNKNOWN; -} - } // namespace using android::base::Result; @@ -892,756 +763,4 @@ android::base::Result InputPublisher::receiveC return android::base::Error(UNKNOWN_ERROR); } -// --- InputConsumer --- - -InputConsumer::InputConsumer(const std::shared_ptr& channel) - : InputConsumer(channel, isTouchResamplingEnabled()) {} - -InputConsumer::InputConsumer(const std::shared_ptr& channel, - bool enableTouchResampling) - : mResampleTouch(enableTouchResampling), mChannel(channel), mMsgDeferred(false) {} - -InputConsumer::~InputConsumer() { -} - -bool InputConsumer::isTouchResamplingEnabled() { - return property_get_bool(PROPERTY_RESAMPLING_ENABLED, true); -} - -status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consumeBatches, - nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) { - ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, - "channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%" PRId64, - mChannel->getName().c_str(), toString(consumeBatches), frameTime); - - *outSeq = 0; - *outEvent = nullptr; - - // Fetch the next input message. - // Loop until an event can be returned or no additional events are received. - while (!*outEvent) { - if (mMsgDeferred) { - // mMsg contains a valid input message from the previous call to consume - // that has not yet been processed. - mMsgDeferred = false; - } else { - // Receive a fresh message. - status_t result = mChannel->receiveMessage(&mMsg); - if (result == OK) { - const auto [_, inserted] = - mConsumeTimes.emplace(mMsg.header.seq, systemTime(SYSTEM_TIME_MONOTONIC)); - LOG_ALWAYS_FATAL_IF(!inserted, "Already have a consume time for seq=%" PRIu32, - mMsg.header.seq); - - // Trace the event processing timeline - event was just read from the socket - ATRACE_ASYNC_BEGIN("InputConsumer processing", /*cookie=*/mMsg.header.seq); - } - if (result) { - // Consume the next batched event unless batches are being held for later. - if (consumeBatches || result != WOULD_BLOCK) { - result = consumeBatch(factory, frameTime, outSeq, outEvent); - if (*outEvent) { - ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, - "channel '%s' consumer ~ consumed batch event, seq=%u", - mChannel->getName().c_str(), *outSeq); - break; - } - } - return result; - } - } - - switch (mMsg.header.type) { - case InputMessage::Type::KEY: { - KeyEvent* keyEvent = factory->createKeyEvent(); - if (!keyEvent) return NO_MEMORY; - - initializeKeyEvent(*keyEvent, mMsg); - *outSeq = mMsg.header.seq; - *outEvent = keyEvent; - ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, - "channel '%s' consumer ~ consumed key event, seq=%u", - mChannel->getName().c_str(), *outSeq); - break; - } - - case InputMessage::Type::MOTION: { - ssize_t batchIndex = findBatch(mMsg.body.motion.deviceId, mMsg.body.motion.source); - if (batchIndex >= 0) { - Batch& batch = mBatches[batchIndex]; - if (canAddSample(batch, &mMsg)) { - batch.samples.push_back(mMsg); - ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, - "channel '%s' consumer ~ appended to batch event", - mChannel->getName().c_str()); - break; - } else if (isPointerEvent(mMsg.body.motion.source) && - mMsg.body.motion.action == AMOTION_EVENT_ACTION_CANCEL) { - // No need to process events that we are going to cancel anyways - const size_t count = batch.samples.size(); - for (size_t i = 0; i < count; i++) { - const InputMessage& msg = batch.samples[i]; - sendFinishedSignal(msg.header.seq, false); - } - batch.samples.erase(batch.samples.begin(), batch.samples.begin() + count); - mBatches.erase(mBatches.begin() + batchIndex); - } else { - // We cannot append to the batch in progress, so we need to consume - // the previous batch right now and defer the new message until later. - mMsgDeferred = true; - status_t result = consumeSamples(factory, batch, batch.samples.size(), - outSeq, outEvent); - mBatches.erase(mBatches.begin() + batchIndex); - if (result) { - return result; - } - ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, - "channel '%s' consumer ~ consumed batch event and " - "deferred current event, seq=%u", - mChannel->getName().c_str(), *outSeq); - break; - } - } - - // Start a new batch if needed. - if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE || - mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) { - Batch batch; - batch.samples.push_back(mMsg); - mBatches.push_back(batch); - ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, - "channel '%s' consumer ~ started batch event", - mChannel->getName().c_str()); - break; - } - - MotionEvent* motionEvent = factory->createMotionEvent(); - if (!motionEvent) return NO_MEMORY; - - updateTouchState(mMsg); - initializeMotionEvent(*motionEvent, mMsg); - *outSeq = mMsg.header.seq; - *outEvent = motionEvent; - - ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, - "channel '%s' consumer ~ consumed motion event, seq=%u", - mChannel->getName().c_str(), *outSeq); - break; - } - - case InputMessage::Type::FINISHED: - case InputMessage::Type::TIMELINE: { - LOG_ALWAYS_FATAL("Consumed a %s message, which should never be seen by " - "InputConsumer!", - ftl::enum_string(mMsg.header.type).c_str()); - break; - } - - case InputMessage::Type::FOCUS: { - FocusEvent* focusEvent = factory->createFocusEvent(); - if (!focusEvent) return NO_MEMORY; - - initializeFocusEvent(*focusEvent, mMsg); - *outSeq = mMsg.header.seq; - *outEvent = focusEvent; - break; - } - - case InputMessage::Type::CAPTURE: { - CaptureEvent* captureEvent = factory->createCaptureEvent(); - if (!captureEvent) return NO_MEMORY; - - initializeCaptureEvent(*captureEvent, mMsg); - *outSeq = mMsg.header.seq; - *outEvent = captureEvent; - break; - } - - case InputMessage::Type::DRAG: { - DragEvent* dragEvent = factory->createDragEvent(); - if (!dragEvent) return NO_MEMORY; - - initializeDragEvent(*dragEvent, mMsg); - *outSeq = mMsg.header.seq; - *outEvent = dragEvent; - break; - } - - case InputMessage::Type::TOUCH_MODE: { - TouchModeEvent* touchModeEvent = factory->createTouchModeEvent(); - if (!touchModeEvent) return NO_MEMORY; - - initializeTouchModeEvent(*touchModeEvent, mMsg); - *outSeq = mMsg.header.seq; - *outEvent = touchModeEvent; - break; - } - } - } - return OK; -} - -status_t InputConsumer::consumeBatch(InputEventFactoryInterface* factory, - nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) { - status_t result; - for (size_t i = mBatches.size(); i > 0; ) { - i--; - Batch& batch = mBatches[i]; - if (frameTime < 0) { - result = consumeSamples(factory, batch, batch.samples.size(), outSeq, outEvent); - mBatches.erase(mBatches.begin() + i); - return result; - } - - nsecs_t sampleTime = frameTime; - if (mResampleTouch) { - sampleTime -= std::chrono::nanoseconds(RESAMPLE_LATENCY).count(); - } - ssize_t split = findSampleNoLaterThan(batch, sampleTime); - if (split < 0) { - continue; - } - - result = consumeSamples(factory, batch, split + 1, outSeq, outEvent); - const InputMessage* next; - if (batch.samples.empty()) { - mBatches.erase(mBatches.begin() + i); - next = nullptr; - } else { - next = &batch.samples[0]; - } - if (!result && mResampleTouch) { - resampleTouchState(sampleTime, static_cast(*outEvent), next); - } - return result; - } - - return WOULD_BLOCK; -} - -status_t InputConsumer::consumeSamples(InputEventFactoryInterface* factory, - Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent) { - MotionEvent* motionEvent = factory->createMotionEvent(); - if (! motionEvent) return NO_MEMORY; - - uint32_t chain = 0; - for (size_t i = 0; i < count; i++) { - InputMessage& msg = batch.samples[i]; - updateTouchState(msg); - if (i) { - SeqChain seqChain; - seqChain.seq = msg.header.seq; - seqChain.chain = chain; - mSeqChains.push_back(seqChain); - addSample(*motionEvent, msg); - } else { - initializeMotionEvent(*motionEvent, msg); - } - chain = msg.header.seq; - } - batch.samples.erase(batch.samples.begin(), batch.samples.begin() + count); - - *outSeq = chain; - *outEvent = motionEvent; - return OK; -} - -void InputConsumer::updateTouchState(InputMessage& msg) { - if (!mResampleTouch || !isPointerEvent(msg.body.motion.source)) { - return; - } - - int32_t deviceId = msg.body.motion.deviceId; - int32_t source = msg.body.motion.source; - - // Update the touch state history to incorporate the new input message. - // If the message is in the past relative to the most recently produced resampled - // touch, then use the resampled time and coordinates instead. - switch (msg.body.motion.action & AMOTION_EVENT_ACTION_MASK) { - case AMOTION_EVENT_ACTION_DOWN: { - ssize_t index = findTouchState(deviceId, source); - if (index < 0) { - mTouchStates.push_back({}); - index = mTouchStates.size() - 1; - } - TouchState& touchState = mTouchStates[index]; - touchState.initialize(deviceId, source); - touchState.addHistory(msg); - break; - } - - case AMOTION_EVENT_ACTION_MOVE: { - ssize_t index = findTouchState(deviceId, source); - if (index >= 0) { - TouchState& touchState = mTouchStates[index]; - touchState.addHistory(msg); - rewriteMessage(touchState, msg); - } - break; - } - - case AMOTION_EVENT_ACTION_POINTER_DOWN: { - ssize_t index = findTouchState(deviceId, source); - if (index >= 0) { - TouchState& touchState = mTouchStates[index]; - touchState.lastResample.idBits.clearBit(msg.body.motion.getActionId()); - rewriteMessage(touchState, msg); - } - break; - } - - case AMOTION_EVENT_ACTION_POINTER_UP: { - ssize_t index = findTouchState(deviceId, source); - if (index >= 0) { - TouchState& touchState = mTouchStates[index]; - rewriteMessage(touchState, msg); - touchState.lastResample.idBits.clearBit(msg.body.motion.getActionId()); - } - break; - } - - case AMOTION_EVENT_ACTION_SCROLL: { - ssize_t index = findTouchState(deviceId, source); - if (index >= 0) { - TouchState& touchState = mTouchStates[index]; - rewriteMessage(touchState, msg); - } - break; - } - - case AMOTION_EVENT_ACTION_UP: - case AMOTION_EVENT_ACTION_CANCEL: { - ssize_t index = findTouchState(deviceId, source); - if (index >= 0) { - TouchState& touchState = mTouchStates[index]; - rewriteMessage(touchState, msg); - mTouchStates.erase(mTouchStates.begin() + index); - } - break; - } - } -} - -/** - * Replace the coordinates in msg with the coordinates in lastResample, if necessary. - * - * If lastResample is no longer valid for a specific pointer (i.e. the lastResample time - * is in the past relative to msg and the past two events do not contain identical coordinates), - * then invalidate the lastResample data for that pointer. - * If the two past events have identical coordinates, then lastResample data for that pointer will - * remain valid, and will be used to replace these coordinates. Thus, if a certain coordinate x0 is - * resampled to the new value x1, then x1 will always be used to replace x0 until some new value - * not equal to x0 is received. - */ -void InputConsumer::rewriteMessage(TouchState& state, InputMessage& msg) { - nsecs_t eventTime = msg.body.motion.eventTime; - for (uint32_t i = 0; i < msg.body.motion.pointerCount; i++) { - uint32_t id = msg.body.motion.pointers[i].properties.id; - if (state.lastResample.idBits.hasBit(id)) { - if (eventTime < state.lastResample.eventTime || - state.recentCoordinatesAreIdentical(id)) { - PointerCoords& msgCoords = msg.body.motion.pointers[i].coords; - const PointerCoords& resampleCoords = state.lastResample.getPointerById(id); - ALOGD_IF(debugResampling(), "[%d] - rewrite (%0.3f, %0.3f), old (%0.3f, %0.3f)", id, - resampleCoords.getX(), resampleCoords.getY(), msgCoords.getX(), - msgCoords.getY()); - msgCoords.setAxisValue(AMOTION_EVENT_AXIS_X, resampleCoords.getX()); - msgCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, resampleCoords.getY()); - msgCoords.isResampled = true; - } else { - state.lastResample.idBits.clearBit(id); - } - } - } -} - -void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event, - const InputMessage* next) { - if (!mResampleTouch - || !(isPointerEvent(event->getSource())) - || event->getAction() != AMOTION_EVENT_ACTION_MOVE) { - return; - } - - ssize_t index = findTouchState(event->getDeviceId(), event->getSource()); - if (index < 0) { - ALOGD_IF(debugResampling(), "Not resampled, no touch state for device."); - return; - } - - TouchState& touchState = mTouchStates[index]; - if (touchState.historySize < 1) { - ALOGD_IF(debugResampling(), "Not resampled, no history for device."); - return; - } - - // Ensure that the current sample has all of the pointers that need to be reported. - const History* current = touchState.getHistory(0); - size_t pointerCount = event->getPointerCount(); - for (size_t i = 0; i < pointerCount; i++) { - uint32_t id = event->getPointerId(i); - if (!current->idBits.hasBit(id)) { - ALOGD_IF(debugResampling(), "Not resampled, missing id %d", id); - return; - } - } - - // Find the data to use for resampling. - const History* other; - History future; - float alpha; - if (next) { - // Interpolate between current sample and future sample. - // So current->eventTime <= sampleTime <= future.eventTime. - future.initializeFrom(*next); - other = &future; - nsecs_t delta = future.eventTime - current->eventTime; - if (delta < RESAMPLE_MIN_DELTA) { - ALOGD_IF(debugResampling(), "Not resampled, delta time is too small: %" PRId64 " ns.", - delta); - return; - } - alpha = float(sampleTime - current->eventTime) / delta; - } else if (touchState.historySize >= 2) { - // Extrapolate future sample using current sample and past sample. - // So other->eventTime <= current->eventTime <= sampleTime. - other = touchState.getHistory(1); - nsecs_t delta = current->eventTime - other->eventTime; - if (delta < RESAMPLE_MIN_DELTA) { - ALOGD_IF(debugResampling(), "Not resampled, delta time is too small: %" PRId64 " ns.", - delta); - return; - } else if (delta > RESAMPLE_MAX_DELTA) { - ALOGD_IF(debugResampling(), "Not resampled, delta time is too large: %" PRId64 " ns.", - delta); - return; - } - nsecs_t maxPredict = current->eventTime + std::min(delta / 2, RESAMPLE_MAX_PREDICTION); - if (sampleTime > maxPredict) { - ALOGD_IF(debugResampling(), - "Sample time is too far in the future, adjusting prediction " - "from %" PRId64 " to %" PRId64 " ns.", - sampleTime - current->eventTime, maxPredict - current->eventTime); - sampleTime = maxPredict; - } - alpha = float(current->eventTime - sampleTime) / delta; - } else { - ALOGD_IF(debugResampling(), "Not resampled, insufficient data."); - return; - } - - if (current->eventTime == sampleTime) { - // Prevents having 2 events with identical times and coordinates. - return; - } - - // Resample touch coordinates. - History oldLastResample; - oldLastResample.initializeFrom(touchState.lastResample); - touchState.lastResample.eventTime = sampleTime; - touchState.lastResample.idBits.clear(); - for (size_t i = 0; i < pointerCount; i++) { - uint32_t id = event->getPointerId(i); - touchState.lastResample.idToIndex[id] = i; - touchState.lastResample.idBits.markBit(id); - if (oldLastResample.hasPointerId(id) && touchState.recentCoordinatesAreIdentical(id)) { - // We maintain the previously resampled value for this pointer (stored in - // oldLastResample) when the coordinates for this pointer haven't changed since then. - // This way we don't introduce artificial jitter when pointers haven't actually moved. - // The isResampled flag isn't cleared as the values don't reflect what the device is - // actually reporting. - - // We know here that the coordinates for the pointer haven't changed because we - // would've cleared the resampled bit in rewriteMessage if they had. We can't modify - // lastResample in place becasue the mapping from pointer ID to index may have changed. - touchState.lastResample.pointers[i] = oldLastResample.getPointerById(id); - continue; - } - - PointerCoords& resampledCoords = touchState.lastResample.pointers[i]; - const PointerCoords& currentCoords = current->getPointerById(id); - resampledCoords = currentCoords; - resampledCoords.isResampled = true; - if (other->idBits.hasBit(id) && shouldResampleTool(event->getToolType(i))) { - const PointerCoords& otherCoords = other->getPointerById(id); - resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X, - lerp(currentCoords.getX(), otherCoords.getX(), alpha)); - resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, - lerp(currentCoords.getY(), otherCoords.getY(), alpha)); - ALOGD_IF(debugResampling(), - "[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f), " - "other (%0.3f, %0.3f), alpha %0.3f", - id, resampledCoords.getX(), resampledCoords.getY(), currentCoords.getX(), - currentCoords.getY(), otherCoords.getX(), otherCoords.getY(), alpha); - } else { - ALOGD_IF(debugResampling(), "[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f)", id, - resampledCoords.getX(), resampledCoords.getY(), currentCoords.getX(), - currentCoords.getY()); - } - } - - event->addSample(sampleTime, touchState.lastResample.pointers); -} - -status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) { - ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, - "channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s", - mChannel->getName().c_str(), seq, toString(handled)); - - if (!seq) { - ALOGE("Attempted to send a finished signal with sequence number 0."); - return BAD_VALUE; - } - - // Send finished signals for the batch sequence chain first. - size_t seqChainCount = mSeqChains.size(); - if (seqChainCount) { - uint32_t currentSeq = seq; - uint32_t chainSeqs[seqChainCount]; - size_t chainIndex = 0; - for (size_t i = seqChainCount; i > 0; ) { - i--; - const SeqChain& seqChain = mSeqChains[i]; - if (seqChain.seq == currentSeq) { - currentSeq = seqChain.chain; - chainSeqs[chainIndex++] = currentSeq; - mSeqChains.erase(mSeqChains.begin() + i); - } - } - status_t status = OK; - while (!status && chainIndex > 0) { - chainIndex--; - status = sendUnchainedFinishedSignal(chainSeqs[chainIndex], handled); - } - if (status) { - // An error occurred so at least one signal was not sent, reconstruct the chain. - for (;;) { - SeqChain seqChain; - seqChain.seq = chainIndex != 0 ? chainSeqs[chainIndex - 1] : seq; - seqChain.chain = chainSeqs[chainIndex]; - mSeqChains.push_back(seqChain); - if (!chainIndex) break; - chainIndex--; - } - return status; - } - } - - // Send finished signal for the last message in the batch. - return sendUnchainedFinishedSignal(seq, handled); -} - -status_t InputConsumer::sendTimeline(int32_t inputEventId, - std::array graphicsTimeline) { - ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, - "channel '%s' consumer ~ sendTimeline: inputEventId=%" PRId32 - ", gpuCompletedTime=%" PRId64 ", presentTime=%" PRId64, - mChannel->getName().c_str(), inputEventId, - graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME], - graphicsTimeline[GraphicsTimeline::PRESENT_TIME]); - - InputMessage msg; - msg.header.type = InputMessage::Type::TIMELINE; - msg.header.seq = 0; - msg.body.timeline.eventId = inputEventId; - msg.body.timeline.graphicsTimeline = std::move(graphicsTimeline); - return mChannel->sendMessage(&msg); -} - -nsecs_t InputConsumer::getConsumeTime(uint32_t seq) const { - auto it = mConsumeTimes.find(seq); - // Consume time will be missing if either 'finishInputEvent' is called twice, or if it was - // called for the wrong (synthetic?) input event. Either way, it is a bug that should be fixed. - LOG_ALWAYS_FATAL_IF(it == mConsumeTimes.end(), "Could not find consume time for seq=%" PRIu32, - seq); - return it->second; -} - -void InputConsumer::popConsumeTime(uint32_t seq) { - mConsumeTimes.erase(seq); -} - -status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) { - InputMessage msg; - msg.header.type = InputMessage::Type::FINISHED; - msg.header.seq = seq; - msg.body.finished.handled = handled; - msg.body.finished.consumeTime = getConsumeTime(seq); - status_t result = mChannel->sendMessage(&msg); - if (result == OK) { - // Remove the consume time if the socket write succeeded. We will not need to ack this - // message anymore. If the socket write did not succeed, we will try again and will still - // need consume time. - popConsumeTime(seq); - - // Trace the event processing timeline - event was just finished - ATRACE_ASYNC_END("InputConsumer processing", /*cookie=*/seq); - } - return result; -} - -bool InputConsumer::hasPendingBatch() const { - return !mBatches.empty(); -} - -int32_t InputConsumer::getPendingBatchSource() const { - if (mBatches.empty()) { - return AINPUT_SOURCE_CLASS_NONE; - } - - const Batch& batch = mBatches[0]; - const InputMessage& head = batch.samples[0]; - return head.body.motion.source; -} - -bool InputConsumer::probablyHasInput() const { - return hasPendingBatch() || mChannel->probablyHasInput(); -} - -ssize_t InputConsumer::findBatch(int32_t deviceId, int32_t source) const { - for (size_t i = 0; i < mBatches.size(); i++) { - const Batch& batch = mBatches[i]; - const InputMessage& head = batch.samples[0]; - if (head.body.motion.deviceId == deviceId && head.body.motion.source == source) { - return i; - } - } - return -1; -} - -ssize_t InputConsumer::findTouchState(int32_t deviceId, int32_t source) const { - for (size_t i = 0; i < mTouchStates.size(); i++) { - const TouchState& touchState = mTouchStates[i]; - if (touchState.deviceId == deviceId && touchState.source == source) { - return i; - } - } - return -1; -} - -bool InputConsumer::canAddSample(const Batch& batch, const InputMessage *msg) { - const InputMessage& head = batch.samples[0]; - uint32_t pointerCount = msg->body.motion.pointerCount; - if (head.body.motion.pointerCount != pointerCount - || head.body.motion.action != msg->body.motion.action) { - return false; - } - for (size_t i = 0; i < pointerCount; i++) { - if (head.body.motion.pointers[i].properties - != msg->body.motion.pointers[i].properties) { - return false; - } - } - return true; -} - -ssize_t InputConsumer::findSampleNoLaterThan(const Batch& batch, nsecs_t time) { - size_t numSamples = batch.samples.size(); - size_t index = 0; - while (index < numSamples && batch.samples[index].body.motion.eventTime <= time) { - index += 1; - } - return ssize_t(index) - 1; -} - -std::string InputConsumer::dump() const { - std::string out; - out = out + "mResampleTouch = " + toString(mResampleTouch) + "\n"; - out = out + "mChannel = " + mChannel->getName() + "\n"; - out = out + "mMsgDeferred: " + toString(mMsgDeferred) + "\n"; - if (mMsgDeferred) { - out = out + "mMsg : " + ftl::enum_string(mMsg.header.type) + "\n"; - } - out += "Batches:\n"; - for (const Batch& batch : mBatches) { - out += " Batch:\n"; - for (const InputMessage& msg : batch.samples) { - out += android::base::StringPrintf(" Message %" PRIu32 ": %s ", msg.header.seq, - ftl::enum_string(msg.header.type).c_str()); - switch (msg.header.type) { - case InputMessage::Type::KEY: { - out += android::base::StringPrintf("action=%s keycode=%" PRId32, - KeyEvent::actionToString( - msg.body.key.action), - msg.body.key.keyCode); - break; - } - case InputMessage::Type::MOTION: { - out = out + "action=" + MotionEvent::actionToString(msg.body.motion.action); - for (uint32_t i = 0; i < msg.body.motion.pointerCount; i++) { - const float x = msg.body.motion.pointers[i].coords.getX(); - const float y = msg.body.motion.pointers[i].coords.getY(); - out += android::base::StringPrintf("\n Pointer %" PRIu32 - " : x=%.1f y=%.1f", - i, x, y); - } - break; - } - case InputMessage::Type::FINISHED: { - out += android::base::StringPrintf("handled=%s, consumeTime=%" PRId64, - toString(msg.body.finished.handled), - msg.body.finished.consumeTime); - break; - } - case InputMessage::Type::FOCUS: { - out += android::base::StringPrintf("hasFocus=%s", - toString(msg.body.focus.hasFocus)); - break; - } - case InputMessage::Type::CAPTURE: { - out += android::base::StringPrintf("hasCapture=%s", - toString(msg.body.capture - .pointerCaptureEnabled)); - break; - } - case InputMessage::Type::DRAG: { - out += android::base::StringPrintf("x=%.1f y=%.1f, isExiting=%s", - msg.body.drag.x, msg.body.drag.y, - toString(msg.body.drag.isExiting)); - break; - } - case InputMessage::Type::TIMELINE: { - const nsecs_t gpuCompletedTime = - msg.body.timeline - .graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME]; - const nsecs_t presentTime = - msg.body.timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME]; - out += android::base::StringPrintf("inputEventId=%" PRId32 - ", gpuCompletedTime=%" PRId64 - ", presentTime=%" PRId64, - msg.body.timeline.eventId, gpuCompletedTime, - presentTime); - break; - } - case InputMessage::Type::TOUCH_MODE: { - out += android::base::StringPrintf("isInTouchMode=%s", - toString(msg.body.touchMode.isInTouchMode)); - break; - } - } - out += "\n"; - } - } - if (mBatches.empty()) { - out += " \n"; - } - out += "mSeqChains:\n"; - for (const SeqChain& chain : mSeqChains) { - out += android::base::StringPrintf(" chain: seq = %" PRIu32 " chain=%" PRIu32, chain.seq, - chain.chain); - } - if (mSeqChains.empty()) { - out += " \n"; - } - out += "mConsumeTimes:\n"; - for (const auto& [seq, consumeTime] : mConsumeTimes) { - out += android::base::StringPrintf(" seq = %" PRIu32 " consumeTime = %" PRId64, seq, - consumeTime); - } - if (mConsumeTimes.empty()) { - out += " \n"; - } - return out; -} - } // namespace android diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index b5fab496e2..332831febb 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include using android::base::Result; diff --git a/libs/input/tests/TouchResampling_test.cpp b/libs/input/tests/TouchResampling_test.cpp index 0b0bb63a81..6e23d4e910 100644 --- a/libs/input/tests/TouchResampling_test.cpp +++ b/libs/input/tests/TouchResampling_test.cpp @@ -19,6 +19,7 @@ #include #include +#include #include using namespace std::chrono_literals; diff --git a/services/inputflinger/tests/FakeWindowHandle.h b/services/inputflinger/tests/FakeWindowHandle.h index fe25130bd8..8ce61e7800 100644 --- a/services/inputflinger/tests/FakeWindowHandle.h +++ b/services/inputflinger/tests/FakeWindowHandle.h @@ -17,6 +17,7 @@ #pragma once #include +#include #include "../dispatcher/InputDispatcher.h" using android::base::Result; diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index a662198351..423975f75b 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3-59-g8ed1b From 99280991b707a51e575c11b606426d4bc96de7f2 Mon Sep 17 00:00:00 2001 From: Chavi Weingarten Date: Mon, 4 Mar 2024 22:49:22 +0000 Subject: Add surface_control_input_receiver native API Bug: 324271765 Test: ASurfaceControlInputReceiverTest Change-Id: I19a1c796c1d22e8b247368df07c34c1b4b195e64 --- include/android/surface_control_input_receiver.h | 194 +++++++++++++++++++++++ libs/gui/Choreographer.cpp | 4 + libs/gui/include/gui/Choreographer.h | 1 + 3 files changed, 199 insertions(+) create mode 100644 include/android/surface_control_input_receiver.h (limited to 'libs/gui') diff --git a/include/android/surface_control_input_receiver.h b/include/android/surface_control_input_receiver.h new file mode 100644 index 0000000000..cd2c5dfbcf --- /dev/null +++ b/include/android/surface_control_input_receiver.h @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include + +__BEGIN_DECLS + +/** + * The AInputReceiver_onMotionEvent callback is invoked when the registered input channel receives + * a motion event. + * + * \param context Optional context provided by the client that is passed when creating the + * AInputReceiverCallbacks. + * + * \param motionEvent The motion event. This must be released with AInputEvent_release. + * + * Available since API level 35. + */ +typedef bool (*AInputReceiver_onMotionEvent)(void *_Null_unspecified context, + AInputEvent *_Nonnull motionEvent) + __INTRODUCED_IN(__ANDROID_API_V__); +/** + * The AInputReceiver_onKeyEvent callback is invoked when the registered input channel receives + * a key event. + * + * \param context Optional context provided by the client that is passed when creating the + * AInputReceiverCallbacks. + * + * \param keyEvent The key event. This must be released with AInputEvent_release. + * + * Available since API level 35. + */ +typedef bool (*AInputReceiver_onKeyEvent)(void *_Null_unspecified context, + AInputEvent *_Nonnull keyEvent) + __INTRODUCED_IN(__ANDROID_API_V__); + +struct AInputReceiverCallbacks; + +struct AInputReceiver; + +/** + * The InputReceiver that holds the reference to the registered input channel. This must be released + * using AInputReceiver_release + * + * Available since API level 35. + */ +typedef struct AInputReceiver AInputReceiver __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Registers an input receiver for an ASurfaceControl that will receive batched input event. For + * those events that are batched, the invocation will happen once per AChoreographer frame, and + * other input events will be delivered immediately. + * + * This is different from AInputReceiver_createUnbatchedInputReceiver in that the input events are + * received batched. The caller must invoke AInputReceiver_release to cleanv up the resources when + * no longer needing to use the input receiver. + * + * \param aChoreographer The AChoreographer used for batching. This should match the + * rendering AChoreographer. + * \param hostInputTransferToken The host token to link the embedded. This is used to handle + * transferring touch gesture from host to embedded and for ANRs + * to ensure the host receives the ANR if any issues with + * touch on the embedded. This can be retrieved for the host window + * by calling AttachedSurfaceControl#getInputTransferToken() + * \param aSurfaceControl The ASurfaceControl to register the InputChannel for + * \param aInputReceiverCallbacks The SurfaceControlInputReceiver that will receive the input events + * + * Returns the reference to AInputReceiver to clean up resources when done. + * + * Available since API level 35. + */ +AInputReceiver* _Nonnull +AInputReceiver_createBatchedInputReceiver(AChoreographer* _Nonnull aChoreographer, + const AInputTransferToken* _Nonnull hostInputTransferToken, + const ASurfaceControl* _Nonnull aSurfaceControl, + AInputReceiverCallbacks* _Nonnull aInputReceiverCallbacks) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Registers an input receiver for an ASurfaceControl that will receive every input event. + * This is different from AInputReceiver_createBatchedInputReceiver in that the input events are + * received unbatched. The caller must invoke AInputReceiver_release to clean up the resources when + * no longer needing to use the input receiver. + * + * \param aLooper The looper to use when invoking callbacks. + * \param hostInputTransferToken The host token to link the embedded. This is used to handle + * transferring touch gesture from host to embedded and for ANRs + * to ensure the host receives the ANR if any issues with + * touch on the embedded. This can be retrieved for the host window + * by calling AttachedSurfaceControl#getInputTransferToken() + * \param aSurfaceControl The ASurfaceControl to register the InputChannel for + * \param aInputReceiverCallbacks The SurfaceControlInputReceiver that will receive the input events + * + * Returns the reference to AInputReceiver to clean up resources when done. + * + * Available since API level 35. + */ +AInputReceiver* _Nonnull +AInputReceiver_createUnbatchedInputReceiver(ALooper* _Nonnull aLooper, + const AInputTransferToken* _Nonnull hostInputTransferToken, + const ASurfaceControl* _Nonnull aSurfaceControl, + AInputReceiverCallbacks* _Nonnull aInputReceiverCallbacks) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Returns the AInputTransferToken that can be used to transfer touch gesture to or from other + * windows. This InputTransferToken is associated with the SurfaceControl that registered an input + * receiver and can be used with the host token for things like transfer touch gesture via + * WindowManager#transferTouchGesture(). + * + * This must be released with AInputTransferToken_release. + * + * \param aInputReceiver The inputReceiver object to retrieve the AInputTransferToken for. + * + * Available since API level 35. + */ +const AInputTransferToken *_Nonnull +AInputReceiver_getInputTransferToken(AInputReceiver *_Nonnull aInputReceiver) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Unregisters the input channel and deletes the AInputReceiver. This must be called on the same + * looper thread it was created with. + * + * \param aInputReceiver The inputReceiver object to release. + * + * Available since API level 35. + */ +void +AInputReceiver_release(AInputReceiver *_Nonnull aInputReceiver) __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Creates a AInputReceiverCallbacks object that is used when registering for an AInputReceiver. + * This must be released using AInputReceiverCallbacks_release + * + * \param context Optional context provided by the client that will be passed into the callbacks. + * + * Available since API level 35. + */ +AInputReceiverCallbacks* _Nonnull AInputReceiverCallbacks_create(void* _Nullable context) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Releases the AInputReceiverCallbacks. This must be called on the same + * looper thread the AInputReceiver was created with. The receiver will not invoke any callbacks + * once it's been released. + * + * Available since API level 35 + */ +void AInputReceiverCallbacks_release(AInputReceiverCallbacks* _Nonnull callbacks) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Sets a AInputReceiver_onMotionEvent callback for an AInputReceiverCallbacks + * + * \param callbacks The callback object to set the motion event on. + * \param onMotionEvent The motion event that will be invoked + * + * Available since API level 35. + */ +void AInputReceiverCallbacks_setMotionEventCallback(AInputReceiverCallbacks* _Nonnull callbacks, + AInputReceiver_onMotionEvent _Nonnull onMotionEvent) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Sets a AInputReceiver_onKeyEvent callback for an AInputReceiverCallbacks + * + * \param callbacks The callback object to set the motion event on. + * \param onMotionEvent The key event that will be invoked + * + * Available since API level 35. + */ +void AInputReceiverCallbacks_setKeyEventCallback(AInputReceiverCallbacks* _Nonnull callbacks, + AInputReceiver_onKeyEvent _Nonnull onKeyEvent) + __INTRODUCED_IN(__ANDROID_API_V__); + +__END_DECLS diff --git a/libs/gui/Choreographer.cpp b/libs/gui/Choreographer.cpp index 54290cd629..0c8f3fa096 100644 --- a/libs/gui/Choreographer.cpp +++ b/libs/gui/Choreographer.cpp @@ -425,4 +425,8 @@ int64_t Choreographer::getStartTimeNanosForVsyncId(AVsyncId vsyncId) { return iter->second; } +const sp Choreographer::getLooper() { + return mLooper; +} + } // namespace android diff --git a/libs/gui/include/gui/Choreographer.h b/libs/gui/include/gui/Choreographer.h index fc79b03c23..2e5aa4a893 100644 --- a/libs/gui/include/gui/Choreographer.h +++ b/libs/gui/include/gui/Choreographer.h @@ -109,6 +109,7 @@ public: virtual ~Choreographer() override EXCLUDES(gChoreographers.lock); int64_t getFrameInterval() const; bool inCallback() const; + const sp getLooper(); private: Choreographer(const Choreographer&) = delete; -- cgit v1.2.3-59-g8ed1b From 47504789e2d22b4ef5d98700992b0243e0cc8922 Mon Sep 17 00:00:00 2001 From: Steven Moreland Date: Fri, 8 Mar 2024 23:28:32 +0000 Subject: libgui: avoid allocations for bit manip Avoid doing allocations when getting layer metadata attributes. Bug: 328177618 Test: TH Change-Id: I63189bce152bd275bd46d9d2cdd4d7d02d282533 --- libs/gui/LayerMetadata.cpp | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'libs/gui') diff --git a/libs/gui/LayerMetadata.cpp b/libs/gui/LayerMetadata.cpp index 4e12fd330c..535a0218b6 100644 --- a/libs/gui/LayerMetadata.cpp +++ b/libs/gui/LayerMetadata.cpp @@ -100,27 +100,31 @@ bool LayerMetadata::has(uint32_t key) const { int32_t LayerMetadata::getInt32(uint32_t key, int32_t fallback) const { if (!has(key)) return fallback; const std::vector& data = mMap.at(key); - if (data.size() < sizeof(uint32_t)) return fallback; - Parcel p; - p.setData(data.data(), data.size()); - return p.readInt32(); + + // TODO: should handle when not equal? + if (data.size() < sizeof(int32_t)) return fallback; + + int32_t result; + memcpy(&result, data.data(), sizeof(result)); + return result; } void LayerMetadata::setInt32(uint32_t key, int32_t value) { std::vector& data = mMap[key]; - Parcel p; - p.writeInt32(value); - data.resize(p.dataSize()); - memcpy(data.data(), p.data(), p.dataSize()); + data.resize(sizeof(value)); + memcpy(data.data(), &value, sizeof(value)); } std::optional LayerMetadata::getInt64(uint32_t key) const { if (!has(key)) return std::nullopt; const std::vector& data = mMap.at(key); + + // TODO: should handle when not equal? if (data.size() < sizeof(int64_t)) return std::nullopt; - Parcel p; - p.setData(data.data(), data.size()); - return p.readInt64(); + + int64_t result; + memcpy(&result, data.data(), sizeof(result)); + return result; } std::string LayerMetadata::itemToString(uint32_t key, const char* separator) const { -- cgit v1.2.3-59-g8ed1b From 682820d1c9e6f9c41d83bd8fa08614216eca9c20 Mon Sep 17 00:00:00 2001 From: Rupesh Bansal Date: Wed, 13 Mar 2024 12:58:06 +0000 Subject: Added idlescreen timeout Bug: 310026579 Test: atest DisplayServicesTest Change-Id: I19cb005c6fc790e5e0b65833d7b7984b46c25e71 --- libs/gui/aidl/android/gui/DisplayModeSpecs.aidl | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'libs/gui') diff --git a/libs/gui/aidl/android/gui/DisplayModeSpecs.aidl b/libs/gui/aidl/android/gui/DisplayModeSpecs.aidl index af138c7539..13962fee5d 100644 --- a/libs/gui/aidl/android/gui/DisplayModeSpecs.aidl +++ b/libs/gui/aidl/android/gui/DisplayModeSpecs.aidl @@ -41,6 +41,17 @@ parcelable DisplayModeSpecs { RefreshRateRange render; } + /** + * Refers to the time after which the idle screen's refresh rate is to be reduced + */ + parcelable IdleScreenRefreshRateConfig { + + /** + * The timeout value in milli seconds + */ + int timeoutMillis; + } + /** * Base mode ID. This is what system defaults to for all other settings, or * if the refresh rate range is not available. @@ -72,4 +83,13 @@ parcelable DisplayModeSpecs { * never smaller. */ RefreshRateRanges appRequestRanges; + + /** + * The config to represent the maximum time (in ms) for which the display can remain in an idle + * state before reducing the refresh rate to conserve power. + * Null value refers that the device is not configured to dynamically reduce the refresh rate + * based on external conditions. + * -1 refers to the current conditions requires no timeout + */ + @nullable IdleScreenRefreshRateConfig idleScreenRefreshRateConfig; } -- cgit v1.2.3-59-g8ed1b From bbf362d24642ed22fd7d19f428125c8bd9e3f170 Mon Sep 17 00:00:00 2001 From: Melody Hsu Date: Wed, 20 Mar 2024 21:59:06 +0000 Subject: Delete border rendering code from SurfaceFlinger. Removed code is never used and drawing borders is done instead by Window Manager Service. Changes revert ag/16980603 and ag/17496275. Bug: b/227656283 Test: presubmit Test: SurfaceFlinger_test Change-Id: Ib5c8bf74ad6764d65536dc60cc3c458edde86b3f --- libs/gui/LayerState.cpp | 25 -- libs/gui/SurfaceComposerClient.cpp | 17 -- libs/gui/include/gui/LayerState.h | 10 +- libs/gui/include/gui/SurfaceComposerClient.h | 3 - .../include/renderengine/BorderRenderInfo.h | 36 --- .../include/renderengine/DisplaySettings.h | 6 +- libs/renderengine/skia/SkiaRenderEngine.cpp | 19 -- libs/renderengine/tests/RenderEngineTest.cpp | 45 ---- .../compositionengine/CompositionRefreshArgs.h | 8 - .../include/compositionengine/impl/Output.h | 1 - .../impl/OutputCompositionState.h | 3 - .../CompositionEngine/src/Output.cpp | 45 ---- .../src/OutputCompositionState.cpp | 5 - services/surfaceflinger/Layer.cpp | 23 -- services/surfaceflinger/Layer.h | 8 - services/surfaceflinger/SurfaceFlinger.cpp | 24 -- services/surfaceflinger/tests/Android.bp | 1 - services/surfaceflinger/tests/LayerBorder_test.cpp | 287 --------------------- services/surfaceflinger/tests/utils/ColorUtils.h | 12 - 19 files changed, 3 insertions(+), 575 deletions(-) delete mode 100644 libs/renderengine/include/renderengine/BorderRenderInfo.h delete mode 100644 services/surfaceflinger/tests/LayerBorder_test.cpp (limited to 'libs/gui') diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 1e0aacddab..0a2879975b 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -90,7 +90,6 @@ layer_state_t::layer_state_t() fixedTransformHint(ui::Transform::ROT_INVALID), autoRefresh(false), isTrustedOverlay(false), - borderEnabled(false), bufferCrop(Rect::INVALID_RECT), destinationFrame(Rect::INVALID_RECT), dropInputMode(gui::DropInputMode::NONE) { @@ -122,12 +121,6 @@ status_t layer_state_t::write(Parcel& output) const SAFE_PARCEL(output.write, transparentRegion); SAFE_PARCEL(output.writeUint32, bufferTransform); SAFE_PARCEL(output.writeBool, transformToDisplayInverse); - SAFE_PARCEL(output.writeBool, borderEnabled); - SAFE_PARCEL(output.writeFloat, borderWidth); - SAFE_PARCEL(output.writeFloat, borderColor.r); - SAFE_PARCEL(output.writeFloat, borderColor.g); - SAFE_PARCEL(output.writeFloat, borderColor.b); - SAFE_PARCEL(output.writeFloat, borderColor.a); SAFE_PARCEL(output.writeUint32, static_cast(dataspace)); SAFE_PARCEL(output.write, hdrMetadata); SAFE_PARCEL(output.write, surfaceDamageRegion); @@ -238,17 +231,6 @@ status_t layer_state_t::read(const Parcel& input) SAFE_PARCEL(input.read, transparentRegion); SAFE_PARCEL(input.readUint32, &bufferTransform); SAFE_PARCEL(input.readBool, &transformToDisplayInverse); - SAFE_PARCEL(input.readBool, &borderEnabled); - SAFE_PARCEL(input.readFloat, &tmpFloat); - borderWidth = tmpFloat; - SAFE_PARCEL(input.readFloat, &tmpFloat); - borderColor.r = tmpFloat; - SAFE_PARCEL(input.readFloat, &tmpFloat); - borderColor.g = tmpFloat; - SAFE_PARCEL(input.readFloat, &tmpFloat); - borderColor.b = tmpFloat; - SAFE_PARCEL(input.readFloat, &tmpFloat); - borderColor.a = tmpFloat; uint32_t tmpUint32 = 0; SAFE_PARCEL(input.readUint32, &tmpUint32); @@ -659,12 +641,6 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eShadowRadiusChanged; shadowRadius = other.shadowRadius; } - if (other.what & eRenderBorderChanged) { - what |= eRenderBorderChanged; - borderEnabled = other.borderEnabled; - borderWidth = other.borderWidth; - borderColor = other.borderColor; - } if (other.what & eDefaultFrameRateCompatibilityChanged) { what |= eDefaultFrameRateCompatibilityChanged; defaultFrameRateCompatibility = other.defaultFrameRateCompatibility; @@ -794,7 +770,6 @@ uint64_t layer_state_t::diff(const layer_state_t& other) const { CHECK_DIFF2(diff, eBackgroundColorChanged, other, bgColor, bgColorDataspace); if (other.what & eMetadataChanged) diff |= eMetadataChanged; CHECK_DIFF(diff, eShadowRadiusChanged, other, shadowRadius); - CHECK_DIFF3(diff, eRenderBorderChanged, other, borderEnabled, borderWidth, borderColor); CHECK_DIFF(diff, eDefaultFrameRateCompatibilityChanged, other, defaultFrameRateCompatibility); CHECK_DIFF(diff, eFrameRateSelectionPriority, other, frameRateSelectionPriority); CHECK_DIFF3(diff, eFrameRateChanged, other, frameRate, frameRateCompatibility, diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 4f1356bebb..1d2ea3ea02 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -2250,23 +2250,6 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDropI return *this; } -SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::enableBorder( - const sp& sc, bool shouldEnable, float width, const half4& color) { - layer_state_t* s = getLayerState(sc); - if (!s) { - mStatus = BAD_INDEX; - return *this; - } - - s->what |= layer_state_t::eRenderBorderChanged; - s->borderEnabled = shouldEnable; - s->borderWidth = width; - s->borderColor = color; - - registerSurfaceControlForCallback(sc); - return *this; -} - // --------------------------------------------------------------------------- DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp& token) { diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index 0fedea7b9e..ca7acf9be4 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -179,7 +179,6 @@ struct layer_state_t { eCachingHintChanged = 0x00000200, eDimmingEnabledChanged = 0x00000400, eShadowRadiusChanged = 0x00000800, - eRenderBorderChanged = 0x00001000, eBufferCropChanged = 0x00002000, eRelativeLayerChanged = 0x00004000, eReparent = 0x00008000, @@ -258,8 +257,8 @@ struct layer_state_t { layer_state_t::eBlurRegionsChanged | layer_state_t::eColorChanged | layer_state_t::eColorSpaceAgnosticChanged | layer_state_t::eColorTransformChanged | layer_state_t::eCornerRadiusChanged | layer_state_t::eDimmingEnabledChanged | - layer_state_t::eHdrMetadataChanged | layer_state_t::eRenderBorderChanged | - layer_state_t::eShadowRadiusChanged | layer_state_t::eStretchChanged; + layer_state_t::eHdrMetadataChanged | layer_state_t::eShadowRadiusChanged | + layer_state_t::eStretchChanged; // Changes which invalidates the layer's visible region in CE. static constexpr uint64_t CONTENT_DIRTY = layer_state_t::CONTENT_CHANGES | @@ -388,11 +387,6 @@ struct layer_state_t { // should be trusted for input occlusion detection purposes bool isTrustedOverlay; - // Flag to indicate if border needs to be enabled on the layer - bool borderEnabled; - float borderWidth; - half4 borderColor; - // Stretch effect to be applied to this layer StretchEffect stretchEffect; diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 288882695a..79224e6782 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -744,9 +744,6 @@ public: const Rect& destinationFrame); Transaction& setDropInputMode(const sp& sc, gui::DropInputMode mode); - Transaction& enableBorder(const sp& sc, bool shouldEnable, float width, - const half4& color); - status_t setDisplaySurface(const sp& token, const sp& bufferProducer); diff --git a/libs/renderengine/include/renderengine/BorderRenderInfo.h b/libs/renderengine/include/renderengine/BorderRenderInfo.h deleted file mode 100644 index 0ee6661f33..0000000000 --- a/libs/renderengine/include/renderengine/BorderRenderInfo.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 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 -#include - -namespace android { -namespace renderengine { - -struct BorderRenderInfo { - float width = 0; - half4 color; - Region combinedRegion; - - bool operator==(const BorderRenderInfo& rhs) const { - return (width == rhs.width && color == rhs.color && - combinedRegion.hasSameRects(rhs.combinedRegion)); - } -}; - -} // namespace renderengine -} // namespace android \ No newline at end of file diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h index 8d7c13cb18..deb62534a5 100644 --- a/libs/renderengine/include/renderengine/DisplaySettings.h +++ b/libs/renderengine/include/renderengine/DisplaySettings.h @@ -22,7 +22,6 @@ #include #include -#include #include #include #include @@ -87,8 +86,6 @@ struct DisplaySettings { // Configures the rendering intent of the output display. This is used for tonemapping. aidl::android::hardware::graphics::composer3::RenderIntent renderIntent = aidl::android::hardware::graphics::composer3::RenderIntent::TONE_MAP_COLORIMETRIC; - - std::vector borderInfoList; }; static inline bool operator==(const DisplaySettings& lhs, const DisplaySettings& rhs) { @@ -100,8 +97,7 @@ static inline bool operator==(const DisplaySettings& lhs, const DisplaySettings& lhs.deviceHandlesColorTransform == rhs.deviceHandlesColorTransform && lhs.orientation == rhs.orientation && lhs.targetLuminanceNits == rhs.targetLuminanceNits && - lhs.dimmingStage == rhs.dimmingStage && lhs.renderIntent == rhs.renderIntent && - lhs.borderInfoList == rhs.borderInfoList; + lhs.dimmingStage == rhs.dimmingStage && lhs.renderIntent == rhs.renderIntent; } static const char* orientation_to_string(uint32_t orientation) { diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp index 9e8fe6802d..c12c119bfd 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaRenderEngine.cpp @@ -1127,25 +1127,6 @@ void SkiaRenderEngine::drawLayersInternal( skgpu::ganesh::Flush(activeSurface); } } - for (const auto& borderRenderInfo : display.borderInfoList) { - SkPaint p; - p.setColor(SkColor4f{borderRenderInfo.color.r, borderRenderInfo.color.g, - borderRenderInfo.color.b, borderRenderInfo.color.a}); - p.setAntiAlias(true); - p.setStyle(SkPaint::kStroke_Style); - p.setStrokeWidth(borderRenderInfo.width); - SkRegion sk_region; - SkPath path; - - // Construct a final SkRegion using Regions - for (const auto& r : borderRenderInfo.combinedRegion) { - sk_region.op({r.left, r.top, r.right, r.bottom}, SkRegion::kUnion_Op); - } - - sk_region.getBoundaryPath(&path); - canvas->drawPath(path, p); - path.close(); - } surfaceAutoSaveRestore.restore(); mCapture->endCapture(); diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp index 7b8eb8470f..3a07205ea6 100644 --- a/libs/renderengine/tests/RenderEngineTest.cpp +++ b/libs/renderengine/tests/RenderEngineTest.cpp @@ -2490,51 +2490,6 @@ TEST_P(RenderEngineTest, testDisableBlendingBuffer) { expectBufferColor(rect, 0, 128, 0, 128); } -TEST_P(RenderEngineTest, testBorder) { - if (!GetParam()->apiSupported()) { - GTEST_SKIP(); - } - - initializeRenderEngine(); - - const ui::Dataspace dataspace = ui::Dataspace::V0_SRGB; - - const auto displayRect = Rect(1080, 2280); - renderengine::DisplaySettings display{ - .physicalDisplay = displayRect, - .clip = displayRect, - .outputDataspace = dataspace, - }; - display.borderInfoList.clear(); - renderengine::BorderRenderInfo info; - info.combinedRegion = Region(Rect(99, 99, 199, 199)); - info.width = 20.0f; - info.color = half4{1.0f, 128.0f / 255.0f, 0.0f, 1.0f}; - display.borderInfoList.emplace_back(info); - - const auto greenBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(0, 255, 0, 255)); - const renderengine::LayerSettings greenLayer{ - .geometry.boundaries = FloatRect(0.f, 0.f, 1.f, 1.f), - .source = - renderengine::PixelSource{ - .buffer = - renderengine::Buffer{ - .buffer = greenBuffer, - .usePremultipliedAlpha = true, - }, - }, - .alpha = 1.0f, - .sourceDataspace = dataspace, - .whitePointNits = 200.f, - }; - - std::vector layers; - layers.emplace_back(greenLayer); - invokeDraw(display, layers); - - expectBufferColor(Rect(99, 99, 101, 101), 255, 128, 0, 255, 1); -} - TEST_P(RenderEngineTest, testDimming) { if (!GetParam()->apiSupported()) { GTEST_SKIP(); diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h index 843b5c5c82..dd0f985e1c 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h @@ -34,12 +34,6 @@ namespace android::compositionengine { using Layers = std::vector>; using Outputs = std::vector>; -struct BorderRenderInfo { - float width = 0; - half4 color; - std::vector layerIds; -}; - // Interface of composition engine power hint callback. struct ICEPowerCallback { virtual void notifyCpuLoadUp() = 0; @@ -101,8 +95,6 @@ struct CompositionRefreshArgs { // TODO (b/255601557): Calculate per display. std::optional scheduledFrameTime; - std::vector borderInfoList; - bool hasTrustedPresentationListener = false; ICEPowerCallback* powerCallback = nullptr; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h index 911d67b5ed..b2ef9191ba 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h @@ -162,7 +162,6 @@ protected: private: void dirtyEntireOutput(); - void updateCompositionStateForBorder(const compositionengine::CompositionRefreshArgs&); compositionengine::OutputLayer* findLayerRequestingBackgroundComposition() const; void finishPrepareFrame(); ui::Dataspace getBestDataspace(ui::Dataspace*, bool*) const; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h index 6b1c318d1c..f8ffde1e51 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h @@ -34,7 +34,6 @@ #include #include -#include #include #include #include @@ -166,8 +165,6 @@ struct OutputCompositionState { bool treat170mAsSrgb = false; - std::vector borderInfoList; - uint64_t lastOutputLayerHash = 0; uint64_t outputLayerHash = 0; diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index 921e05dfb2..0935a67f9b 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -818,44 +818,6 @@ void Output::updateCompositionState(const compositionengine::CompositionRefreshA forceClientComposition = false; } } - - updateCompositionStateForBorder(refreshArgs); -} - -void Output::updateCompositionStateForBorder( - const compositionengine::CompositionRefreshArgs& refreshArgs) { - std::unordered_map layerVisibleRegionMap; - // Store a map of layerId to their computed visible region. - for (auto* layer : getOutputLayersOrderedByZ()) { - int layerId = (layer->getLayerFE()).getSequence(); - layerVisibleRegionMap[layerId] = &((layer->getState()).visibleRegion); - } - OutputCompositionState& outputCompositionState = editState(); - outputCompositionState.borderInfoList.clear(); - bool clientComposeTopLayer = false; - for (const auto& borderInfo : refreshArgs.borderInfoList) { - renderengine::BorderRenderInfo info; - for (const auto& id : borderInfo.layerIds) { - info.combinedRegion.orSelf(*(layerVisibleRegionMap[id])); - } - - if (!info.combinedRegion.isEmpty()) { - info.width = borderInfo.width; - info.color = borderInfo.color; - outputCompositionState.borderInfoList.emplace_back(std::move(info)); - clientComposeTopLayer = true; - } - } - - // In this situation we must client compose the top layer instead of using hwc - // because we want to draw the border above all else. - // This could potentially cause a bit of a performance regression if the top - // layer would have been rendered using hwc originally. - // TODO(b/227656283): Measure system's performance before enabling the border feature - if (clientComposeTopLayer) { - auto topLayer = getOutputLayerOrderedByZByIndex(getOutputLayerCount() - 1); - (topLayer->editState()).forceClientComposition = true; - } } void Output::planComposition() { @@ -1443,13 +1405,6 @@ renderengine::DisplaySettings Output::generateClientCompositionDisplaySettings( // Compute the global color transform matrix. clientCompositionDisplay.colorTransform = outputState.colorTransformMatrix; - for (auto& info : outputState.borderInfoList) { - renderengine::BorderRenderInfo borderInfo; - borderInfo.width = info.width; - borderInfo.color = info.color; - borderInfo.combinedRegion = info.combinedRegion; - clientCompositionDisplay.borderInfoList.emplace_back(std::move(borderInfo)); - } clientCompositionDisplay.deviceHandlesColorTransform = outputState.usesDeviceComposition || getSkipColorTransform(); return clientCompositionDisplay; diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp index 39cf67165a..6683e67029 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp @@ -67,11 +67,6 @@ void OutputCompositionState::dump(std::string& out) const { out.append("\n "); dumpVal(out, "treat170mAsSrgb", treat170mAsSrgb); - - out.append("\n"); - for (const auto& borderRenderInfo : borderInfoList) { - dumpVal(out, "borderRegion", borderRenderInfo.combinedRegion); - } out.append("\n"); } diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 736fec6fb2..4606e43691 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -150,7 +150,6 @@ Layer::Layer(const surfaceflinger::LayerCreationArgs& args) mWindowType(static_cast( args.metadata.getInt32(gui::METADATA_WINDOW_TYPE, 0))), mLayerCreationFlags(args.flags), - mBorderEnabled(false), mLegacyLayerFE(args.flinger->getFactory().createLayerFE(mName)) { ALOGV("Creating Layer %s", getDebugName()); @@ -1235,28 +1234,6 @@ StretchEffect Layer::getStretchEffect() const { return StretchEffect{}; } -bool Layer::enableBorder(bool shouldEnable, float width, const half4& color) { - if (mBorderEnabled == shouldEnable && mBorderWidth == width && mBorderColor == color) { - return false; - } - mBorderEnabled = shouldEnable; - mBorderWidth = width; - mBorderColor = color; - return true; -} - -bool Layer::isBorderEnabled() { - return mBorderEnabled; -} - -float Layer::getBorderWidth() { - return mBorderWidth; -} - -const half4& Layer::getBorderColor() { - return mBorderColor; -} - bool Layer::propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool overrideChildren, bool* transactionNeeded) { // Gets the frame rate to propagate to children. diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 0ceecec7ec..b5d7075574 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -874,10 +874,6 @@ public: bool setStretchEffect(const StretchEffect& effect); StretchEffect getStretchEffect() const; - bool enableBorder(bool shouldEnable, float width, const half4& color); - bool isBorderEnabled(); - float getBorderWidth(); - const half4& getBorderColor(); bool setBufferCrop(const Rect& /* bufferCrop */); bool setDestinationFrame(const Rect& /* destinationFrame */); @@ -1238,10 +1234,6 @@ private: bool findInHierarchy(const sp&); - bool mBorderEnabled = false; - float mBorderWidth; - half4 mBorderColor; - void setTransformHintLegacy(ui::Transform::RotationFlags); void releasePreviousBuffer(); void resetDrawingStateBufferInfo(); diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 9d07671232..b1bb576586 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -167,10 +167,6 @@ #define NO_THREAD_SAFETY_ANALYSIS \ _Pragma("GCC error \"Prefer or MutexUtils.h helpers.\"") -// To enable layer borders in the system, change the below flag to true. -#undef DOES_CONTAIN_BORDER -#define DOES_CONTAIN_BORDER false - namespace android { using namespace std::chrono_literals; using namespace std::string_literals; @@ -2702,21 +2698,6 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( mLayerMetadataSnapshotNeeded = false; } - if (DOES_CONTAIN_BORDER) { - refreshArgs.borderInfoList.clear(); - mDrawingState.traverse([&refreshArgs](Layer* layer) { - if (layer->isBorderEnabled()) { - compositionengine::BorderRenderInfo info; - info.width = layer->getBorderWidth(); - info.color = layer->getBorderColor(); - layer->traverse(LayerVector::StateSet::Drawing, [&info](Layer* ilayer) { - info.layerIds.push_back(ilayer->getSequence()); - }); - refreshArgs.borderInfoList.emplace_back(std::move(info)); - } - }); - } - refreshArgs.bufferIdsToUncache = std::move(mBufferIdsToUncache); refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size()); @@ -5461,11 +5442,6 @@ uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTime if (what & layer_state_t::eBlurRegionsChanged) { if (layer->setBlurRegions(s.blurRegions)) flags |= eTraversalNeeded; } - if (what & layer_state_t::eRenderBorderChanged) { - if (layer->enableBorder(s.borderEnabled, s.borderWidth, s.borderColor)) { - flags |= eTraversalNeeded; - } - } if (what & layer_state_t::eLayerStackChanged) { ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer); // We only allow setting layer stacks for top level layers, diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp index 1fd7ae06c0..988d26926d 100644 --- a/services/surfaceflinger/tests/Android.bp +++ b/services/surfaceflinger/tests/Android.bp @@ -42,7 +42,6 @@ cc_test { "EffectLayer_test.cpp", "HdrSdrRatioOverlay_test.cpp", "InvalidHandles_test.cpp", - "LayerBorder_test.cpp", "LayerCallback_test.cpp", "LayerRenderTypeTransaction_test.cpp", "LayerState_test.cpp", diff --git a/services/surfaceflinger/tests/LayerBorder_test.cpp b/services/surfaceflinger/tests/LayerBorder_test.cpp deleted file mode 100644 index 00e134b4d2..0000000000 --- a/services/surfaceflinger/tests/LayerBorder_test.cpp +++ /dev/null @@ -1,287 +0,0 @@ -/* - * 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. - */ - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -// TODO: Amend all tests when screenshots become fully reworked for borders -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" - -#include // std::chrono::seconds -#include // std::this_thread::sleep_for -#include "LayerTransactionTest.h" - -namespace android { - -class LayerBorderTest : public LayerTransactionTest { -protected: - virtual void SetUp() { - LayerTransactionTest::SetUp(); - ASSERT_EQ(NO_ERROR, mClient->initCheck()); - - toHalf3 = ColorTransformHelper::toHalf3; - toHalf4 = ColorTransformHelper::toHalf4; - - const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); - ASSERT_FALSE(ids.empty()); - const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front()); - ASSERT_FALSE(display == nullptr); - mColorOrange = toHalf4({255, 140, 0, 255}); - mParentLayer = createColorLayer("Parent layer", Color::RED); - - mContainerLayer = mClient->createSurface(String8("Container Layer"), 0 /* width */, - 0 /* height */, PIXEL_FORMAT_RGBA_8888, - ISurfaceComposerClient::eFXSurfaceContainer | - ISurfaceComposerClient::eNoColorFill, - mParentLayer->getHandle()); - EXPECT_NE(nullptr, mContainerLayer.get()) << "failed to create container layer"; - - mEffectLayer1 = mClient->createSurface(String8("Effect Layer"), 0 /* width */, - 0 /* height */, PIXEL_FORMAT_RGBA_8888, - ISurfaceComposerClient::eFXSurfaceEffect | - ISurfaceComposerClient::eNoColorFill, - mContainerLayer->getHandle()); - EXPECT_NE(nullptr, mEffectLayer1.get()) << "failed to create effect layer 1"; - - mEffectLayer2 = mClient->createSurface(String8("Effect Layer"), 0 /* width */, - 0 /* height */, PIXEL_FORMAT_RGBA_8888, - ISurfaceComposerClient::eFXSurfaceEffect | - ISurfaceComposerClient::eNoColorFill, - mContainerLayer->getHandle()); - - EXPECT_NE(nullptr, mEffectLayer2.get()) << "failed to create effect layer 2"; - - asTransaction([&](Transaction& t) { - t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK); - t.setLayer(mParentLayer, INT32_MAX - 20).show(mParentLayer); - t.setFlags(mParentLayer, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque); - - t.setColor(mEffectLayer1, toHalf3(Color::BLUE)); - - t.setColor(mEffectLayer2, toHalf3(Color::GREEN)); - }); - } - - virtual void TearDown() { - // Uncomment the line right below when running any of the tests - // std::this_thread::sleep_for (std::chrono::seconds(30)); - LayerTransactionTest::TearDown(); - mParentLayer = 0; - } - - std::function toHalf3; - std::function toHalf4; - sp mParentLayer, mContainerLayer, mEffectLayer1, mEffectLayer2; - half4 mColorOrange; -}; - -TEST_F(LayerBorderTest, OverlappingVisibleRegions) { - asTransaction([&](Transaction& t) { - t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400)); - t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600)); - - t.enableBorder(mContainerLayer, true, 20, mColorOrange); - t.show(mEffectLayer1); - t.show(mEffectLayer2); - t.show(mContainerLayer); - }); -} - -TEST_F(LayerBorderTest, PartiallyCoveredVisibleRegion) { - asTransaction([&](Transaction& t) { - t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400)); - t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600)); - - t.enableBorder(mEffectLayer1, true, 20, mColorOrange); - t.show(mEffectLayer1); - t.show(mEffectLayer2); - t.show(mContainerLayer); - }); -} - -TEST_F(LayerBorderTest, NonOverlappingVisibleRegion) { - asTransaction([&](Transaction& t) { - t.setCrop(mEffectLayer1, Rect(0, 0, 200, 200)); - t.setCrop(mEffectLayer2, Rect(400, 400, 600, 600)); - - t.enableBorder(mContainerLayer, true, 20, mColorOrange); - t.show(mEffectLayer1); - t.show(mEffectLayer2); - t.show(mContainerLayer); - }); -} - -TEST_F(LayerBorderTest, EmptyVisibleRegion) { - asTransaction([&](Transaction& t) { - t.setCrop(mEffectLayer1, Rect(200, 200, 400, 400)); - t.setCrop(mEffectLayer2, Rect(0, 0, 600, 600)); - - t.enableBorder(mEffectLayer1, true, 20, mColorOrange); - t.show(mEffectLayer1); - t.show(mEffectLayer2); - t.show(mContainerLayer); - }); -} - -TEST_F(LayerBorderTest, ZOrderAdjustment) { - asTransaction([&](Transaction& t) { - t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400)); - t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600)); - t.setLayer(mParentLayer, 10); - t.setLayer(mEffectLayer1, 30); - t.setLayer(mEffectLayer2, 20); - - t.enableBorder(mEffectLayer1, true, 20, mColorOrange); - t.show(mEffectLayer1); - t.show(mEffectLayer2); - t.show(mContainerLayer); - }); -} - -TEST_F(LayerBorderTest, GrandChildHierarchy) { - sp containerLayer2 = - mClient->createSurface(String8("Container Layer"), 0 /* width */, 0 /* height */, - PIXEL_FORMAT_RGBA_8888, - ISurfaceComposerClient::eFXSurfaceContainer | - ISurfaceComposerClient::eNoColorFill, - mContainerLayer->getHandle()); - EXPECT_NE(nullptr, containerLayer2.get()) << "failed to create container layer 2"; - - sp effectLayer3 = - mClient->createSurface(String8("Effect Layer"), 0 /* width */, 0 /* height */, - PIXEL_FORMAT_RGBA_8888, - ISurfaceComposerClient::eFXSurfaceEffect | - ISurfaceComposerClient::eNoColorFill, - containerLayer2->getHandle()); - - asTransaction([&](Transaction& t) { - t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400)); - t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600)); - t.setCrop(effectLayer3, Rect(400, 400, 800, 800)); - t.setColor(effectLayer3, toHalf3(Color::BLUE)); - - t.enableBorder(mContainerLayer, true, 20, mColorOrange); - t.show(mEffectLayer1); - t.show(mEffectLayer2); - t.show(effectLayer3); - t.show(mContainerLayer); - }); -} - -TEST_F(LayerBorderTest, TransparentAlpha) { - asTransaction([&](Transaction& t) { - t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400)); - t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600)); - t.setAlpha(mEffectLayer1, 0.0f); - - t.enableBorder(mContainerLayer, true, 20, mColorOrange); - t.show(mEffectLayer1); - t.show(mEffectLayer2); - t.show(mContainerLayer); - }); -} - -TEST_F(LayerBorderTest, SemiTransparentAlpha) { - asTransaction([&](Transaction& t) { - t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400)); - t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600)); - t.setAlpha(mEffectLayer2, 0.5f); - - t.enableBorder(mEffectLayer2, true, 20, mColorOrange); - t.show(mEffectLayer1); - t.show(mEffectLayer2); - t.show(mContainerLayer); - }); -} - -TEST_F(LayerBorderTest, InvisibleLayers) { - asTransaction([&](Transaction& t) { - t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400)); - t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600)); - - t.enableBorder(mContainerLayer, true, 20, mColorOrange); - t.hide(mEffectLayer2); - t.show(mContainerLayer); - }); -} - -TEST_F(LayerBorderTest, LayerWithBuffer) { - asTransaction([&](Transaction& t) { - t.hide(mEffectLayer1); - t.hide(mEffectLayer2); - t.show(mContainerLayer); - - sp layer = - mClient->createSurface(String8("BufferState"), 0 /* width */, 0 /* height */, - PIXEL_FORMAT_RGBA_8888, - ISurfaceComposerClient::eFXSurfaceBufferState, - mContainerLayer->getHandle()); - - sp buffer = - sp::make(400u, 400u, PIXEL_FORMAT_RGBA_8888, 1u, - BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | - BufferUsage::COMPOSER_OVERLAY | - BufferUsage::GPU_TEXTURE, - "test"); - TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 200, 200), Color::GREEN); - TransactionUtils::fillGraphicBufferColor(buffer, Rect(200, 200, 400, 400), Color::BLUE); - - t.setBuffer(layer, buffer); - t.setPosition(layer, 100, 100); - t.show(layer); - t.enableBorder(mContainerLayer, true, 20, mColorOrange); - }); -} - -TEST_F(LayerBorderTest, CustomWidth) { - asTransaction([&](Transaction& t) { - t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400)); - t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600)); - - t.enableBorder(mContainerLayer, true, 50, mColorOrange); - t.show(mEffectLayer1); - t.show(mEffectLayer2); - t.show(mContainerLayer); - }); -} - -TEST_F(LayerBorderTest, CustomColor) { - asTransaction([&](Transaction& t) { - t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400)); - t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600)); - - t.enableBorder(mContainerLayer, true, 20, toHalf4({255, 0, 255, 255})); - t.show(mEffectLayer1); - t.show(mEffectLayer2); - t.show(mContainerLayer); - }); -} - -TEST_F(LayerBorderTest, CustomWidthAndColorAndOpacity) { - asTransaction([&](Transaction& t) { - t.setCrop(mEffectLayer1, Rect(0, 0, 200, 200)); - t.setCrop(mEffectLayer2, Rect(400, 400, 600, 600)); - - t.enableBorder(mContainerLayer, true, 40, toHalf4({255, 255, 0, 128})); - t.show(mEffectLayer1); - t.show(mEffectLayer2); - t.show(mContainerLayer); - }); -} - -} // namespace android - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/tests/utils/ColorUtils.h b/services/surfaceflinger/tests/utils/ColorUtils.h index 38c422a26d..07916b60a7 100644 --- a/services/surfaceflinger/tests/utils/ColorUtils.h +++ b/services/surfaceflinger/tests/utils/ColorUtils.h @@ -33,10 +33,6 @@ struct Color { static const Color WHITE; static const Color BLACK; static const Color TRANSPARENT; - - half3 toHalf3() { return half3{r / 255.0f, g / 255.0f, b / 255.0f}; } - - half4 toHalf4() { return half4{r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f}; } }; const Color Color::RED{255, 0, 0, 255}; @@ -85,14 +81,6 @@ public: } color = ret; } - - static half3 toHalf3(const Color& color) { - return half3{color.r / 255.0f, color.g / 255.0f, color.b / 255.0f}; - } - - static half4 toHalf4(const Color& color) { - return half4{color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f}; - } }; } // namespace } // namespace android -- cgit v1.2.3-59-g8ed1b From 969c4de7ba53db35ae2023674f7cfee67a0e5c8f Mon Sep 17 00:00:00 2001 From: Melody Hsu Date: Wed, 27 Mar 2024 22:07:08 +0000 Subject: Remove getLayerDebugInfo in SF and Layer Part of effort to clean SF code prior to removing legacy layer frontend. getLayerDebugInfo is no longer used and can be removed. Bug: b/330785038 Test: atest CredentialsTest Change-Id: Iac9ac3c5934cb320331e9937cb604afc4f62eeb4 --- libs/gui/Android.bp | 1 - libs/gui/LayerDebugInfo.cpp | 152 --------------------- libs/gui/aidl/android/gui/ISurfaceComposer.aidl | 8 -- libs/gui/aidl/android/gui/LayerDebugInfo.aidl | 19 --- libs/gui/include/gui/ISurfaceComposer.h | 1 - libs/gui/include/gui/LayerDebugInfo.h | 74 ---------- libs/gui/tests/Surface_test.cpp | 4 - services/surfaceflinger/Layer.cpp | 48 ------- services/surfaceflinger/Layer.h | 6 - services/surfaceflinger/SurfaceFlinger.cpp | 30 ---- services/surfaceflinger/SurfaceFlinger.h | 3 - services/surfaceflinger/tests/Credentials_test.cpp | 31 ----- 12 files changed, 377 deletions(-) delete mode 100644 libs/gui/LayerDebugInfo.cpp delete mode 100644 libs/gui/aidl/android/gui/LayerDebugInfo.aidl delete mode 100644 libs/gui/include/gui/LayerDebugInfo.h (limited to 'libs/gui') diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index 70cb36bad3..b70e80ea9b 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -241,7 +241,6 @@ filegroup { "IProducerListener.cpp", "ISurfaceComposer.cpp", "ITransactionCompletedListener.cpp", - "LayerDebugInfo.cpp", "LayerMetadata.cpp", "LayerStatePermissions.cpp", "LayerState.cpp", diff --git a/libs/gui/LayerDebugInfo.cpp b/libs/gui/LayerDebugInfo.cpp deleted file mode 100644 index 15b2221464..0000000000 --- a/libs/gui/LayerDebugInfo.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include - -#include - -#include - -using namespace android; -using android::base::StringAppendF; - -#define RETURN_ON_ERROR(X) do {status_t res = (X); if (res != NO_ERROR) return res;} while(false) - -namespace android::gui { - -status_t LayerDebugInfo::writeToParcel(Parcel* parcel) const { - RETURN_ON_ERROR(parcel->writeCString(mName.c_str())); - RETURN_ON_ERROR(parcel->writeCString(mParentName.c_str())); - RETURN_ON_ERROR(parcel->writeCString(mType.c_str())); - RETURN_ON_ERROR(parcel->write(mTransparentRegion)); - RETURN_ON_ERROR(parcel->write(mVisibleRegion)); - RETURN_ON_ERROR(parcel->write(mSurfaceDamageRegion)); - RETURN_ON_ERROR(parcel->writeUint32(mLayerStack)); - RETURN_ON_ERROR(parcel->writeFloat(mX)); - RETURN_ON_ERROR(parcel->writeFloat(mY)); - RETURN_ON_ERROR(parcel->writeUint32(mZ)); - RETURN_ON_ERROR(parcel->writeInt32(mWidth)); - RETURN_ON_ERROR(parcel->writeInt32(mHeight)); - RETURN_ON_ERROR(parcel->write(mCrop)); - RETURN_ON_ERROR(parcel->writeFloat(mColor.r)); - RETURN_ON_ERROR(parcel->writeFloat(mColor.g)); - RETURN_ON_ERROR(parcel->writeFloat(mColor.b)); - RETURN_ON_ERROR(parcel->writeFloat(mColor.a)); - RETURN_ON_ERROR(parcel->writeUint32(mFlags)); - RETURN_ON_ERROR(parcel->writeInt32(mPixelFormat)); - RETURN_ON_ERROR(parcel->writeUint32(static_cast(mDataSpace))); - for (size_t index = 0; index < 4; index++) { - RETURN_ON_ERROR(parcel->writeFloat(mMatrix[index / 2][index % 2])); - } - RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferWidth)); - RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferHeight)); - RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferStride)); - RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferFormat)); - RETURN_ON_ERROR(parcel->writeInt32(mNumQueuedFrames)); - RETURN_ON_ERROR(parcel->writeBool(mIsOpaque)); - RETURN_ON_ERROR(parcel->writeBool(mContentDirty)); - RETURN_ON_ERROR(parcel->write(mStretchEffect)); - return NO_ERROR; -} - -status_t LayerDebugInfo::readFromParcel(const Parcel* parcel) { - mName = parcel->readCString(); - RETURN_ON_ERROR(parcel->errorCheck()); - mParentName = parcel->readCString(); - RETURN_ON_ERROR(parcel->errorCheck()); - mType = parcel->readCString(); - RETURN_ON_ERROR(parcel->errorCheck()); - RETURN_ON_ERROR(parcel->read(mTransparentRegion)); - RETURN_ON_ERROR(parcel->read(mVisibleRegion)); - RETURN_ON_ERROR(parcel->read(mSurfaceDamageRegion)); - RETURN_ON_ERROR(parcel->readUint32(&mLayerStack)); - RETURN_ON_ERROR(parcel->readFloat(&mX)); - RETURN_ON_ERROR(parcel->readFloat(&mY)); - RETURN_ON_ERROR(parcel->readUint32(&mZ)); - RETURN_ON_ERROR(parcel->readInt32(&mWidth)); - RETURN_ON_ERROR(parcel->readInt32(&mHeight)); - RETURN_ON_ERROR(parcel->read(mCrop)); - mColor.r = parcel->readFloat(); - RETURN_ON_ERROR(parcel->errorCheck()); - mColor.g = parcel->readFloat(); - RETURN_ON_ERROR(parcel->errorCheck()); - mColor.b = parcel->readFloat(); - RETURN_ON_ERROR(parcel->errorCheck()); - mColor.a = parcel->readFloat(); - RETURN_ON_ERROR(parcel->errorCheck()); - RETURN_ON_ERROR(parcel->readUint32(&mFlags)); - RETURN_ON_ERROR(parcel->readInt32(&mPixelFormat)); - // \todo [2017-07-25 kraita]: Static casting mDataSpace pointer to an uint32 does work. Better ways? - mDataSpace = static_cast(parcel->readUint32()); - RETURN_ON_ERROR(parcel->errorCheck()); - for (size_t index = 0; index < 4; index++) { - RETURN_ON_ERROR(parcel->readFloat(&mMatrix[index / 2][index % 2])); - } - RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferWidth)); - RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferHeight)); - RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferStride)); - RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferFormat)); - RETURN_ON_ERROR(parcel->readInt32(&mNumQueuedFrames)); - RETURN_ON_ERROR(parcel->readBool(&mIsOpaque)); - RETURN_ON_ERROR(parcel->readBool(&mContentDirty)); - RETURN_ON_ERROR(parcel->read(mStretchEffect)); - return NO_ERROR; -} - -std::string to_string(const LayerDebugInfo& info) { - std::string result; - - StringAppendF(&result, "+ %s (%s)\n", info.mType.c_str(), info.mName.c_str()); - info.mTransparentRegion.dump(result, "TransparentRegion"); - info.mVisibleRegion.dump(result, "VisibleRegion"); - info.mSurfaceDamageRegion.dump(result, "SurfaceDamageRegion"); - if (info.mStretchEffect.hasEffect()) { - const auto& se = info.mStretchEffect; - StringAppendF(&result, - " StretchEffect width = %f, height = %f vec=(%f, %f) " - "maxAmount=(%f, %f)\n", - se.width, se.height, - se.vectorX, se.vectorY, se.maxAmountX, se.maxAmountY); - } - - StringAppendF(&result, " layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), ", - info.mLayerStack, info.mZ, static_cast(info.mX), - static_cast(info.mY), info.mWidth, info.mHeight); - - StringAppendF(&result, "crop=%s, ", to_string(info.mCrop).c_str()); - StringAppendF(&result, "isOpaque=%1d, invalidate=%1d, ", info.mIsOpaque, info.mContentDirty); - StringAppendF(&result, "dataspace=%s, ", dataspaceDetails(info.mDataSpace).c_str()); - StringAppendF(&result, "pixelformat=%s, ", decodePixelFormat(info.mPixelFormat).c_str()); - StringAppendF(&result, "color=(%.3f,%.3f,%.3f,%.3f), flags=0x%08x, ", - static_cast(info.mColor.r), static_cast(info.mColor.g), - static_cast(info.mColor.b), static_cast(info.mColor.a), - info.mFlags); - StringAppendF(&result, "tr=[%.2f, %.2f][%.2f, %.2f]", static_cast(info.mMatrix[0][0]), - static_cast(info.mMatrix[0][1]), static_cast(info.mMatrix[1][0]), - static_cast(info.mMatrix[1][1])); - result.append("\n"); - StringAppendF(&result, " parent=%s\n", info.mParentName.c_str()); - StringAppendF(&result, " activeBuffer=[%4ux%4u:%4u,%s],", info.mActiveBufferWidth, - info.mActiveBufferHeight, info.mActiveBufferStride, - decodePixelFormat(info.mActiveBufferFormat).c_str()); - StringAppendF(&result, " queued-frames=%d", info.mNumQueuedFrames); - result.append("\n"); - return result; -} - -} // namespace android::gui diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl index 51e01930d3..a2549e7e3f 100644 --- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl +++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl @@ -43,7 +43,6 @@ import android.gui.ITunnelModeEnabledListener; import android.gui.IWindowInfosListener; import android.gui.IWindowInfosPublisher; import android.gui.LayerCaptureArgs; -import android.gui.LayerDebugInfo; import android.gui.OverlayProperties; import android.gui.PullAtomData; import android.gui.ScreenCaptureResults; @@ -288,13 +287,6 @@ interface ISurfaceComposer { */ PullAtomData onPullAtom(int atomId); - /** - * Gets the list of active layers in Z order for debugging purposes - * - * Requires the ACCESS_SURFACE_FLINGER permission. - */ - List getLayerDebugInfo(); - /** * Gets the composition preference of the default data space and default pixel format, * as well as the wide color gamut data space and wide color gamut pixel format. diff --git a/libs/gui/aidl/android/gui/LayerDebugInfo.aidl b/libs/gui/aidl/android/gui/LayerDebugInfo.aidl deleted file mode 100644 index faca980f3c..0000000000 --- a/libs/gui/aidl/android/gui/LayerDebugInfo.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 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.gui; - -parcelable LayerDebugInfo cpp_header "gui/LayerDebugInfo.h"; diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index a836f4642a..738c73a24b 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -74,7 +74,6 @@ namespace gui { struct DisplayCaptureArgs; struct LayerCaptureArgs; -class LayerDebugInfo; } // namespace gui diff --git a/libs/gui/include/gui/LayerDebugInfo.h b/libs/gui/include/gui/LayerDebugInfo.h deleted file mode 100644 index dbb80e583c..0000000000 --- a/libs/gui/include/gui/LayerDebugInfo.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2017 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 - -#include -#include -#include - -#include -#include - -namespace android::gui { - -/* Class for transporting debug info from SurfaceFlinger to authorized - * recipients. The class is intended to be a data container. There are - * no getters or setters. - */ -class LayerDebugInfo : public Parcelable { -public: - LayerDebugInfo() = default; - LayerDebugInfo(const LayerDebugInfo&) = default; - virtual ~LayerDebugInfo() = default; - - virtual status_t writeToParcel(Parcel* parcel) const; - virtual status_t readFromParcel(const Parcel* parcel); - - std::string mName = std::string("NOT FILLED"); - std::string mParentName = std::string("NOT FILLED"); - std::string mType = std::string("NOT FILLED"); - Region mTransparentRegion = Region::INVALID_REGION; - Region mVisibleRegion = Region::INVALID_REGION; - Region mSurfaceDamageRegion = Region::INVALID_REGION; - uint32_t mLayerStack = 0; - float mX = 0.f; - float mY = 0.f; - uint32_t mZ = 0 ; - int32_t mWidth = -1; - int32_t mHeight = -1; - android::Rect mCrop = android::Rect::INVALID_RECT; - half4 mColor = half4(1.0_hf, 1.0_hf, 1.0_hf, 0.0_hf); - uint32_t mFlags = 0; - PixelFormat mPixelFormat = PIXEL_FORMAT_NONE; - android_dataspace mDataSpace = HAL_DATASPACE_UNKNOWN; - // Row-major transform matrix (SurfaceControl::setMatrix()) - float mMatrix[2][2] = {{0.f, 0.f}, {0.f, 0.f}}; - int32_t mActiveBufferWidth = -1; - int32_t mActiveBufferHeight = -1; - int32_t mActiveBufferStride = 0; - PixelFormat mActiveBufferFormat = PIXEL_FORMAT_NONE; - int32_t mNumQueuedFrames = -1; - bool mIsOpaque = false; - bool mContentDirty = false; - StretchEffect mStretchEffect = {}; -}; - -std::string to_string(const LayerDebugInfo& info); - -} // namespace android::gui diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 577d2394c6..93bf4faa13 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -815,10 +815,6 @@ public: return binder::Status::ok(); } - binder::Status getLayerDebugInfo(std::vector* /*outLayers*/) override { - return binder::Status::ok(); - } - binder::Status getCompositionPreference(gui::CompositionPreference* /*outPref*/) override { return binder::Status::ok(); } diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index cd2120e990..4cc2f9e1e7 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -40,7 +40,6 @@ #include #include #include -#include #include #include #include @@ -1566,53 +1565,6 @@ void Layer::updateTransformHint(ui::Transform::RotationFlags transformHint) { // debugging // ---------------------------------------------------------------------------- -// TODO(marissaw): add new layer state info to layer debugging -gui::LayerDebugInfo Layer::getLayerDebugInfo(const DisplayDevice* display) const { - using namespace std::string_literals; - - gui::LayerDebugInfo info; - const State& ds = getDrawingState(); - info.mName = getName(); - sp parent = mDrawingParent.promote(); - info.mParentName = parent ? parent->getName() : "none"s; - info.mType = getType(); - - info.mVisibleRegion = getVisibleRegion(display); - info.mSurfaceDamageRegion = surfaceDamageRegion; - info.mLayerStack = getLayerStack().id; - info.mX = ds.transform.tx(); - info.mY = ds.transform.ty(); - info.mZ = ds.z; - info.mCrop = ds.crop; - info.mColor = ds.color; - info.mFlags = ds.flags; - info.mPixelFormat = getPixelFormat(); - info.mDataSpace = static_cast(getDataSpace()); - info.mMatrix[0][0] = ds.transform[0][0]; - info.mMatrix[0][1] = ds.transform[0][1]; - info.mMatrix[1][0] = ds.transform[1][0]; - info.mMatrix[1][1] = ds.transform[1][1]; - { - sp buffer = getBuffer(); - if (buffer != 0) { - info.mActiveBufferWidth = buffer->getWidth(); - info.mActiveBufferHeight = buffer->getHeight(); - info.mActiveBufferStride = buffer->getStride(); - info.mActiveBufferFormat = buffer->format; - } else { - info.mActiveBufferWidth = 0; - info.mActiveBufferHeight = 0; - info.mActiveBufferStride = 0; - info.mActiveBufferFormat = 0; - } - } - info.mNumQueuedFrames = getQueuedFrameCount(); - info.mIsOpaque = isOpaque(ds); - info.mContentDirty = contentDirty; - info.mStretchEffect = getStretchEffect(); - return info; -} - void Layer::miniDumpHeader(std::string& result) { result.append(kDumpTableRowLength, '-'); result.append("\n"); diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index d283d6fda9..edf2ae260b 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -71,10 +71,6 @@ class OutputLayer; struct LayerFECompositionState; } -namespace gui { -class LayerDebugInfo; -} - namespace frametimeline { class SurfaceFrame; } // namespace frametimeline @@ -703,8 +699,6 @@ public: inline const State& getDrawingState() const { return mDrawingState; } inline State& getDrawingState() { return mDrawingState; } - gui::LayerDebugInfo getLayerDebugInfo(const DisplayDevice*) const; - void miniDumpLegacy(std::string& result, const DisplayDevice&) const; void miniDump(std::string& result, const frontend::LayerSnapshot&, const DisplayDevice&) const; void dumpFrameStats(std::string& result) const; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index cf94f942fa..29f8e3fb73 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -65,7 +65,6 @@ #include #include #include -#include #include #include #include @@ -1834,19 +1833,6 @@ status_t SurfaceFlinger::isWideColorDisplay(const sp& displayToken, return NO_ERROR; } -status_t SurfaceFlinger::getLayerDebugInfo(std::vector* outLayers) { - outLayers->clear(); - auto future = mScheduler->schedule([=, this] { - const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()); - mDrawingState.traverseInZOrder([&](Layer* layer) { - outLayers->push_back(layer->getLayerDebugInfo(display.get())); - }); - }); - - future.wait(); - return NO_ERROR; -} - status_t SurfaceFlinger::getCompositionPreference( Dataspace* outDataspace, ui::PixelFormat* outPixelFormat, Dataspace* outWideColorGamutDataspace, @@ -9895,22 +9881,6 @@ binder::Status SurfaceComposerAIDL::onPullAtom(int32_t atomId, gui::PullAtomData return binderStatusFromStatusT(status); } -binder::Status SurfaceComposerAIDL::getLayerDebugInfo(std::vector* outLayers) { - if (!outLayers) { - return binderStatusFromStatusT(UNEXPECTED_NULL); - } - - IPCThreadState* ipc = IPCThreadState::self(); - const int pid = ipc->getCallingPid(); - const int uid = ipc->getCallingUid(); - if ((uid != AID_SHELL) && !PermissionCache::checkPermission(sDump, pid, uid)) { - ALOGE("Layer debug info permission denied for pid=%d, uid=%d", pid, uid); - return binderStatusFromStatusT(PERMISSION_DENIED); - } - status_t status = mFlinger->getLayerDebugInfo(outLayers); - return binderStatusFromStatusT(status); -} - binder::Status SurfaceComposerAIDL::getCompositionPreference(gui::CompositionPreference* outPref) { ui::Dataspace dataspace; ui::PixelFormat pixelFormat; diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index c106abda37..704fcef263 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -38,7 +38,6 @@ #include #include #include -#include #include #include #include @@ -599,7 +598,6 @@ private: status_t overrideHdrTypes(const sp& displayToken, const std::vector& hdrTypes); status_t onPullAtom(const int32_t atomId, std::vector* pulledData, bool* success); - status_t getLayerDebugInfo(std::vector* outLayers); status_t getCompositionPreference(ui::Dataspace* outDataspace, ui::PixelFormat* outPixelFormat, ui::Dataspace* outWideColorGamutDataspace, ui::PixelFormat* outWideColorGamutPixelFormat) const; @@ -1601,7 +1599,6 @@ public: binder::Status overrideHdrTypes(const sp& display, const std::vector& hdrTypes) override; binder::Status onPullAtom(int32_t atomId, gui::PullAtomData* outPullData) override; - binder::Status getLayerDebugInfo(std::vector* outLayers) override; binder::Status getCompositionPreference(gui::CompositionPreference* outPref) override; binder::Status getDisplayedContentSamplingAttributes( const sp& display, gui::ContentSamplingAttributes* outAttrs) override; diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp index 9b83713f50..3b6a51a224 100644 --- a/services/surfaceflinger/tests/Credentials_test.cpp +++ b/services/surfaceflinger/tests/Credentials_test.cpp @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -36,7 +35,6 @@ namespace android { using Transaction = SurfaceComposerClient::Transaction; -using gui::LayerDebugInfo; using gui::aidl_utils::statusTFromBinderStatus; using ui::ColorMode; @@ -292,35 +290,6 @@ TEST_F(CredentialsTest, CaptureLayersTest) { /** * The following tests are for methods accessible directly through SurfaceFlinger. */ -TEST_F(CredentialsTest, GetLayerDebugInfo) { - setupBackgroundSurface(); - sp sf(ComposerServiceAIDL::getComposerService()); - - // Historically, only root and shell can access the getLayerDebugInfo which - // is called when we call dumpsys. I don't see a reason why we should change this. - std::vector outLayers; - binder::Status status = binder::Status::ok(); - // Check with root. - { - UIDFaker f(AID_ROOT); - status = sf->getLayerDebugInfo(&outLayers); - ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status)); - } - - // Check as a shell. - { - UIDFaker f(AID_SHELL); - status = sf->getLayerDebugInfo(&outLayers); - ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status)); - } - - // Check as anyone else. - { - UIDFaker f(AID_BIN); - status = sf->getLayerDebugInfo(&outLayers); - ASSERT_EQ(PERMISSION_DENIED, statusTFromBinderStatus(status)); - } -} TEST_F(CredentialsTest, IsWideColorDisplayBasicCorrectness) { const auto display = getFirstDisplayToken(); -- cgit v1.2.3-59-g8ed1b From cf35919ac3bd07bd139f69cc1e3741c5788a5345 Mon Sep 17 00:00:00 2001 From: Prabir Pradhan Date: Wed, 20 Mar 2024 00:42:57 +0000 Subject: Introduce InputConfig::SENSITIVE_FOR_TRACING We don't want to rely on WM flags in input, and we also want to be able to mark a window as secure in the input framework without affecting other aspects of the system. Introduce a new InputConfig flag for sensitive windows. It must always be set when the window flag FLAG_SECURE is set. Bug: 210460522 Test: manual Change-Id: I5adb7467ab76bdcf89803e1ba08b52273a09e7bb --- libs/gui/include/gui/WindowInfo.h | 2 ++ libs/input/android/os/InputConfig.aidl | 8 +++++ .../inputflinger/dispatcher/InputDispatcher.cpp | 7 +++++ .../inputflinger/dispatcher/trace/InputTracer.cpp | 5 +-- .../FrontEnd/LayerSnapshotBuilder.cpp | 19 ++++++++---- .../tests/unittests/LayerHierarchyTest.h | 18 +++++++++++ .../tests/unittests/LayerSnapshotTest.cpp | 36 ++++++++++++++++++++++ 7 files changed, 87 insertions(+), 8 deletions(-) (limited to 'libs/gui') diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h index 2d1b51a418..e4f1890c76 100644 --- a/libs/gui/include/gui/WindowInfo.h +++ b/libs/gui/include/gui/WindowInfo.h @@ -178,6 +178,8 @@ struct WindowInfo : public Parcelable { static_cast(os::InputConfig::CLONE), GLOBAL_STYLUS_BLOCKS_TOUCH = static_cast(os::InputConfig::GLOBAL_STYLUS_BLOCKS_TOUCH), + SENSITIVE_FOR_TRACING = + static_cast(os::InputConfig::SENSITIVE_FOR_TRACING), // clang-format on }; diff --git a/libs/input/android/os/InputConfig.aidl b/libs/input/android/os/InputConfig.aidl index 5d391551c2..6b97cbbc59 100644 --- a/libs/input/android/os/InputConfig.aidl +++ b/libs/input/android/os/InputConfig.aidl @@ -157,4 +157,12 @@ enum InputConfig { * like StatusBar and TaskBar. */ GLOBAL_STYLUS_BLOCKS_TOUCH = 1 << 17, + + /** + * InputConfig used to indicate that this window is sensitive for tracing. + * This must be set on windows that use {@link WindowManager.LayoutParams#FLAG_SECURE}, + * but it may also be set without setting FLAG_SECURE. The tracing configuration will + * determine how these sensitive events are eventually traced. + */ + SENSITIVE_FOR_TRACING = 1 << 18, } diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index e331e8d0a3..25632fe161 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -838,6 +838,13 @@ Result validateWindowInfosUpdate(const gui::WindowInfosUpdate& update) { if (!inserted) { return Error() << "Duplicate entry for " << info; } + if (info.layoutParamsFlags.test(WindowInfo::Flag::SECURE) && + !info.inputConfig.test(WindowInfo::InputConfig::NOT_VISIBLE) && + !info.inputConfig.test(WindowInfo::InputConfig::SENSITIVE_FOR_TRACING)) { + return Error() + << "Window with FLAG_SECURE does not set InputConfig::SENSITIVE_FOR_TRACING: " + << info; + } } return {}; } diff --git a/services/inputflinger/dispatcher/trace/InputTracer.cpp b/services/inputflinger/dispatcher/trace/InputTracer.cpp index 1d4d11c13a..f8ee95fa9a 100644 --- a/services/inputflinger/dispatcher/trace/InputTracer.cpp +++ b/services/inputflinger/dispatcher/trace/InputTracer.cpp @@ -86,8 +86,9 @@ InputTargetInfo getTargetInfo(const InputTarget& target) { // This is a global monitor, assume its target is the system. return {.uid = gui::Uid{AID_SYSTEM}, .isSecureWindow = false}; } - return {target.windowHandle->getInfo()->ownerUid, - target.windowHandle->getInfo()->layoutParamsFlags.test(gui::WindowInfo::Flag::SECURE)}; + const bool isSensitiveTarget = target.windowHandle->getInfo()->inputConfig.test( + gui::WindowInfo::InputConfig::SENSITIVE_FOR_TRACING); + return {target.windowHandle->getInfo()->ownerUid, isSensitiveTarget}; } } // namespace diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp index 0966fe0496..7daeefe874 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp @@ -1028,6 +1028,8 @@ void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot, const LayerSnapshot& parentSnapshot, const LayerHierarchy::TraversalPath& path, const Args& args) { + using InputConfig = gui::WindowInfo::InputConfig; + if (requested.windowInfoHandle) { snapshot.inputInfo = *requested.windowInfoHandle->getInfo(); } else { @@ -1056,6 +1058,11 @@ void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot, snapshot.dropInputMode = gui::DropInputMode::NONE; } + if (snapshot.isSecure || + parentSnapshot.inputInfo.inputConfig.test(InputConfig::SENSITIVE_FOR_TRACING)) { + snapshot.inputInfo.inputConfig |= InputConfig::SENSITIVE_FOR_TRACING; + } + updateVisibility(snapshot, snapshot.isVisible); if (!needsInputInfo(snapshot, requested)) { return; @@ -1068,14 +1075,14 @@ void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot, auto displayInfo = displayInfoOpt.value_or(sDefaultInfo); if (!requested.windowInfoHandle) { - snapshot.inputInfo.inputConfig = gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL; + snapshot.inputInfo.inputConfig = InputConfig::NO_INPUT_CHANNEL; } fillInputFrameInfo(snapshot.inputInfo, displayInfo.transform, snapshot); if (noValidDisplay) { // Do not let the window receive touches if it is not associated with a valid display // transform. We still allow the window to receive keys and prevent ANRs. - snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::NOT_TOUCHABLE; + snapshot.inputInfo.inputConfig |= InputConfig::NOT_TOUCHABLE; } snapshot.inputInfo.alpha = snapshot.color.a; @@ -1085,7 +1092,7 @@ void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot, // 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 (!displayInfo.isSecure && snapshot.isSecure) { - snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::DROP_INPUT; + snapshot.inputInfo.inputConfig |= InputConfig::DROP_INPUT; } if (requested.touchCropId != UNASSIGNED_LAYER_ID || path.isClone()) { @@ -1102,7 +1109,7 @@ void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot, // Inherit the trusted state from the parent hierarchy, but don't clobber the trusted state // if it was set by WM for a known system overlay if (snapshot.isTrustedOverlay) { - snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::TRUSTED_OVERLAY; + snapshot.inputInfo.inputConfig |= InputConfig::TRUSTED_OVERLAY; } snapshot.inputInfo.contentSize = snapshot.croppedBufferSize.getSize(); @@ -1110,10 +1117,10 @@ void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot, // If the layer is a clone, we need to crop the input region to cloned root to prevent // touches from going outside the cloned area. if (path.isClone()) { - snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::CLONE; + snapshot.inputInfo.inputConfig |= InputConfig::CLONE; // Cloned layers shouldn't handle watch outside since their z order is not determined by // WM or the client. - snapshot.inputInfo.inputConfig.clear(gui::WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH); + snapshot.inputInfo.inputConfig.clear(InputConfig::WATCH_OUTSIDE_TOUCH); } } diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h index 67e624922c..e8e7667a66 100644 --- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h +++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h @@ -281,6 +281,24 @@ protected: mLifecycleManager.applyTransactions(transactions); } + void setInputInfo(uint32_t id, std::function configureInput) { + std::vector transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged; + transactions.back().states.front().layerId = id; + transactions.back().states.front().state.windowInfoHandle = + sp::make(); + auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo(); + if (!inputInfo->token) { + inputInfo->token = sp::make(); + } + configureInput(*inputInfo); + + mLifecycleManager.applyTransactions(transactions); + } + void setTouchableRegionCrop(uint32_t id, Region region, uint32_t touchCropId, bool replaceTouchableRegionWithCrop) { std::vector transactions; diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp index 94989aac84..ae9a89c0b6 100644 --- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp @@ -1198,6 +1198,42 @@ TEST_F(LayerSnapshotTest, setSecureRootSnapshot) { EXPECT_TRUE(getSnapshot(11)->isSecure); } +TEST_F(LayerSnapshotTest, setSensitiveForTracingConfigForSecureLayers) { + setFlags(11, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure); + + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + + EXPECT_TRUE(getSnapshot(11)->inputInfo.inputConfig.test( + gui::WindowInfo::InputConfig::SENSITIVE_FOR_TRACING)); + EXPECT_TRUE(getSnapshot(111)->inputInfo.inputConfig.test( + gui::WindowInfo::InputConfig::SENSITIVE_FOR_TRACING)); + EXPECT_FALSE(getSnapshot(1)->inputInfo.inputConfig.test( + gui::WindowInfo::InputConfig::SENSITIVE_FOR_TRACING)); + EXPECT_FALSE(getSnapshot(12)->inputInfo.inputConfig.test( + gui::WindowInfo::InputConfig::SENSITIVE_FOR_TRACING)); + EXPECT_FALSE(getSnapshot(2)->inputInfo.inputConfig.test( + gui::WindowInfo::InputConfig::SENSITIVE_FOR_TRACING)); +} + +TEST_F(LayerSnapshotTest, setSensitiveForTracingFromInputWindowHandle) { + setInputInfo(11, [](auto& inputInfo) { + inputInfo.inputConfig |= gui::WindowInfo::InputConfig::SENSITIVE_FOR_TRACING; + }); + + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + + EXPECT_TRUE(getSnapshot(11)->inputInfo.inputConfig.test( + gui::WindowInfo::InputConfig::SENSITIVE_FOR_TRACING)); + EXPECT_TRUE(getSnapshot(111)->inputInfo.inputConfig.test( + gui::WindowInfo::InputConfig::SENSITIVE_FOR_TRACING)); + EXPECT_FALSE(getSnapshot(1)->inputInfo.inputConfig.test( + gui::WindowInfo::InputConfig::SENSITIVE_FOR_TRACING)); + EXPECT_FALSE(getSnapshot(12)->inputInfo.inputConfig.test( + gui::WindowInfo::InputConfig::SENSITIVE_FOR_TRACING)); + EXPECT_FALSE(getSnapshot(2)->inputInfo.inputConfig.test( + gui::WindowInfo::InputConfig::SENSITIVE_FOR_TRACING)); +} + // b/314350323 TEST_F(LayerSnapshotTest, propagateDropInputMode) { setDropInputMode(1, gui::DropInputMode::ALL); -- cgit v1.2.3-59-g8ed1b From 7b3fda386c94009d601bb1e4e7c163b1f9fdae8b Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Fri, 29 Mar 2024 14:27:49 -0700 Subject: EndToEndNativeInputTest: print the event received unexpectedly To learn more about how the test is failing, let's print the events that are received expectedly. Currently, the test uncropped_container_replaces_touchable_region_with_null_crop fails intermittently because it actually receives an event when we are expecting it to be dropped instead. Bug: 331747627 Test: TEST=libgui_test; m $TEST && adb sync && adb shell -t /data/nativetest64/$TEST/$TEST --gtest_filter="*input*" Change-Id: I297950e8a197962c8adb0a9bcb044ce35a723d1e --- libs/gui/tests/EndToEndNativeInputTest.cpp | 53 +++++++++++++++--------------- 1 file changed, 26 insertions(+), 27 deletions(-) (limited to 'libs/gui') diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index 9791212e07..8d5d1e449f 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -195,7 +195,7 @@ public: EXPECT_EQ(hasFocus, focusEvent->getHasFocus()); } - void expectTap(int x, int y) { + void expectTap(float x, float y) { InputEvent* ev = consumeEvent(); ASSERT_NE(ev, nullptr); ASSERT_EQ(InputEventType::MOTION, ev->getType()); @@ -268,6 +268,11 @@ public: EXPECT_EQ(0, keyEvent->getFlags() & VERIFIED_KEY_EVENT_FLAGS); } + void assertNoEvent() { + InputEvent* event = consumeEvent(/*timeout=*/100ms); + ASSERT_EQ(event, nullptr) << "Expected no event, but got " << *event; + } + virtual ~InputSurface() { if (mClientChannel) { mInputFlinger->removeInputChannel(mClientChannel->getConnectionToken()); @@ -937,9 +942,7 @@ TEST_F(InputSurfacesTest, strict_unobscured_input_unobscured_window) { surface->showAt(100, 100); injectTap(101, 101); - - EXPECT_NE(surface->consumeEvent(), nullptr); - EXPECT_NE(surface->consumeEvent(), nullptr); + surface->expectTap(1, 1); surface->requestFocus(); surface->assertFocusChange(true); @@ -956,9 +959,7 @@ TEST_F(InputSurfacesTest, strict_unobscured_input_scaled_without_crop_window) { surface->showAt(100, 100); injectTap(101, 101); - - EXPECT_NE(surface->consumeEvent(), nullptr); - EXPECT_NE(surface->consumeEvent(), nullptr); + surface->expectTap(.5, .5); surface->requestFocus(); surface->assertFocusChange(true); @@ -977,12 +978,12 @@ TEST_F(InputSurfacesTest, strict_unobscured_input_obscured_window) { obscuringSurface->mInputInfo.ownerUid = gui::Uid{22222}; obscuringSurface->showAt(100, 100); injectTap(101, 101); - EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); + surface->assertNoEvent(); surface->requestFocus(); surface->assertFocusChange(true); injectKey(AKEYCODE_V); - EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); + surface->assertNoEvent(); } TEST_F(InputSurfacesTest, strict_unobscured_input_partially_obscured_window) { @@ -998,12 +999,12 @@ TEST_F(InputSurfacesTest, strict_unobscured_input_partially_obscured_window) { injectTap(101, 101); - EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); + surface->assertNoEvent(); surface->requestFocus(); surface->assertFocusChange(true); injectKey(AKEYCODE_V); - EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); + surface->assertNoEvent(); } TEST_F(InputSurfacesTest, strict_unobscured_input_alpha_window) { @@ -1020,12 +1021,12 @@ TEST_F(InputSurfacesTest, strict_unobscured_input_alpha_window) { injectTap(101, 101); - EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); + surface->assertNoEvent(); surface->requestFocus(); surface->assertFocusChange(true); injectKey(AKEYCODE_V); - EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); + surface->assertNoEvent(); } TEST_F(InputSurfacesTest, strict_unobscured_input_cropped_window) { @@ -1042,12 +1043,12 @@ TEST_F(InputSurfacesTest, strict_unobscured_input_cropped_window) { injectTap(111, 111); - EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); + surface->assertNoEvent(); surface->requestFocus(); surface->assertFocusChange(true); injectKey(AKEYCODE_V); - EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); + surface->assertNoEvent(); } TEST_F(InputSurfacesTest, ignore_touch_region_with_zero_sized_blast) { @@ -1071,13 +1072,12 @@ TEST_F(InputSurfacesTest, drop_input_policy) { surface->showAt(100, 100); injectTap(101, 101); - - EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); + surface->assertNoEvent(); surface->requestFocus(); surface->assertFocusChange(true); injectKey(AKEYCODE_V); - EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); + surface->assertNoEvent(); } TEST_F(InputSurfacesTest, layer_with_valid_crop_can_be_focused) { @@ -1112,7 +1112,7 @@ TEST_F(InputSurfacesTest, cropped_container_replaces_touchable_region_with_null_ // Does not receive events outside its crop injectTap(26, 26); - EXPECT_EQ(containerSurface->consumeEvent(/*timeout=*/100ms), nullptr); + containerSurface->assertNoEvent(); } /** @@ -1137,7 +1137,7 @@ TEST_F(InputSurfacesTest, uncropped_container_replaces_touchable_region_with_nul // Does not receive events outside parent bounds injectTap(31, 31); - EXPECT_EQ(containerSurface->consumeEvent(/*timeout=*/100ms), nullptr); + containerSurface->assertNoEvent(); } /** @@ -1163,7 +1163,7 @@ TEST_F(InputSurfacesTest, replace_touchable_region_with_crop) { // Does not receive events outside crop layer bounds injectTap(21, 21); injectTap(71, 71); - EXPECT_EQ(containerSurface->consumeEvent(/*timeout=*/100ms), nullptr); + containerSurface->assertNoEvent(); } TEST_F(InputSurfacesTest, child_container_with_no_input_channel_blocks_parent) { @@ -1180,7 +1180,7 @@ TEST_F(InputSurfacesTest, child_container_with_no_input_channel_blocks_parent) { [&](auto &t, auto &sc) { t.reparent(sc, parent->mSurfaceControl); }); injectTap(101, 101); - EXPECT_EQ(parent->consumeEvent(/*timeout=*/100ms), nullptr); + parent->assertNoEvent(); } class MultiDisplayTests : public InputSurfacesTest { @@ -1229,7 +1229,7 @@ TEST_F(MultiDisplayTests, drop_touch_if_layer_on_invalid_display) { // Touches should be dropped if the layer is on an invalid display. injectTapOnDisplay(101, 101, layerStack.id); - EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); + surface->assertNoEvent(); // However, we still let the window be focused and receive keys. surface->requestFocus(layerStack.id); @@ -1267,12 +1267,12 @@ TEST_F(MultiDisplayTests, drop_input_for_secure_layer_on_nonsecure_display) { injectTapOnDisplay(101, 101, layerStack.id); - EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); + surface->assertNoEvent(); surface->requestFocus(layerStack.id); surface->assertFocusChange(true); injectKeyOnDisplay(AKEYCODE_V, layerStack.id); - EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); + surface->assertNoEvent(); } TEST_F(MultiDisplayTests, dont_drop_input_for_secure_layer_on_secure_display) { @@ -1292,8 +1292,7 @@ TEST_F(MultiDisplayTests, dont_drop_input_for_secure_layer_on_secure_display) { surface->showAt(100, 100); injectTapOnDisplay(101, 101, layerStack.id); - EXPECT_NE(surface->consumeEvent(), nullptr); - EXPECT_NE(surface->consumeEvent(), nullptr); + surface->expectTap(1, 1); surface->requestFocus(layerStack.id); surface->assertFocusChange(true); -- cgit v1.2.3-59-g8ed1b From bf98a5759f3b7ae19b41ad4c66c535d33ae646a4 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Fri, 29 Mar 2024 13:34:42 -0700 Subject: Add compile checks to EndToEndNativeInputTest While trying to reproduce a test failure, I added thread safety annotations to double check the current logic. It will be useful to have this going forward if there's indeed expectation that the input team helps out with these test failures. In file BLASTBufferQueue_test, there are a bunch of thread safety and signed comparison compilation errors, so disable them locally to work around this. The test owners can choose to fix those separately if desired. Unfortunately the test failure is currently not reproducible locally. Bug: 331747627 Test: TEST=libgui_test; m $TEST && adb sync && adb shell -t /data/nativetest64/$TEST/$TEST --gtest_filter="*uncropped_container_replaces_touchable_region_with_null_crop" --gtest_repeat=1000 --gtest_break_on_failure Change-Id: I65943d3204b43f1c03488d669767cd8388b558ee --- libs/gui/tests/Android.bp | 3 ++- libs/gui/tests/BLASTBufferQueue_test.cpp | 5 ++++- libs/gui/tests/BufferQueue_test.cpp | 7 +++---- libs/gui/tests/DisplayedContentSampling_test.cpp | 8 ++++---- libs/gui/tests/EndToEndNativeInputTest.cpp | 14 ++++++++------ libs/gui/tests/Surface_test.cpp | 4 ++-- 6 files changed, 23 insertions(+), 18 deletions(-) (limited to 'libs/gui') diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp index 0f16f714dc..2faa330426 100644 --- a/libs/gui/tests/Android.bp +++ b/libs/gui/tests/Android.bp @@ -21,7 +21,8 @@ cc_test { cppflags: [ "-Wall", "-Werror", - "-Wno-extra", + "-Wextra", + "-Wthread-safety", "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_BQ_SETFRAMERATE=true", ], diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp index ea7078dd05..0cc0156d26 100644 --- a/libs/gui/tests/BLASTBufferQueue_test.cpp +++ b/libs/gui/tests/BLASTBufferQueue_test.cpp @@ -16,6 +16,9 @@ #define LOG_TAG "BLASTBufferQueue_test" +#pragma clang diagnostic ignored "-Wsign-compare" +#pragma clang diagnostic ignored "-Wthread-safety" + #include #include @@ -476,7 +479,7 @@ TEST_F(BLASTBufferQueueTest, TripleBuffering) { ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf)); allocated.push_back({slot, fence}); } - for (int i = 0; i < allocated.size(); i++) { + for (size_t i = 0; i < allocated.size(); i++) { igbProducer->cancelBuffer(allocated[i].first, allocated[i].second); } diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp index df7739c3fb..1ec6f915f7 100644 --- a/libs/gui/tests/BufferQueue_test.cpp +++ b/libs/gui/tests/BufferQueue_test.cpp @@ -119,8 +119,7 @@ TEST_F(BufferQueueTest, DISABLED_BufferQueueInAnotherProcess) { } sp serviceManager = defaultServiceManager(); - sp binderProducer = - serviceManager->getService(PRODUCER_NAME); + sp binderProducer = serviceManager->waitForService(PRODUCER_NAME); mProducer = interface_cast(binderProducer); EXPECT_TRUE(mProducer != nullptr); sp binderConsumer = @@ -1114,7 +1113,7 @@ TEST_F(BufferQueueTest, TestDiscardFreeBuffers) { // Check onBuffersDiscarded is called with correct slots auto buffersDiscarded = pl->getDiscardedSlots(); - ASSERT_EQ(buffersDiscarded.size(), 1); + ASSERT_EQ(buffersDiscarded.size(), 1u); ASSERT_EQ(buffersDiscarded[0], releasedSlot); // Check no free buffers in dump @@ -1239,7 +1238,7 @@ TEST_F(BufferQueueTest, TestConsumerDetachProducerListener) { ASSERT_EQ(OK, mConsumer->detachBuffer(item.mSlot)); // Check whether the slot from IProducerListener is same to the detached slot. - ASSERT_EQ(pl->getDetachedSlots().size(), 1); + ASSERT_EQ(pl->getDetachedSlots().size(), 1u); ASSERT_EQ(pl->getDetachedSlots()[0], slots[1]); // Dequeue another buffer. diff --git a/libs/gui/tests/DisplayedContentSampling_test.cpp b/libs/gui/tests/DisplayedContentSampling_test.cpp index 0a2750a4dd..bffb3f0430 100644 --- a/libs/gui/tests/DisplayedContentSampling_test.cpp +++ b/libs/gui/tests/DisplayedContentSampling_test.cpp @@ -116,10 +116,10 @@ TEST_F(DisplayedContentSamplingTest, SampleCollectionCoherentWithSupportMask) { EXPECT_EQ(OK, status); if (stats.numFrames <= 0) return; - if (componentMask & (0x1 << 0)) EXPECT_NE(0, stats.component_0_sample.size()); - if (componentMask & (0x1 << 1)) EXPECT_NE(0, stats.component_1_sample.size()); - if (componentMask & (0x1 << 2)) EXPECT_NE(0, stats.component_2_sample.size()); - if (componentMask & (0x1 << 3)) EXPECT_NE(0, stats.component_3_sample.size()); + if (componentMask & (0x1 << 0)) EXPECT_NE(0u, stats.component_0_sample.size()); + if (componentMask & (0x1 << 1)) EXPECT_NE(0u, stats.component_1_sample.size()); + if (componentMask & (0x1 << 2)) EXPECT_NE(0u, stats.component_2_sample.size()); + if (componentMask & (0x1 << 3)) EXPECT_NE(0u, stats.component_3_sample.size()); } } // namespace android diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index 9791212e07..7222c607e7 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -24,6 +24,7 @@ #include +#include #include #include #include @@ -78,21 +79,22 @@ static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 5s; class SynchronousWindowInfosReportedListener : public gui::BnWindowInfosReportedListener { public: binder::Status onWindowInfosReported() override { - std::lock_guard lock{mMutex}; + std::scoped_lock lock{mLock}; mWindowInfosReported = true; mConditionVariable.notify_one(); return binder::Status::ok(); } void wait() { - std::unique_lock lock{mMutex}; - mConditionVariable.wait(lock, [&] { return mWindowInfosReported; }); + std::unique_lock lock{mLock}; + android::base::ScopedLockAssertion assumeLocked(mLock); + mConditionVariable.wait(lock, [&]() REQUIRES(mLock) { return mWindowInfosReported; }); } private: - std::mutex mMutex; + std::mutex mLock; std::condition_variable mConditionVariable; - bool mWindowInfosReported{false}; + bool mWindowInfosReported GUARDED_BY(mLock){false}; }; class InputSurface { @@ -250,7 +252,7 @@ public: EXPECT_EQ(0, mev->getFlags() & VERIFIED_MOTION_EVENT_FLAGS); } - void expectKey(uint32_t keycode) { + void expectKey(int32_t keycode) { InputEvent *ev = consumeEvent(); ASSERT_NE(ev, nullptr); ASSERT_EQ(InputEventType::KEY, ev->getType()); diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 93bf4faa13..f4b059c39b 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -173,7 +173,7 @@ protected: // Acquire and free 1+extraDiscardedBuffers buffer, check onBufferReleased is called. std::vector releasedItems; releasedItems.resize(1+extraDiscardedBuffers); - for (int i = 0; i < releasedItems.size(); i++) { + for (size_t i = 0; i < releasedItems.size(); i++) { ASSERT_EQ(NO_ERROR, consumer->acquireBuffer(&releasedItems[i], 0)); ASSERT_EQ(NO_ERROR, consumer->releaseBuffer(releasedItems[i].mSlot, releasedItems[i].mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, @@ -197,7 +197,7 @@ protected: // Check onBufferDiscarded is called with correct buffer auto discardedBuffers = listener->getDiscardedBuffers(); ASSERT_EQ(discardedBuffers.size(), releasedItems.size()); - for (int i = 0; i < releasedItems.size(); i++) { + for (size_t i = 0; i < releasedItems.size(); i++) { ASSERT_EQ(discardedBuffers[i], releasedItems[i].mGraphicBuffer); } -- cgit v1.2.3-59-g8ed1b From 2e8ae85dfa36c22c4b9b47525cfee39b31bbd0f0 Mon Sep 17 00:00:00 2001 From: Vishnu Nair Date: Sat, 30 Mar 2024 00:02:30 +0000 Subject: Support -Wthread-safety for BLASTBufferQueue_test Bug: 331747627 Test: presubmit Change-Id: Ic418acb930ff01836ff11dadc41c40b1e9c0d2c6 --- libs/gui/tests/BLASTBufferQueue_test.cpp | 78 ++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 35 deletions(-) (limited to 'libs/gui') diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp index 0cc0156d26..946ff058cf 100644 --- a/libs/gui/tests/BLASTBufferQueue_test.cpp +++ b/libs/gui/tests/BLASTBufferQueue_test.cpp @@ -16,11 +16,9 @@ #define LOG_TAG "BLASTBufferQueue_test" -#pragma clang diagnostic ignored "-Wsign-compare" -#pragma clang diagnostic ignored "-Wthread-safety" - #include +#include #include #include #include @@ -64,7 +62,8 @@ public: } void waitOnNumberReleased(int32_t expectedNumReleased) { - std::unique_lock lock(mMutex); + std::unique_lock lock{mMutex}; + base::ScopedLockAssertion assumeLocked(mMutex); while (mNumReleased < expectedNumReleased) { ASSERT_NE(mReleaseCallback.wait_for(lock, std::chrono::seconds(3)), std::cv_status::timeout) @@ -137,11 +136,18 @@ public: void clearSyncTransaction() { mBlastBufferQueueAdapter->clearSyncTransaction(); } - int getWidth() { return mBlastBufferQueueAdapter->mSize.width; } + int getWidth() { + std::scoped_lock lock(mBlastBufferQueueAdapter->mMutex); + return mBlastBufferQueueAdapter->mSize.width; + } - int getHeight() { return mBlastBufferQueueAdapter->mSize.height; } + int getHeight() { + std::scoped_lock lock(mBlastBufferQueueAdapter->mMutex); + return mBlastBufferQueueAdapter->mSize.height; + } std::function getTransactionReadyCallback() { + std::scoped_lock lock(mBlastBufferQueueAdapter->mMutex); return mBlastBufferQueueAdapter->mTransactionReadyCallback; } @@ -150,6 +156,7 @@ public: } const sp getSurfaceControl() { + std::scoped_lock lock(mBlastBufferQueueAdapter->mMutex); return mBlastBufferQueueAdapter->mSurfaceControl; } @@ -159,6 +166,7 @@ public: void waitForCallbacks() { std::unique_lock lock{mBlastBufferQueueAdapter->mMutex}; + base::ScopedLockAssertion assumeLocked(mBlastBufferQueueAdapter->mMutex); // Wait until all but one of the submitted buffers have been released. while (mBlastBufferQueueAdapter->mSubmitted.size() > 1) { mBlastBufferQueueAdapter->mCallbackCV.wait(lock); @@ -169,8 +177,8 @@ public: mBlastBufferQueueAdapter->waitForCallback(frameNumber); } - void validateNumFramesSubmitted(int64_t numFramesSubmitted) { - std::unique_lock lock{mBlastBufferQueueAdapter->mMutex}; + void validateNumFramesSubmitted(size_t numFramesSubmitted) { + std::scoped_lock lock{mBlastBufferQueueAdapter->mMutex}; ASSERT_EQ(numFramesSubmitted, mBlastBufferQueueAdapter->mSubmitted.size()); } @@ -204,7 +212,7 @@ protected: mDisplayWidth = resolution.getWidth(); mDisplayHeight = resolution.getHeight(); ALOGD("Display: %dx%d orientation:%d", mDisplayWidth, mDisplayHeight, - displayState.orientation); + static_cast(displayState.orientation)); mRootSurfaceControl = mClient->createSurface(String8("RootTestSurface"), mDisplayWidth, mDisplayHeight, PIXEL_FORMAT_RGBA_8888, @@ -243,8 +251,8 @@ protected: void fillBuffer(uint32_t* bufData, Rect rect, uint32_t stride, uint8_t r, uint8_t g, uint8_t b) { - for (uint32_t row = rect.top; row < rect.bottom; row++) { - for (uint32_t col = rect.left; col < rect.right; col++) { + for (int32_t row = rect.top; row < rect.bottom; row++) { + for (int32_t col = rect.left; col < rect.right; col++) { uint8_t* pixel = (uint8_t*)(bufData + (row * stride) + col); *pixel = r; *(pixel + 1) = g; @@ -274,16 +282,16 @@ protected: bool outsideRegion = false) { sp& captureBuf = mCaptureResults.buffer; const auto epsilon = 3; - const auto width = captureBuf->getWidth(); - const auto height = captureBuf->getHeight(); + const int32_t width = static_cast(captureBuf->getWidth()); + const int32_t height = static_cast(captureBuf->getHeight()); const auto stride = captureBuf->getStride(); uint32_t* bufData; captureBuf->lock(static_cast(GraphicBuffer::USAGE_SW_READ_OFTEN), reinterpret_cast(&bufData)); - for (uint32_t row = 0; row < height; row++) { - for (uint32_t col = 0; col < width; col++) { + for (int32_t row = 0; row < height; row++) { + for (int32_t col = 0; col < width; col++) { uint8_t* pixel = (uint8_t*)(bufData + (row * stride) + col); ASSERT_NE(nullptr, pixel); bool inRegion; @@ -355,8 +363,8 @@ TEST_F(BLASTBufferQueueTest, CreateBLASTBufferQueue) { // create BLASTBufferQueue adapter associated with this surface BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); ASSERT_EQ(mSurfaceControl, adapter.getSurfaceControl()); - ASSERT_EQ(mDisplayWidth, adapter.getWidth()); - ASSERT_EQ(mDisplayHeight, adapter.getHeight()); + ASSERT_EQ(static_cast(mDisplayWidth), adapter.getWidth()); + ASSERT_EQ(static_cast(mDisplayHeight), adapter.getHeight()); ASSERT_EQ(nullptr, adapter.getTransactionReadyCallback()); } @@ -374,10 +382,10 @@ TEST_F(BLASTBufferQueueTest, Update) { int32_t width; igbProducer->query(NATIVE_WINDOW_WIDTH, &width); - ASSERT_EQ(mDisplayWidth / 2, width); + ASSERT_EQ(static_cast(mDisplayWidth) / 2, width); int32_t height; igbProducer->query(NATIVE_WINDOW_HEIGHT, &height); - ASSERT_EQ(mDisplayHeight / 2, height); + ASSERT_EQ(static_cast(mDisplayHeight) / 2, height); } TEST_F(BLASTBufferQueueTest, SyncNextTransaction) { @@ -1316,14 +1324,14 @@ TEST_F(BLASTBufferQueueTest, TransformHint) { // Before connecting to the surface, we do not get a valid transform hint int transformHint; surface->query(NATIVE_WINDOW_TRANSFORM_HINT, &transformHint); - ASSERT_EQ(ui::Transform::ROT_0, transformHint); + ASSERT_EQ(ui::Transform::ROT_0, static_cast(transformHint)); ASSERT_EQ(NO_ERROR, surface->connect(NATIVE_WINDOW_API_CPU, new TestProducerListener(igbProducer))); // After connecting to the surface, we should get the correct hint. surface->query(NATIVE_WINDOW_TRANSFORM_HINT, &transformHint); - ASSERT_EQ(ui::Transform::ROT_90, transformHint); + ASSERT_EQ(ui::Transform::ROT_90, static_cast(transformHint)); ANativeWindow_Buffer buffer; surface->lock(&buffer, nullptr /* inOutDirtyBounds */); @@ -1334,13 +1342,13 @@ TEST_F(BLASTBufferQueueTest, TransformHint) { // The hint does not change and matches the value used when dequeueing the buffer. surface->query(NATIVE_WINDOW_TRANSFORM_HINT, &transformHint); - ASSERT_EQ(ui::Transform::ROT_90, transformHint); + ASSERT_EQ(ui::Transform::ROT_90, static_cast(transformHint)); surface->unlockAndPost(); // After queuing the buffer, we get the updated transform hint surface->query(NATIVE_WINDOW_TRANSFORM_HINT, &transformHint); - ASSERT_EQ(ui::Transform::ROT_0, transformHint); + ASSERT_EQ(ui::Transform::ROT_0, static_cast(transformHint)); adapter.waitForCallbacks(); } @@ -1576,7 +1584,7 @@ TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_Basic) { FrameEvents* events = nullptr; events = history.getFrame(1); ASSERT_NE(nullptr, events); - ASSERT_EQ(1, events->frameNumber); + ASSERT_EQ(1u, events->frameNumber); ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime); ASSERT_GE(events->postedTime, postedTimeA); @@ -1594,7 +1602,7 @@ TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_Basic) { ASSERT_NE(nullptr, events); // frame number, requestedPresentTime, and postTime should not have changed - ASSERT_EQ(1, events->frameNumber); + ASSERT_EQ(1u, events->frameNumber); ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime); ASSERT_GE(events->postedTime, postedTimeA); @@ -1609,7 +1617,7 @@ TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_Basic) { // we should also have gotten the initial values for the next frame events = history.getFrame(2); ASSERT_NE(nullptr, events); - ASSERT_EQ(2, events->frameNumber); + ASSERT_EQ(2u, events->frameNumber); ASSERT_EQ(requestedPresentTimeB, events->requestedPresentTime); ASSERT_GE(events->postedTime, postedTimeB); @@ -1625,7 +1633,7 @@ TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_Basic) { // Check the first frame... events = history.getFrame(1); ASSERT_NE(nullptr, events); - ASSERT_EQ(1, events->frameNumber); + ASSERT_EQ(1u, events->frameNumber); ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime); ASSERT_GE(events->postedTime, postedTimeA); ASSERT_GE(events->latchTime, postedTimeA); @@ -1639,7 +1647,7 @@ TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_Basic) { // ...and the second events = history.getFrame(2); ASSERT_NE(nullptr, events); - ASSERT_EQ(2, events->frameNumber); + ASSERT_EQ(2u, events->frameNumber); ASSERT_EQ(requestedPresentTimeB, events->requestedPresentTime); ASSERT_GE(events->postedTime, postedTimeB); ASSERT_GE(events->latchTime, postedTimeB); @@ -1653,7 +1661,7 @@ TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_Basic) { // ...and finally the third! events = history.getFrame(3); ASSERT_NE(nullptr, events); - ASSERT_EQ(3, events->frameNumber); + ASSERT_EQ(3u, events->frameNumber); ASSERT_EQ(requestedPresentTimeC, events->requestedPresentTime); ASSERT_GE(events->postedTime, postedTimeC); @@ -1680,7 +1688,7 @@ TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_DroppedFrame) { FrameEvents* events = nullptr; events = history.getFrame(1); ASSERT_NE(nullptr, events); - ASSERT_EQ(1, events->frameNumber); + ASSERT_EQ(1u, events->frameNumber); ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime); ASSERT_GE(events->postedTime, postedTimeA); @@ -1695,7 +1703,7 @@ TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_DroppedFrame) { ASSERT_NE(nullptr, events); // frame number, requestedPresentTime, and postTime should not have changed - ASSERT_EQ(1, events->frameNumber); + ASSERT_EQ(1u, events->frameNumber); ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime); ASSERT_GE(events->postedTime, postedTimeA); @@ -1718,7 +1726,7 @@ TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_DroppedFrame) { adapter.waitForCallback(3); // frame number, requestedPresentTime, and postTime should not have changed - ASSERT_EQ(1, events->frameNumber); + ASSERT_EQ(1u, events->frameNumber); ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime); ASSERT_GE(events->postedTime, postedTimeA); @@ -1732,7 +1740,7 @@ TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_DroppedFrame) { // we should also have gotten values for the presented frame events = history.getFrame(2); ASSERT_NE(nullptr, events); - ASSERT_EQ(2, events->frameNumber); + ASSERT_EQ(2u, events->frameNumber); ASSERT_EQ(requestedPresentTimeB, events->requestedPresentTime); ASSERT_GE(events->postedTime, postedTimeB); ASSERT_GE(events->latchTime, postedTimeB); @@ -1754,7 +1762,7 @@ TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_DroppedFrame) { // frame number, requestedPresentTime, and postTime should not have changed events = history.getFrame(1); - ASSERT_EQ(1, events->frameNumber); + ASSERT_EQ(1u, events->frameNumber); ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime); ASSERT_GE(events->postedTime, postedTimeA); @@ -1768,7 +1776,7 @@ TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_DroppedFrame) { // we should also have gotten values for the presented frame events = history.getFrame(2); ASSERT_NE(nullptr, events); - ASSERT_EQ(2, events->frameNumber); + ASSERT_EQ(2u, events->frameNumber); ASSERT_EQ(requestedPresentTimeB, events->requestedPresentTime); ASSERT_GE(events->postedTime, postedTimeB); ASSERT_GE(events->latchTime, postedTimeB); -- cgit v1.2.3-59-g8ed1b From 682a45b6d2bfed732b584be10d3deed6faae8c71 Mon Sep 17 00:00:00 2001 From: Carlos Martinez Romero Date: Mon, 26 Feb 2024 10:58:58 -0800 Subject: [sfdo] Migrate sfdo over to Rust. Test: build and run on device. Bug: 329450914 Change-Id: I6a1cbba7eacc74d960f9eb854928bac1ad66d20a --- cmds/sfdo/Android.bp | 19 +-- cmds/sfdo/sfdo.cpp | 166 --------------------- cmds/sfdo/sfdo.rs | 155 +++++++++++++++++++ libs/gui/Android.bp | 31 +++- libs/gui/aidl/Android.bp | 85 +++++++++++ libs/gui/aidl/android/gui/BitTube.aidl | 2 +- libs/gui/aidl/android/gui/CaptureArgs.aidl | 2 +- libs/gui/aidl/android/gui/DisplayCaptureArgs.aidl | 3 +- libs/gui/aidl/android/gui/LayerCaptureArgs.aidl | 2 +- libs/gui/aidl/android/gui/LayerMetadata.aidl | 2 +- .../aidl/android/gui/ParcelableVsyncEventData.aidl | 2 +- .../gui/aidl/android/gui/ScreenCaptureResults.aidl | 2 +- libs/gui/android/gui/DisplayInfo.aidl | 2 +- libs/gui/android/gui/WindowInfo.aidl | 2 +- libs/gui/android/gui/WindowInfosUpdate.aidl | 2 +- libs/gui/rust/aidl_types/Android.bp | 23 +++ libs/gui/rust/aidl_types/src/lib.rs | 55 +++++++ 17 files changed, 362 insertions(+), 193 deletions(-) delete mode 100644 cmds/sfdo/sfdo.cpp create mode 100644 cmds/sfdo/sfdo.rs create mode 100644 libs/gui/aidl/Android.bp create mode 100644 libs/gui/rust/aidl_types/Android.bp create mode 100644 libs/gui/rust/aidl_types/src/lib.rs (limited to 'libs/gui') diff --git a/cmds/sfdo/Android.bp b/cmds/sfdo/Android.bp index c19c9da7bf..a91a7dc08a 100644 --- a/cmds/sfdo/Android.bp +++ b/cmds/sfdo/Android.bp @@ -1,17 +1,10 @@ -cc_binary { +rust_binary { name: "sfdo", + srcs: ["sfdo.rs"], - srcs: ["sfdo.cpp"], - - shared_libs: [ - "libutils", - "libgui", - ], - - cflags: [ - "-Wall", - "-Werror", - "-Wunused", - "-Wunreachable-code", + rustlibs: [ + "android.gui-rust", + "libclap", ], + edition: "2021", } diff --git a/cmds/sfdo/sfdo.cpp b/cmds/sfdo/sfdo.cpp deleted file mode 100644 index de0e1718ab..0000000000 --- a/cmds/sfdo/sfdo.cpp +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -using namespace android; - -std::map g_functions; - -enum class ParseToggleResult { - kError, - kFalse, - kTrue, -}; - -const std::map g_function_details = { - {"debugFlash", "[optional(delay)] Perform a debug flash."}, - {"frameRateIndicator", "[hide | show] displays the framerate in the top left corner."}, - {"scheduleComposite", "Force composite ahead of next VSYNC."}, - {"scheduleCommit", "Force commit ahead of next VSYNC."}, - {"scheduleComposite", "PENDING - if you have a good understanding let me know!"}, - {"forceClientComposition", - "[enabled | disabled] When enabled, it disables " - "Hardware Overlays, and routes all window composition to the GPU. This can " - "help check if there is a bug in HW Composer."}, -}; - -static void ShowUsage() { - std::cout << "usage: sfdo [help, frameRateIndicator show, debugFlash enabled, ...]\n\n"; - for (const auto& sf : g_functions) { - const std::string fn = sf.first; - std::string fdetails = "TODO"; - if (g_function_details.find(fn) != g_function_details.end()) - fdetails = g_function_details.find(fn)->second; - std::cout << " " << fn << ": " << fdetails << "\n"; - } -} - -// Returns 1 for positive keywords and 0 for negative keywords. -// If the string does not match any it will return -1. -ParseToggleResult parseToggle(const char* str) { - const std::unordered_set positive{"1", "true", "y", "yes", - "on", "enabled", "show"}; - const std::unordered_set negative{"0", "false", "n", "no", - "off", "disabled", "hide"}; - - const std::string word(str); - if (positive.count(word)) { - return ParseToggleResult::kTrue; - } - if (negative.count(word)) { - return ParseToggleResult::kFalse; - } - - return ParseToggleResult::kError; -} - -int frameRateIndicator(int argc, char** argv) { - bool hide = false, show = false; - if (argc == 3) { - show = strcmp(argv[2], "show") == 0; - hide = strcmp(argv[2], "hide") == 0; - } - - if (show || hide) { - ComposerServiceAIDL::getComposerService()->enableRefreshRateOverlay(show); - } else { - std::cerr << "Incorrect usage of frameRateIndicator. Missing [hide | show].\n"; - return -1; - } - return 0; -} - -int debugFlash(int argc, char** argv) { - int delay = 0; - if (argc == 3) { - delay = atoi(argv[2]) == 0; - } - - ComposerServiceAIDL::getComposerService()->setDebugFlash(delay); - return 0; -} - -int scheduleComposite([[maybe_unused]] int argc, [[maybe_unused]] char** argv) { - ComposerServiceAIDL::getComposerService()->scheduleComposite(); - return 0; -} - -int scheduleCommit([[maybe_unused]] int argc, [[maybe_unused]] char** argv) { - ComposerServiceAIDL::getComposerService()->scheduleCommit(); - return 0; -} - -int forceClientComposition(int argc, char** argv) { - bool enabled = true; - // A valid command looks like this: - // adb shell sfdo forceClientComposition enabled - if (argc >= 3) { - const ParseToggleResult toggle = parseToggle(argv[2]); - if (toggle == ParseToggleResult::kError) { - std::cerr << "Incorrect usage of forceClientComposition. " - "Missing [enabled | disabled].\n"; - return -1; - } - if (argc > 3) { - std::cerr << "Too many arguments after [enabled | disabled]. " - "Ignoring extra arguments.\n"; - } - enabled = (toggle == ParseToggleResult::kTrue); - } else { - std::cerr << "Incorrect usage of forceClientComposition. Missing [enabled | disabled].\n"; - return -1; - } - - ComposerServiceAIDL::getComposerService()->forceClientComposition(enabled); - return 0; -} - -int main(int argc, char** argv) { - std::cout << "Execute SurfaceFlinger internal commands.\n"; - std::cout << "sfdo requires to be run with root permissions..\n"; - - g_functions["frameRateIndicator"] = frameRateIndicator; - g_functions["debugFlash"] = debugFlash; - g_functions["scheduleComposite"] = scheduleComposite; - g_functions["scheduleCommit"] = scheduleCommit; - g_functions["forceClientComposition"] = forceClientComposition; - - if (argc > 1 && g_functions.find(argv[1]) != g_functions.end()) { - std::cout << "Running: " << argv[1] << "\n"; - const std::string key(argv[1]); - const auto fn = g_functions[key]; - int result = std::any_cast(fn)(argc, argv); - if (result == 0) { - std::cout << "Success.\n"; - } - return result; - } else { - ShowUsage(); - } - return 0; -} \ No newline at end of file diff --git a/cmds/sfdo/sfdo.rs b/cmds/sfdo/sfdo.rs new file mode 100644 index 0000000000..863df6b7a0 --- /dev/null +++ b/cmds/sfdo/sfdo.rs @@ -0,0 +1,155 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! sfdo: Make surface flinger do things +use android_gui::{aidl::android::gui::ISurfaceComposer::ISurfaceComposer, binder}; +use clap::{Parser, Subcommand}; +use std::fmt::Debug; + +const SERVICE_IDENTIFIER: &str = "SurfaceFlingerAIDL"; + +fn print_result(function_name: &str, res: Result) +where + E: Debug, +{ + match res { + Ok(_) => println!("{}: Operation successful!", function_name), + Err(err) => println!("{}: Operation failed: {:?}", function_name, err), + } +} + +fn parse_toggle(toggle_value: &str) -> Option { + let positive = ["1", "true", "y", "yes", "on", "enabled", "show"]; + let negative = ["0", "false", "n", "no", "off", "disabled", "hide"]; + + let word = toggle_value.to_lowercase(); // Case-insensitive comparison + + if positive.contains(&word.as_str()) { + Some(true) + } else if negative.contains(&word.as_str()) { + Some(false) + } else { + None + } +} + +#[derive(Parser)] +#[command(version = "0.1", about = "Execute SurfaceFlinger internal commands.")] +#[command(propagate_version = true)] +struct Cli { + #[command(subcommand)] + command: Option, +} + +#[derive(Subcommand, Debug)] +enum Commands { + #[command(about = "[optional(--delay)] Perform a debug flash.")] + DebugFlash { + #[arg(short, long, default_value_t = 0)] + delay: i32, + }, + + #[command( + about = "state = [enabled | disabled] When enabled, it disables Hardware Overlays, \ + and routes all window composition to the GPU. This can help check if \ + there is a bug in HW Composer." + )] + ForceClientComposition { state: Option }, + + #[command(about = "state = [hide | show], displays the framerate in the top left corner.")] + FrameRateIndicator { state: Option }, + + #[command(about = "Force composite ahead of next VSYNC.")] + ScheduleComposite, + + #[command(about = "Force commit ahead of next VSYNC.")] + ScheduleCommit, +} + +/// sfdo command line tool +/// +/// sfdo allows you to call different functions from the SurfaceComposer using +/// the adb shell. +fn main() { + binder::ProcessState::start_thread_pool(); + let composer_service = match binder::get_interface::(SERVICE_IDENTIFIER) { + Ok(service) => service, + Err(err) => { + eprintln!("Unable to connect to ISurfaceComposer: {}", err); + return; + } + }; + + let cli = Cli::parse(); + + match &cli.command { + Some(Commands::FrameRateIndicator { state }) => { + if let Some(op_state) = state { + let toggle = parse_toggle(op_state); + match toggle { + Some(true) => { + let res = composer_service.enableRefreshRateOverlay(true); + print_result("enableRefreshRateOverlay", res); + } + Some(false) => { + let res = composer_service.enableRefreshRateOverlay(false); + print_result("enableRefreshRateOverlay", res); + } + None => { + eprintln!("Invalid state: {}, choices are [hide | show]", op_state); + } + } + } else { + eprintln!("No state, choices are [hide | show]"); + } + } + Some(Commands::DebugFlash { delay }) => { + let res = composer_service.setDebugFlash(*delay); + print_result("setDebugFlash", res); + } + Some(Commands::ScheduleComposite) => { + let res = composer_service.scheduleComposite(); + print_result("scheduleComposite", res); + } + Some(Commands::ScheduleCommit) => { + let res = composer_service.scheduleCommit(); + print_result("scheduleCommit", res); + } + Some(Commands::ForceClientComposition { state }) => { + if let Some(op_state) = state { + let toggle = parse_toggle(op_state); + match toggle { + Some(true) => { + let res = composer_service.forceClientComposition(true); + print_result("forceClientComposition", res); + } + Some(false) => { + let res = composer_service.forceClientComposition(false); + print_result("forceClientComposition", res); + } + None => { + eprintln!("Invalid state: {}, choices are [enabled | disabled]", op_state); + } + } + } else { + eprintln!("No state, choices are [enabled | disabled]"); + } + } + None => { + println!("Execute SurfaceFlinger internal commands."); + println!("run `adb shell sfdo help` for more to view the commands."); + println!("run `adb shell sfdo [COMMAND] --help` for more info on the command."); + } + } +} diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index b70e80ea9b..d1b2c5f025 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -155,9 +155,9 @@ cc_library_static { }, } -aidl_library { - name: "libgui_aidl_hdrs", - hdrs: [ +filegroup { + name: "libgui_extra_aidl_files", + srcs: [ "android/gui/DisplayInfo.aidl", "android/gui/FocusRequest.aidl", "android/gui/InputApplicationInfo.aidl", @@ -170,11 +170,34 @@ aidl_library { ], } +filegroup { + name: "libgui_extra_unstructured_aidl_files", + srcs: [ + "android/gui/DisplayInfo.aidl", + "android/gui/InputApplicationInfo.aidl", + "android/gui/WindowInfo.aidl", + "android/gui/WindowInfosUpdate.aidl", + ], +} + +aidl_library { + name: "libgui_aidl_hdrs", + hdrs: [":libgui_extra_aidl_files"], +} + +aidl_library { + name: "libgui_extra_unstructured_aidl_hdrs", + hdrs: [":libgui_extra_unstructured_aidl_files"], +} + aidl_library { name: "libgui_aidl", srcs: ["aidl/**/*.aidl"], strip_import_prefix: "aidl", - deps: ["libgui_aidl_hdrs"], + deps: [ + "libgui_aidl_hdrs", + "libgui_extra_unstructured_aidl_hdrs", + ], } filegroup { diff --git a/libs/gui/aidl/Android.bp b/libs/gui/aidl/Android.bp new file mode 100644 index 0000000000..8ed08c2644 --- /dev/null +++ b/libs/gui/aidl/Android.bp @@ -0,0 +1,85 @@ +// Copyright 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +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"], + default_team: "trendy_team_android_core_graphics_stack", +} + +filegroup { + name: "libgui_unstructured_aidl_files", + srcs: [ + ":libgui_extra_unstructured_aidl_files", + + "android/gui/BitTube.aidl", + "android/gui/CaptureArgs.aidl", + "android/gui/DisplayCaptureArgs.aidl", + "android/gui/LayerCaptureArgs.aidl", + "android/gui/LayerMetadata.aidl", + "android/gui/ParcelableVsyncEventData.aidl", + "android/gui/ScreenCaptureResults.aidl", + ], +} + +aidl_library { + name: "libgui_unstructured_aidl", + hdrs: [":libgui_unstructured_aidl_files"], +} + +filegroup { + name: "libgui_interface_aidl_files", + srcs: [ + ":libgui_extra_aidl_files", + "**/*.aidl", + ], + exclude_srcs: [":libgui_unstructured_aidl_files"], +} + +aidl_interface { + name: "android.gui", + unstable: true, + srcs: [ + ":libgui_interface_aidl_files", + ], + include_dirs: [ + "frameworks/native/libs/gui", + "frameworks/native/libs/gui/aidl", + ], + headers: [ + "libgui_aidl_hdrs", + "libgui_extra_unstructured_aidl_hdrs", + ], + backend: { + rust: { + enabled: true, + additional_rustlibs: [ + "libgui_aidl_types_rs", + ], + }, + java: { + enabled: false, + }, + cpp: { + enabled: false, + }, + ndk: { + enabled: false, + }, + }, +} diff --git a/libs/gui/aidl/android/gui/BitTube.aidl b/libs/gui/aidl/android/gui/BitTube.aidl index 6b0595ec66..eb231c1c9f 100644 --- a/libs/gui/aidl/android/gui/BitTube.aidl +++ b/libs/gui/aidl/android/gui/BitTube.aidl @@ -16,4 +16,4 @@ package android.gui; -parcelable BitTube cpp_header "private/gui/BitTube.h"; +parcelable BitTube cpp_header "private/gui/BitTube.h" rust_type "gui_aidl_types_rs::BitTube"; diff --git a/libs/gui/aidl/android/gui/CaptureArgs.aidl b/libs/gui/aidl/android/gui/CaptureArgs.aidl index 920d94980a..9f198cae10 100644 --- a/libs/gui/aidl/android/gui/CaptureArgs.aidl +++ b/libs/gui/aidl/android/gui/CaptureArgs.aidl @@ -16,4 +16,4 @@ package android.gui; -parcelable CaptureArgs cpp_header "gui/DisplayCaptureArgs.h"; +parcelable CaptureArgs cpp_header "gui/DisplayCaptureArgs.h" rust_type "gui_aidl_types_rs::CaptureArgs"; diff --git a/libs/gui/aidl/android/gui/DisplayCaptureArgs.aidl b/libs/gui/aidl/android/gui/DisplayCaptureArgs.aidl index 2caa2b9f61..fc97dbf03d 100644 --- a/libs/gui/aidl/android/gui/DisplayCaptureArgs.aidl +++ b/libs/gui/aidl/android/gui/DisplayCaptureArgs.aidl @@ -16,4 +16,5 @@ package android.gui; -parcelable DisplayCaptureArgs cpp_header "gui/DisplayCaptureArgs.h"; +parcelable DisplayCaptureArgs cpp_header "gui/DisplayCaptureArgs.h" rust_type "gui_aidl_types_rs::DisplayCaptureArgs"; + diff --git a/libs/gui/aidl/android/gui/LayerCaptureArgs.aidl b/libs/gui/aidl/android/gui/LayerCaptureArgs.aidl index f0def5019a..18d293f211 100644 --- a/libs/gui/aidl/android/gui/LayerCaptureArgs.aidl +++ b/libs/gui/aidl/android/gui/LayerCaptureArgs.aidl @@ -16,4 +16,4 @@ package android.gui; -parcelable LayerCaptureArgs cpp_header "gui/LayerCaptureArgs.h"; +parcelable LayerCaptureArgs cpp_header "gui/LayerCaptureArgs.h" rust_type "gui_aidl_types_rs::LayerCaptureArgs"; diff --git a/libs/gui/aidl/android/gui/LayerMetadata.aidl b/libs/gui/aidl/android/gui/LayerMetadata.aidl index 1368ac512f..d8121bedb0 100644 --- a/libs/gui/aidl/android/gui/LayerMetadata.aidl +++ b/libs/gui/aidl/android/gui/LayerMetadata.aidl @@ -16,4 +16,4 @@ package android.gui; -parcelable LayerMetadata cpp_header "gui/LayerMetadata.h"; +parcelable LayerMetadata cpp_header "gui/LayerMetadata.h" rust_type "gui_aidl_types_rs::LayerMetadata"; diff --git a/libs/gui/aidl/android/gui/ParcelableVsyncEventData.aidl b/libs/gui/aidl/android/gui/ParcelableVsyncEventData.aidl index ba76671f8f..53f443aa59 100644 --- a/libs/gui/aidl/android/gui/ParcelableVsyncEventData.aidl +++ b/libs/gui/aidl/android/gui/ParcelableVsyncEventData.aidl @@ -16,4 +16,4 @@ package android.gui; -parcelable ParcelableVsyncEventData cpp_header "gui/VsyncEventData.h"; +parcelable ParcelableVsyncEventData cpp_header "gui/VsyncEventData.h" rust_type "gui_aidl_types_rs::VsyncEventData"; diff --git a/libs/gui/aidl/android/gui/ScreenCaptureResults.aidl b/libs/gui/aidl/android/gui/ScreenCaptureResults.aidl index 9908edd2ef..97a903515b 100644 --- a/libs/gui/aidl/android/gui/ScreenCaptureResults.aidl +++ b/libs/gui/aidl/android/gui/ScreenCaptureResults.aidl @@ -16,4 +16,4 @@ package android.gui; -parcelable ScreenCaptureResults cpp_header "gui/ScreenCaptureResults.h"; \ No newline at end of file +parcelable ScreenCaptureResults cpp_header "gui/ScreenCaptureResults.h" rust_type "gui_aidl_types_rs::ScreenCaptureResults"; \ No newline at end of file diff --git a/libs/gui/android/gui/DisplayInfo.aidl b/libs/gui/android/gui/DisplayInfo.aidl index 30c088525d..3b16724e7f 100644 --- a/libs/gui/android/gui/DisplayInfo.aidl +++ b/libs/gui/android/gui/DisplayInfo.aidl @@ -16,4 +16,4 @@ package android.gui; -parcelable DisplayInfo cpp_header "gui/DisplayInfo.h"; +parcelable DisplayInfo cpp_header "gui/DisplayInfo.h" rust_type "gui_aidl_types_rs::DisplayInfo"; diff --git a/libs/gui/android/gui/WindowInfo.aidl b/libs/gui/android/gui/WindowInfo.aidl index 2c85d155a8..b9d5ccf753 100644 --- a/libs/gui/android/gui/WindowInfo.aidl +++ b/libs/gui/android/gui/WindowInfo.aidl @@ -16,4 +16,4 @@ package android.gui; -parcelable WindowInfo cpp_header "gui/WindowInfo.h"; +parcelable WindowInfo cpp_header "gui/WindowInfo.h" rust_type "gui_aidl_types_rs::WindowInfo"; diff --git a/libs/gui/android/gui/WindowInfosUpdate.aidl b/libs/gui/android/gui/WindowInfosUpdate.aidl index 0c6109da8f..5c23e088ba 100644 --- a/libs/gui/android/gui/WindowInfosUpdate.aidl +++ b/libs/gui/android/gui/WindowInfosUpdate.aidl @@ -19,4 +19,4 @@ package android.gui; import android.gui.DisplayInfo; import android.gui.WindowInfo; -parcelable WindowInfosUpdate cpp_header "gui/WindowInfosUpdate.h"; +parcelable WindowInfosUpdate cpp_header "gui/WindowInfosUpdate.h" rust_type "gui_aidl_types_rs::WindowInfosUpdate"; diff --git a/libs/gui/rust/aidl_types/Android.bp b/libs/gui/rust/aidl_types/Android.bp new file mode 100644 index 0000000000..794f69e1e6 --- /dev/null +++ b/libs/gui/rust/aidl_types/Android.bp @@ -0,0 +1,23 @@ +rust_defaults { + name: "libgui_aidl_types_defaults", + srcs: ["src/lib.rs"], + rustlibs: [ + "libbinder_rs", + ], +} + +rust_library { + name: "libgui_aidl_types_rs", + crate_name: "gui_aidl_types_rs", + defaults: ["libgui_aidl_types_defaults"], + + // Currently necessary for host builds + // TODO(b/31559095): bionic on host should define this + target: { + darwin: { + enabled: false, + }, + }, + min_sdk_version: "VanillaIceCream", + vendor_available: true, +} diff --git a/libs/gui/rust/aidl_types/src/lib.rs b/libs/gui/rust/aidl_types/src/lib.rs new file mode 100644 index 0000000000..3d29529d23 --- /dev/null +++ b/libs/gui/rust/aidl_types/src/lib.rs @@ -0,0 +1,55 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Rust wrapper for libgui AIDL types. + +use binder::{ + binder_impl::{BorrowedParcel, UnstructuredParcelable}, + impl_deserialize_for_unstructured_parcelable, impl_serialize_for_unstructured_parcelable, + StatusCode, +}; + +macro_rules! stub_unstructured_parcelable { + ($name:ident) => { + /// Unimplemented stub parcelable. + #[derive(Debug, Default)] + pub struct $name(Option<()>); + + impl UnstructuredParcelable for $name { + fn write_to_parcel(&self, _parcel: &mut BorrowedParcel) -> Result<(), StatusCode> { + todo!() + } + + fn from_parcel(_parcel: &BorrowedParcel) -> Result { + todo!() + } + } + + impl_deserialize_for_unstructured_parcelable!($name); + impl_serialize_for_unstructured_parcelable!($name); + }; +} + +stub_unstructured_parcelable!(BitTube); +stub_unstructured_parcelable!(CaptureArgs); +stub_unstructured_parcelable!(DisplayCaptureArgs); +stub_unstructured_parcelable!(DisplayInfo); +stub_unstructured_parcelable!(LayerCaptureArgs); +stub_unstructured_parcelable!(LayerDebugInfo); +stub_unstructured_parcelable!(LayerMetadata); +stub_unstructured_parcelable!(ParcelableVsyncEventData); +stub_unstructured_parcelable!(ScreenCaptureResults); +stub_unstructured_parcelable!(VsyncEventData); +stub_unstructured_parcelable!(WindowInfo); +stub_unstructured_parcelable!(WindowInfosUpdate); -- cgit v1.2.3-59-g8ed1b From 47b795c2acb82e5d19a6e19f73ead1a6c563a3ea Mon Sep 17 00:00:00 2001 From: Charisee Date: Tue, 9 Apr 2024 02:21:16 +0000 Subject: Update needed for Rust v1.77.1 error: field `0` is never read --> frameworks/native/libs/gui/rust/aidl_types/src/lib.rs:27:26 | 27 | pub struct $name(Option<()>); | ^^^^^^^^^^ ... 44 | stub_unstructured_parcelable!(BitTube); | -------------------------------------- | | | | | field in this struct | in this macro invocation | = note: `BitTube` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis = note: `-D dead-code` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(dead_code)]` = note: this error originates in the macro `stub_unstructured_parcelable` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider changing the field to be of unit type to suppress this warning while preserving the field numbering, or remove the field | 27 | pub struct $name(()); | ~~ Bug: http://b/330185853 Test: m rust Change-Id: I8ecaba1f86e00b3ed480e423064edc47153c1244 --- libs/gui/rust/aidl_types/src/lib.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'libs/gui') diff --git a/libs/gui/rust/aidl_types/src/lib.rs b/libs/gui/rust/aidl_types/src/lib.rs index 3d29529d23..941b1f9db8 100644 --- a/libs/gui/rust/aidl_types/src/lib.rs +++ b/libs/gui/rust/aidl_types/src/lib.rs @@ -20,6 +20,7 @@ use binder::{ StatusCode, }; +#[allow(dead_code)] macro_rules! stub_unstructured_parcelable { ($name:ident) => { /// Unimplemented stub parcelable. -- cgit v1.2.3-59-g8ed1b From 6f2b3ca0733875eaba872ace8349b360e43fefab Mon Sep 17 00:00:00 2001 From: Charisee Date: Tue, 9 Apr 2024 21:06:11 +0000 Subject: Update needed for Rust v1.77.1 error: field `0` is never read --> frameworks/native/libs/gui/rust/aidl_types/src/lib.rs:28:26 | 28 | pub struct $name(Option<()>); | ^^^^^^^^^^ ... 45 | stub_unstructured_parcelable!(BitTube); | -------------------------------------- | | | | | field in this struct | in this macro invocation | = note: `BitTube` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis = note: `-D dead-code` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(dead_code)]` = note: this error originates in the macro `stub_unstructured_parcelable` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider changing the field to be of unit type to suppress this warning while preserving the field numbering, or remove the field | 28 | pub struct $name(()); Bug: http://b/330185853 Test: m rust Change-Id: I3d70ca45d88a1c3817b23696ba49a2bbf9e7b9df --- libs/gui/rust/aidl_types/src/lib.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'libs/gui') diff --git a/libs/gui/rust/aidl_types/src/lib.rs b/libs/gui/rust/aidl_types/src/lib.rs index 941b1f9db8..4e86ed6572 100644 --- a/libs/gui/rust/aidl_types/src/lib.rs +++ b/libs/gui/rust/aidl_types/src/lib.rs @@ -24,6 +24,7 @@ use binder::{ macro_rules! stub_unstructured_parcelable { ($name:ident) => { /// Unimplemented stub parcelable. + #[allow(dead_code)] #[derive(Debug, Default)] pub struct $name(Option<()>); -- cgit v1.2.3-59-g8ed1b From 2671fb8f793aef496f2d6a2a01be59de94f7fe92 Mon Sep 17 00:00:00 2001 From: Carlos Martinez Romero Date: Thu, 11 Apr 2024 15:03:03 -0700 Subject: Enable lint again after update to Rust v1.77.1 Test: Build and run Bug: 333894983 Change-Id: Iae60819c9345b44f716183d746454a35f190b9e2 --- libs/gui/rust/aidl_types/src/lib.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'libs/gui') diff --git a/libs/gui/rust/aidl_types/src/lib.rs b/libs/gui/rust/aidl_types/src/lib.rs index 4e86ed6572..fead018bbf 100644 --- a/libs/gui/rust/aidl_types/src/lib.rs +++ b/libs/gui/rust/aidl_types/src/lib.rs @@ -20,13 +20,11 @@ use binder::{ StatusCode, }; -#[allow(dead_code)] macro_rules! stub_unstructured_parcelable { ($name:ident) => { /// Unimplemented stub parcelable. - #[allow(dead_code)] #[derive(Debug, Default)] - pub struct $name(Option<()>); + pub struct $name(()); impl UnstructuredParcelable for $name { fn write_to_parcel(&self, _parcel: &mut BorrowedParcel) -> Result<(), StatusCode> { -- cgit v1.2.3-59-g8ed1b From 01b92c7891d59dc14b6690514ac932547aa4a054 Mon Sep 17 00:00:00 2001 From: Vishnu Nair Date: Mon, 15 Apr 2024 21:53:56 +0000 Subject: Avoid string allocations when checking for permissions Fixes: 334988349 Test: presubmit, pprof Change-Id: Id960aa7356ac927e57aa92a704de8691a996ab59 --- libs/gui/LayerStatePermissions.cpp | 14 +++++++------- libs/gui/include/gui/LayerStatePermissions.h | 7 +++---- 2 files changed, 10 insertions(+), 11 deletions(-) (limited to 'libs/gui') diff --git a/libs/gui/LayerStatePermissions.cpp b/libs/gui/LayerStatePermissions.cpp index 28697ca953..c467cfdc68 100644 --- a/libs/gui/LayerStatePermissions.cpp +++ b/libs/gui/LayerStatePermissions.cpp @@ -23,31 +23,31 @@ #include namespace android { -std::unordered_map LayerStatePermissions::mPermissionMap = { +std::vector> LayerStatePermissions::mPermissionMap = { // If caller has ACCESS_SURFACE_FLINGER, they automatically get ROTATE_SURFACE_FLINGER // permission, as well - {"android.permission.ACCESS_SURFACE_FLINGER", + {String16("android.permission.ACCESS_SURFACE_FLINGER"), layer_state_t::Permission::ACCESS_SURFACE_FLINGER | layer_state_t::Permission::ROTATE_SURFACE_FLINGER}, - {"android.permission.ROTATE_SURFACE_FLINGER", + {String16("android.permission.ROTATE_SURFACE_FLINGER"), layer_state_t::Permission::ROTATE_SURFACE_FLINGER}, - {"android.permission.INTERNAL_SYSTEM_WINDOW", + {String16("android.permission.INTERNAL_SYSTEM_WINDOW"), layer_state_t::Permission::INTERNAL_SYSTEM_WINDOW}, }; -static bool callingThreadHasPermission(const std::string& permission __attribute__((unused)), +static bool callingThreadHasPermission(const String16& permission __attribute__((unused)), int pid __attribute__((unused)), int uid __attribute__((unused))) { #ifndef __ANDROID_VNDK__ return uid == AID_GRAPHICS || uid == AID_SYSTEM || - PermissionCache::checkPermission(String16(permission.c_str()), pid, uid); + PermissionCache::checkPermission(permission, pid, uid); #endif // __ANDROID_VNDK__ return false; } uint32_t LayerStatePermissions::getTransactionPermissions(int pid, int uid) { uint32_t permissions = 0; - for (auto [permissionName, permissionVal] : mPermissionMap) { + for (const auto& [permissionName, permissionVal] : mPermissionMap) { if (callingThreadHasPermission(permissionName, pid, uid)) { permissions |= permissionVal; } diff --git a/libs/gui/include/gui/LayerStatePermissions.h b/libs/gui/include/gui/LayerStatePermissions.h index a90f30c621..b6588a2a82 100644 --- a/libs/gui/include/gui/LayerStatePermissions.h +++ b/libs/gui/include/gui/LayerStatePermissions.h @@ -15,15 +15,14 @@ */ #include -#include -#include - +#include +#include namespace android { class LayerStatePermissions { public: static uint32_t getTransactionPermissions(int pid, int uid); private: - static std::unordered_map mPermissionMap; + static std::vector> mPermissionMap; }; } // namespace android \ No newline at end of file -- cgit v1.2.3-59-g8ed1b From a74ba0f4f2e8766751fb3cf23b2c1dbcda476506 Mon Sep 17 00:00:00 2001 From: Vishnu Nair Date: Tue, 16 Apr 2024 11:25:06 -0700 Subject: Avoid extra locking in TransactionCompletedListener::getInstance Change-Id: I823da204e3ac94b5ad7fec8a16f16f7de57f2d49 Fixes: 333947892 Test: presubmit --- libs/gui/SurfaceComposerClient.cpp | 39 ++++++++++++++-------------- libs/gui/include/gui/SurfaceComposerClient.h | 2 ++ 2 files changed, 21 insertions(+), 20 deletions(-) (limited to 'libs/gui') diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index eb945ccce0..3743025b7b 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -706,6 +706,7 @@ void removeDeadBufferCallback(void* /*context*/, uint64_t graphicBufferId) { SurfaceComposerClient::Transaction::Transaction() { mId = generateId(); + mTransactionCompletedListener = TransactionCompletedListener::getInstance(); } SurfaceComposerClient::Transaction::Transaction(const Transaction& other) @@ -723,6 +724,7 @@ SurfaceComposerClient::Transaction::Transaction(const Transaction& other) mComposerStates = other.mComposerStates; mInputWindowCommands = other.mInputWindowCommands; mListenerCallbacks = other.mListenerCallbacks; + mTransactionCompletedListener = TransactionCompletedListener::getInstance(); } void SurfaceComposerClient::Transaction::sanitize(int pid, int uid) { @@ -1000,8 +1002,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Tr // register all surface controls for all callbackIds for this listener that is merging for (const auto& surfaceControl : currentProcessCallbackInfo.surfaceControls) { - TransactionCompletedListener::getInstance() - ->addSurfaceControlToCallbacks(currentProcessCallbackInfo, surfaceControl); + mTransactionCompletedListener->addSurfaceControlToCallbacks(currentProcessCallbackInfo, + surfaceControl); } } @@ -1354,7 +1356,7 @@ void SurfaceComposerClient::Transaction::registerSurfaceControlForCallback( auto& callbackInfo = mListenerCallbacks[TransactionCompletedListener::getIInstance()]; callbackInfo.surfaceControls.insert(sc); - TransactionCompletedListener::getInstance()->addSurfaceControlToCallbacks(callbackInfo, sc); + mTransactionCompletedListener->addSurfaceControlToCallbacks(callbackInfo, sc); } SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setPosition( @@ -1672,7 +1674,7 @@ std::shared_ptr SurfaceComposerClient::Transaction::getAndClearBuffe std::shared_ptr bufferData = std::move(s->bufferData); - TransactionCompletedListener::getInstance()->removeReleaseBufferCallback( + mTransactionCompletedListener->removeReleaseBufferCallback( bufferData->generateReleaseCallbackId()); s->what &= ~layer_state_t::eBufferChanged; s->bufferData = nullptr; @@ -1715,8 +1717,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffe bufferData->acquireFence = *fence; bufferData->flags |= BufferData::BufferDataChange::fenceChanged; } - bufferData->releaseBufferEndpoint = - IInterface::asBinder(TransactionCompletedListener::getIInstance()); + bufferData->releaseBufferEndpoint = IInterface::asBinder(mTransactionCompletedListener); setReleaseBufferCallback(bufferData.get(), callback); } @@ -1774,9 +1775,10 @@ void SurfaceComposerClient::Transaction::setReleaseBufferCallback(BufferData* bu return; } - bufferData->releaseBufferListener = TransactionCompletedListener::getIInstance(); - auto listener = TransactionCompletedListener::getInstance(); - listener->setReleaseBufferCallback(bufferData->generateReleaseCallbackId(), callback); + bufferData->releaseBufferListener = + static_cast>(mTransactionCompletedListener); + mTransactionCompletedListener->setReleaseBufferCallback(bufferData->generateReleaseCallbackId(), + callback); } SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDataspace( @@ -1932,18 +1934,15 @@ SurfaceComposerClient::Transaction::setFrameRateSelectionPriority(const spaddCallbackFunction(callbackWithContext, surfaceControls, callbackType); + mTransactionCompletedListener->addCallbackFunction(callbackWithContext, surfaceControls, + callbackType); - mListenerCallbacks[TransactionCompletedListener::getIInstance()].callbackIds.emplace( - callbackId); + mListenerCallbacks[mTransactionCompletedListener].callbackIds.emplace(callbackId); return *this; } @@ -2333,8 +2332,9 @@ SurfaceComposerClient::Transaction::setTrustedPresentationCallback( const sp& sc, TrustedPresentationCallback cb, const TrustedPresentationThresholds& thresholds, void* context, sp& outCallbackRef) { - auto listener = TransactionCompletedListener::getInstance(); - outCallbackRef = listener->addTrustedPresentationCallback(cb, sc->getLayerId(), context); + outCallbackRef = + mTransactionCompletedListener->addTrustedPresentationCallback(cb, sc->getLayerId(), + context); layer_state_t* s = getLayerState(sc); if (!s) { @@ -2351,8 +2351,7 @@ SurfaceComposerClient::Transaction::setTrustedPresentationCallback( SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::clearTrustedPresentationCallback(const sp& sc) { - auto listener = TransactionCompletedListener::getInstance(); - listener->clearTrustedPresentationCallback(sc->getLayerId()); + mTransactionCompletedListener->clearTrustedPresentationCallback(sc->getLayerId()); layer_state_t* s = getLayerState(sc); if (!s) { diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 79224e6782..49b0a7d0c9 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -431,6 +431,8 @@ public: static std::mutex sApplyTokenMutex; void releaseBufferIfOverwriting(const layer_state_t& state); static void mergeFrameTimelineInfo(FrameTimelineInfo& t, const FrameTimelineInfo& other); + // Tracks registered callbacks + sp mTransactionCompletedListener = nullptr; protected: std::unordered_map, ComposerState, IBinderHash> mComposerStates; -- cgit v1.2.3-59-g8ed1b From db164ff4e1caccc86654a39ac0def4c51e94d1c0 Mon Sep 17 00:00:00 2001 From: John Reck Date: Wed, 3 Apr 2024 16:59:28 -0400 Subject: Add allocator-v2 extended options to BufferQueue Bug: 268382490 Test: libgui_test Change-Id: If43b31eede87cddfcbfe4b24b53c1bafb453ebf2 --- libs/gui/BufferQueueProducer.cpp | 122 ++++++++++++++++++++++++-- libs/gui/IGraphicBufferProducer.cpp | 49 +++++++++++ libs/gui/Surface.cpp | 32 +++++++ libs/gui/include/gui/AdditionalOptions.h | 32 +++++++ libs/gui/include/gui/BufferQueueCore.h | 11 +++ libs/gui/include/gui/BufferQueueProducer.h | 5 ++ libs/gui/include/gui/BufferSlot.h | 7 ++ libs/gui/include/gui/IGraphicBufferProducer.h | 5 ++ libs/gui/include/gui/Surface.h | 11 +++ libs/gui/libgui_flags.aconfig | 8 ++ libs/gui/tests/Android.bp | 1 + libs/gui/tests/BufferQueue_test.cpp | 57 ++++++++++++ libs/nativewindow/include/system/window.h | 23 +++++ libs/ui/GraphicBufferAllocator.cpp | 4 + libs/ui/include/ui/Gralloc.h | 2 + libs/ui/include/ui/Gralloc5.h | 2 + libs/ui/include/ui/GraphicBufferAllocator.h | 2 + 17 files changed, 365 insertions(+), 8 deletions(-) create mode 100644 libs/gui/include/gui/AdditionalOptions.h (limited to 'libs/gui') diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index fb69fda32d..69345a971e 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -423,6 +423,11 @@ status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp* ou sp listener; bool callOnFrameDequeued = false; uint64_t bufferId = 0; // Only used if callOnFrameDequeued == true +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE) + std::vector allocOptions; + uint32_t allocOptionsGenId = 0; +#endif + { // Autolock scope std::unique_lock lock(mCore->mMutex); @@ -486,11 +491,17 @@ status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp* ou } const sp& buffer(mSlots[found].mGraphicBuffer); - if (mCore->mSharedBufferSlot == found && - buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage)) { - BQ_LOGE("dequeueBuffer: cannot re-allocate a shared" - "buffer"); + bool needsReallocation = buffer == nullptr || + buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage); + +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE) + needsReallocation |= mSlots[found].mAdditionalOptionsGenerationId != + mCore->mAdditionalOptionsGenerationId; +#endif + + if (mCore->mSharedBufferSlot == found && needsReallocation) { + BQ_LOGE("dequeueBuffer: cannot re-allocate a shared buffer"); return BAD_VALUE; } @@ -505,9 +516,7 @@ status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp* ou mSlots[found].mBufferState.dequeue(); - if ((buffer == nullptr) || - buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage)) - { + if (needsReallocation) { if (CC_UNLIKELY(ATRACE_ENABLED())) { if (buffer == nullptr) { ATRACE_FORMAT_INSTANT("%s buffer reallocation: null", mConsumerName.c_str()); @@ -530,6 +539,10 @@ status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp* ou mSlots[found].mFence = Fence::NO_FENCE; mCore->mBufferAge = 0; mCore->mIsAllocating = true; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE) + allocOptions = mCore->mAdditionalOptions; + allocOptionsGenId = mCore->mAdditionalOptionsGenerationId; +#endif returnFlags |= BUFFER_NEEDS_REALLOCATION; } else { @@ -575,9 +588,29 @@ status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp* ou if (returnFlags & BUFFER_NEEDS_REALLOCATION) { BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot); + +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE) + std::vector tempOptions; + tempOptions.reserve(allocOptions.size()); + for (const auto& it : allocOptions) { + tempOptions.emplace_back(it.name.c_str(), it.value); + } + const GraphicBufferAllocator::AllocationRequest allocRequest = { + .importBuffer = true, + .width = width, + .height = height, + .format = format, + .layerCount = BQ_LAYER_COUNT, + .usage = usage, + .requestorName = {mConsumerName.c_str(), mConsumerName.size()}, + .extras = std::move(tempOptions), + }; + sp graphicBuffer = new GraphicBuffer(allocRequest); +#else sp graphicBuffer = new GraphicBuffer(width, height, format, BQ_LAYER_COUNT, usage, {mConsumerName.c_str(), mConsumerName.size()}); +#endif status_t error = graphicBuffer->initCheck(); @@ -587,6 +620,9 @@ status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp* ou if (error == NO_ERROR && !mCore->mIsAbandoned) { graphicBuffer->setGenerationNumber(mCore->mGenerationNumber); mSlots[*outSlot].mGraphicBuffer = graphicBuffer; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE) + mSlots[*outSlot].mAdditionalOptionsGenerationId = allocOptionsGenId; +#endif callOnFrameDequeued = true; bufferId = mSlots[*outSlot].mGraphicBuffer->getId(); } @@ -1342,6 +1378,9 @@ status_t BufferQueueProducer::connect(const sp& listener, } mCore->mAllowAllocation = true; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE) + mCore->mAdditionalOptions.clear(); +#endif VALIDATE_CONSISTENCY(); return status; } @@ -1410,6 +1449,9 @@ status_t BufferQueueProducer::disconnect(int api, DisconnectMode mode) { mCore->mSidebandStream.clear(); mCore->mDequeueCondition.notify_all(); mCore->mAutoPrerotation = false; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE) + mCore->mAdditionalOptions.clear(); +#endif listener = mCore->mConsumerListener; } else if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) { BQ_LOGE("disconnect: not connected (req=%d)", api); @@ -1462,6 +1504,10 @@ void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height, PixelFormat allocFormat = PIXEL_FORMAT_UNKNOWN; uint64_t allocUsage = 0; std::string allocName; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE) + std::vector allocOptions; + uint32_t allocOptionsGenId = 0; +#endif { // Autolock scope std::unique_lock lock(mCore->mMutex); mCore->waitWhileAllocatingLocked(lock); @@ -1490,14 +1536,42 @@ void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height, allocUsage = usage | mCore->mConsumerUsageBits; allocName.assign(mCore->mConsumerName.c_str(), mCore->mConsumerName.size()); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE) + allocOptions = mCore->mAdditionalOptions; + allocOptionsGenId = mCore->mAdditionalOptionsGenerationId; +#endif + mCore->mIsAllocating = true; + } // Autolock scope +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE) + std::vector tempOptions; + tempOptions.reserve(allocOptions.size()); + for (const auto& it : allocOptions) { + tempOptions.emplace_back(it.name.c_str(), it.value); + } + const GraphicBufferAllocator::AllocationRequest allocRequest = { + .importBuffer = true, + .width = allocWidth, + .height = allocHeight, + .format = allocFormat, + .layerCount = BQ_LAYER_COUNT, + .usage = allocUsage, + .requestorName = allocName, + .extras = std::move(tempOptions), + }; +#endif + Vector> buffers; for (size_t i = 0; i < newBufferCount; ++i) { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE) + sp graphicBuffer = new GraphicBuffer(allocRequest); +#else sp graphicBuffer = new GraphicBuffer( allocWidth, allocHeight, allocFormat, BQ_LAYER_COUNT, allocUsage, allocName); +#endif status_t result = graphicBuffer->initCheck(); @@ -1524,8 +1598,12 @@ void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height, PixelFormat checkFormat = format != 0 ? format : mCore->mDefaultBufferFormat; uint64_t checkUsage = usage | mCore->mConsumerUsageBits; + bool allocOptionsChanged = false; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE) + allocOptionsChanged = allocOptionsGenId != mCore->mAdditionalOptionsGenerationId; +#endif if (checkWidth != allocWidth || checkHeight != allocHeight || - checkFormat != allocFormat || checkUsage != allocUsage) { + checkFormat != allocFormat || checkUsage != allocUsage || allocOptionsChanged) { // Something changed while we released the lock. Retry. BQ_LOGV("allocateBuffers: size/format/usage changed while allocating. Retrying."); mCore->mIsAllocating = false; @@ -1543,6 +1621,9 @@ void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height, mCore->clearBufferSlotLocked(*slot); // Clean up the slot first mSlots[*slot].mGraphicBuffer = buffers[i]; mSlots[*slot].mFence = Fence::NO_FENCE; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE) + mSlots[*slot].mAdditionalOptionsGenerationId = allocOptionsGenId; +#endif // freeBufferLocked puts this slot on the free slots list. Since // we then attached a buffer, move the slot to free buffer list. @@ -1778,4 +1859,29 @@ status_t BufferQueueProducer::setFrameRate(float frameRate, int8_t compatibility } #endif +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE) +status_t BufferQueueProducer::setAdditionalOptions( + const std::vector& options) { + ATRACE_CALL(); + BQ_LOGV("setAdditionalOptions, size = %zu", options.size()); + + if (!GraphicBufferAllocator::get().supportsAdditionalOptions()) { + return INVALID_OPERATION; + } + + std::lock_guard lock(mCore->mMutex); + + if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) { + BQ_LOGE("setAdditionalOptions: BufferQueue not connected, cannot set additional options"); + return NO_INIT; + } + + if (mCore->mAdditionalOptions != options) { + mCore->mAdditionalOptions = options; + mCore->mAdditionalOptionsGenerationId++; + } + return NO_ERROR; +} +#endif + } // namespace android diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp index e81c098b85..09144806ee 100644 --- a/libs/gui/IGraphicBufferProducer.cpp +++ b/libs/gui/IGraphicBufferProducer.cpp @@ -80,6 +80,7 @@ enum { QUERY_MULTIPLE, GET_LAST_QUEUED_BUFFER2, SET_FRAME_RATE, + SET_ADDITIONAL_OPTIONS, }; class BpGraphicBufferProducer : public BpInterface @@ -778,6 +779,25 @@ public: return result; } #endif +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE) + virtual status_t setAdditionalOptions(const std::vector& options) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + if (options.size() > 100) { + return BAD_VALUE; + } + data.writeInt32(options.size()); + for (const auto& it : options) { + data.writeCString(it.name.c_str()); + data.writeInt64(it.value); + } + status_t result = remote()->transact(SET_ADDITIONAL_OPTIONS, data, &reply); + if (result == NO_ERROR) { + result = reply.readInt32(); + } + return result; + } +#endif }; // Out-of-line virtual method definition to trigger vtable emission in this @@ -981,6 +1001,13 @@ status_t IGraphicBufferProducer::setFrameRate(float /*frameRate*/, int8_t /*comp } #endif +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE) +status_t IGraphicBufferProducer::setAdditionalOptions(const std::vector&) { + // No-op for IGBP other than BufferQueue. + return INVALID_OPERATION; +} +#endif + status_t IGraphicBufferProducer::exportToParcel(Parcel* parcel) { status_t res = OK; res = parcel->writeUint32(USE_BUFFER_QUEUE); @@ -1532,6 +1559,28 @@ status_t BnGraphicBufferProducer::onTransact( reply->writeInt32(result); return NO_ERROR; } +#endif +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE) + case SET_ADDITIONAL_OPTIONS: { + CHECK_INTERFACE(IGraphicBuffer, data, reply); + int optionCount = data.readInt32(); + if (optionCount < 0 || optionCount > 100) { + return BAD_VALUE; + } + std::vector opts; + opts.reserve(optionCount); + for (int i = 0; i < optionCount; i++) { + const char* name = data.readCString(); + int64_t value = 0; + if (name == nullptr || data.readInt64(&value) != NO_ERROR) { + return BAD_VALUE; + } + opts.emplace_back(name, value); + } + status_t result = setAdditionalOptions(opts); + reply->writeInt32(result); + return NO_ERROR; + } #endif } return BBinder::onTransact(code, data, reply, flags); diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 086544e48a..87fd448f0c 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -1475,6 +1475,9 @@ int Surface::perform(int operation, va_list args) case NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO: res = dispatchSetFrameTimelineInfo(args); break; + case NATIVE_WINDOW_SET_BUFFERS_ADDITIONAL_OPTIONS: + res = dispatchSetAdditionalOptions(args); + break; default: res = NAME_NOT_FOUND; break; @@ -1833,6 +1836,24 @@ int Surface::dispatchSetFrameTimelineInfo(va_list args) { return setFrameTimelineInfo(nativeWindowFtlInfo.frameNumber, ftlInfo); } +int Surface::dispatchSetAdditionalOptions(va_list args) { + ATRACE_CALL(); + +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE) + const AHardwareBufferLongOptions* opts = va_arg(args, const AHardwareBufferLongOptions*); + const size_t optsSize = va_arg(args, size_t); + std::vector convertedOpts; + convertedOpts.reserve(optsSize); + for (size_t i = 0; i < optsSize; i++) { + convertedOpts.emplace_back(opts[i].name, opts[i].value); + } + return setAdditionalOptions(convertedOpts); +#else + (void)args; + return INVALID_OPERATION; +#endif +} + bool Surface::transformToDisplayInverse() const { return (mTransform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) == NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY; @@ -2619,6 +2640,17 @@ status_t Surface::setFrameTimelineInfo(uint64_t /*frameNumber*/, return BAD_VALUE; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE) +status_t Surface::setAdditionalOptions(const std::vector& options) { + if (!GraphicBufferAllocator::get().supportsAdditionalOptions()) { + return INVALID_OPERATION; + } + + Mutex::Autolock lock(mMutex); + return mGraphicBufferProducer->setAdditionalOptions(options); +} +#endif + sp Surface::getSurfaceControlHandle() const { Mutex::Autolock lock(mMutex); return mSurfaceControlHandle; diff --git a/libs/gui/include/gui/AdditionalOptions.h b/libs/gui/include/gui/AdditionalOptions.h new file mode 100644 index 0000000000..87cb913675 --- /dev/null +++ b/libs/gui/include/gui/AdditionalOptions.h @@ -0,0 +1,32 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +namespace android::gui { +// Additional options to pass to AHardwareBuffer_allocateWithOptions. +// See also allocator-v2's BufferDescriptorInfo.aidl +struct AdditionalOptions { + std::string name; + int64_t value; + + bool operator==(const AdditionalOptions& other) const { + return value == other.value && name == other.name; + } +}; +} // namespace android::gui diff --git a/libs/gui/include/gui/BufferQueueCore.h b/libs/gui/include/gui/BufferQueueCore.h index 22c2be7bc7..bb52c8ec88 100644 --- a/libs/gui/include/gui/BufferQueueCore.h +++ b/libs/gui/include/gui/BufferQueueCore.h @@ -17,6 +17,9 @@ #ifndef ANDROID_GUI_BUFFERQUEUECORE_H #define ANDROID_GUI_BUFFERQUEUECORE_H +#include + +#include #include #include #include @@ -357,6 +360,14 @@ private: // This allows the consumer to acquire an additional buffer if that buffer is not droppable and // will eventually be released or acquired by the consumer. bool mAllowExtraAcquire = false; + +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE) + // Additional options to pass when allocating GraphicBuffers. + // GenerationID changes when the options change, indicating reallocation is required + uint32_t mAdditionalOptionsGenerationId = 0; + std::vector mAdditionalOptions; +#endif + }; // class BufferQueueCore } // namespace android diff --git a/libs/gui/include/gui/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h index de47483dca..37a960708c 100644 --- a/libs/gui/include/gui/BufferQueueProducer.h +++ b/libs/gui/include/gui/BufferQueueProducer.h @@ -17,6 +17,7 @@ #ifndef ANDROID_GUI_BUFFERQUEUEPRODUCER_H #define ANDROID_GUI_BUFFERQUEUEPRODUCER_H +#include #include #include @@ -208,6 +209,10 @@ public: int8_t changeFrameRateStrategy) override; #endif +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE) + status_t setAdditionalOptions(const std::vector& options) override; +#endif + protected: // see IGraphicsBufferProducer::setMaxDequeuedBufferCount, but with the ability to retrieve the // total maximum buffer count for the buffer queue (dequeued AND acquired) diff --git a/libs/gui/include/gui/BufferSlot.h b/libs/gui/include/gui/BufferSlot.h index 57704b1d09..5b32710135 100644 --- a/libs/gui/include/gui/BufferSlot.h +++ b/libs/gui/include/gui/BufferSlot.h @@ -17,6 +17,8 @@ #ifndef ANDROID_GUI_BUFFERSLOT_H #define ANDROID_GUI_BUFFERSLOT_H +#include + #include #include @@ -230,6 +232,11 @@ struct BufferSlot { // producer. If so, it needs to set the BUFFER_NEEDS_REALLOCATION flag when // dequeued to prevent the producer from using a stale cached buffer. bool mNeedsReallocation; + +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE) + // The generation id of the additional options that mGraphicBuffer was allocated with + uint32_t mAdditionalOptionsGenerationId = 0; +#endif }; } // namespace android diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h index 7639e709ca..8fca9460aa 100644 --- a/libs/gui/include/gui/IGraphicBufferProducer.h +++ b/libs/gui/include/gui/IGraphicBufferProducer.h @@ -31,6 +31,7 @@ #include #include +#include #include #include @@ -684,6 +685,10 @@ public: int8_t changeFrameRateStrategy); #endif +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE) + virtual status_t setAdditionalOptions(const std::vector& options); +#endif + struct RequestBufferOutput : public Flattenable { RequestBufferOutput() = default; diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h index 39a59e42aa..bdcaaf2866 100644 --- a/libs/gui/include/gui/Surface.h +++ b/libs/gui/include/gui/Surface.h @@ -215,6 +215,16 @@ public: int8_t changeFrameRateStrategy); virtual status_t setFrameTimelineInfo(uint64_t frameNumber, const FrameTimelineInfo& info); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE) + /** + * Set additional options to be passed when allocating a buffer. Only valid if IAllocator-V2 + * or newer is available, otherwise will return INVALID_OPERATION. Only allowed to be called + * after connect and options are cleared when disconnect happens. Returns NO_INIT if not + * connected + */ + status_t setAdditionalOptions(const std::vector& options); +#endif + protected: virtual ~Surface(); @@ -302,6 +312,7 @@ private: int dispatchGetLastQueuedBuffer(va_list args); int dispatchGetLastQueuedBuffer2(va_list args); int dispatchSetFrameTimelineInfo(va_list args); + int dispatchSetAdditionalOptions(va_list args); std::mutex mNameMutex; std::string mName; diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig index 38646992da..a902a8c5cb 100644 --- a/libs/gui/libgui_flags.aconfig +++ b/libs/gui/libgui_flags.aconfig @@ -16,3 +16,11 @@ flag { bug: "310927247" is_fixed_read_only: true } + +flag { + name: "bq_extendedallocate" + namespace: "core_graphics" + description: "Add BQ support for allocate with extended options" + bug: "268382490" + is_fixed_read_only: true +} diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp index 2faa330426..ea8acbbb72 100644 --- a/libs/gui/tests/Android.bp +++ b/libs/gui/tests/Android.bp @@ -24,6 +24,7 @@ cc_test { "-Wextra", "-Wthread-safety", "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_BQ_SETFRAMERATE=true", + "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_BQ_EXTENDEDALLOCATE=true", ], srcs: [ diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp index 1ec6f915f7..272c5ed2b4 100644 --- a/libs/gui/tests/BufferQueue_test.cpp +++ b/libs/gui/tests/BufferQueue_test.cpp @@ -28,6 +28,8 @@ #include +#include + #include #include #include @@ -47,6 +49,10 @@ using namespace std::chrono_literals; +static bool IsCuttlefish() { + return ::android::base::GetProperty("ro.product.board", "") == "cutf"; +} + namespace android { using namespace com::android::graphics::libgui; @@ -1439,4 +1445,55 @@ TEST(BufferQueueThreading, TestProducerDequeueConsumerDestroy) { EXPECT_EQ(nullptr, bufferConsumer.get()); } +TEST_F(BufferQueueTest, TestAdditionalOptions) { + sp producer; + sp consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + + sp bufferConsumer = + sp::make(consumer, GRALLOC_USAGE_SW_READ_OFTEN, 2); + ASSERT_NE(nullptr, bufferConsumer.get()); + sp surface = sp::make(producer); + native_window_set_buffers_format(surface.get(), PIXEL_FORMAT_RGBA_8888); + native_window_set_buffers_dimensions(surface.get(), 100, 100); + + std::array extras = {{ + {.name = "android.hardware.graphics.common.Dataspace", ADATASPACE_DISPLAY_P3}, + }}; + + ASSERT_EQ(NO_INIT, + native_window_set_buffers_additional_options(surface.get(), extras.data(), + extras.size())); + + if (!IsCuttlefish()) { + GTEST_SKIP() << "Not cuttlefish"; + } + + ASSERT_EQ(OK, native_window_api_connect(surface.get(), NATIVE_WINDOW_API_CPU)); + ASSERT_EQ(OK, + native_window_set_buffers_additional_options(surface.get(), extras.data(), + extras.size())); + + ANativeWindowBuffer* windowBuffer = nullptr; + int fence = -1; + ASSERT_EQ(OK, ANativeWindow_dequeueBuffer(surface.get(), &windowBuffer, &fence)); + + AHardwareBuffer* buffer = ANativeWindowBuffer_getHardwareBuffer(windowBuffer); + ASSERT_TRUE(buffer); + ADataSpace dataSpace = AHardwareBuffer_getDataSpace(buffer); + EXPECT_EQ(ADATASPACE_DISPLAY_P3, dataSpace); + + ANativeWindow_cancelBuffer(surface.get(), windowBuffer, -1); + + // Check that reconnecting properly clears the options + ASSERT_EQ(OK, native_window_api_disconnect(surface.get(), NATIVE_WINDOW_API_CPU)); + ASSERT_EQ(OK, native_window_api_connect(surface.get(), NATIVE_WINDOW_API_CPU)); + + ASSERT_EQ(OK, ANativeWindow_dequeueBuffer(surface.get(), &windowBuffer, &fence)); + buffer = ANativeWindowBuffer_getHardwareBuffer(windowBuffer); + ASSERT_TRUE(buffer); + dataSpace = AHardwareBuffer_getDataSpace(buffer); + EXPECT_EQ(ADATASPACE_UNKNOWN, dataSpace); +} + } // namespace android diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h index 969a5cff05..33c303ae71 100644 --- a/libs/nativewindow/include/system/window.h +++ b/libs/nativewindow/include/system/window.h @@ -41,6 +41,8 @@ #include #include +#include + // system/window.h is a superset of the vndk and apex apis #include #include @@ -257,6 +259,7 @@ enum { NATIVE_WINDOW_SET_QUERY_INTERCEPTOR = 47, /* private */ NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO = 48, /* private */ NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER2 = 49, /* private */ + NATIVE_WINDOW_SET_BUFFERS_ADDITIONAL_OPTIONS = 50, // clang-format on }; @@ -1182,6 +1185,26 @@ static inline int native_window_set_frame_timeline_info( return window->perform(window, NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO, frameTimelineInfo); } +/** + * native_window_set_buffers_additional_options(..., ExtendableType* additionalOptions, size_t size) + * All buffers dequeued after this call will have the additionalOptions specified. + * + * This must only be called after api_connect, otherwise NO_INIT is returned. The options are + * cleared in api_disconnect & api_connect + * + * If IAllocator is not v2 or newer this method returns INVALID_OPERATION + * + * \return NO_ERROR on success. + * \return NO_INIT if no api is connected + * \return INVALID_OPERATION if additional option support is not available + */ +static inline int native_window_set_buffers_additional_options( + struct ANativeWindow* window, const AHardwareBufferLongOptions* additionalOptions, + size_t additionalOptionsSize) { + return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_ADDITIONAL_OPTIONS, additionalOptions, + additionalOptionsSize); +} + // ------------------------------------------------------------------------------------------------ // Candidates for APEX visibility // These functions are planned to be made stable for APEX modules, but have not diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp index 98082fb81e..1ebe5973fa 100644 --- a/libs/ui/GraphicBufferAllocator.cpp +++ b/libs/ui/GraphicBufferAllocator.cpp @@ -291,5 +291,9 @@ status_t GraphicBufferAllocator::free(buffer_handle_t handle) return NO_ERROR; } +bool GraphicBufferAllocator::supportsAdditionalOptions() const { + return mAllocator->supportsAdditionalOptions(); +} + // --------------------------------------------------------------------------- }; // namespace android diff --git a/libs/ui/include/ui/Gralloc.h b/libs/ui/include/ui/Gralloc.h index e6015e0b5e..4167dcbab1 100644 --- a/libs/ui/include/ui/Gralloc.h +++ b/libs/ui/include/ui/Gralloc.h @@ -226,6 +226,8 @@ public: const GraphicBufferAllocator::AllocationRequest&) const { return GraphicBufferAllocator::AllocationResult(UNKNOWN_TRANSACTION); } + + virtual bool supportsAdditionalOptions() const { return false; } }; } // namespace android diff --git a/libs/ui/include/ui/Gralloc5.h b/libs/ui/include/ui/Gralloc5.h index f9e8f5e9fd..5aa5019603 100644 --- a/libs/ui/include/ui/Gralloc5.h +++ b/libs/ui/include/ui/Gralloc5.h @@ -178,6 +178,8 @@ public: [[nodiscard]] GraphicBufferAllocator::AllocationResult allocate( const GraphicBufferAllocator::AllocationRequest&) const override; + bool supportsAdditionalOptions() const override { return true; } + private: const Gralloc5Mapper &mMapper; std::shared_ptr mAllocator; diff --git a/libs/ui/include/ui/GraphicBufferAllocator.h b/libs/ui/include/ui/GraphicBufferAllocator.h index 8f461e193b..bbb2d77058 100644 --- a/libs/ui/include/ui/GraphicBufferAllocator.h +++ b/libs/ui/include/ui/GraphicBufferAllocator.h @@ -107,6 +107,8 @@ public: void dump(std::string& res, bool less = true) const; static void dumpToSystemLog(bool less = true); + bool supportsAdditionalOptions() const; + protected: struct alloc_rec_t { uint32_t width; -- cgit v1.2.3-59-g8ed1b From 38d7706ba5c5f2dc6c07df8bde9b4fc8c114ccf7 Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Wed, 17 Apr 2024 11:23:29 -0400 Subject: DisplayEventReceiver: static_assert the size of an Event This verifies we do not have a cross ABI size issue. Bug: 332230323 Test: m -j Flag: Build only Change-Id: I222243f7307ba008887b383eb0fa079a5753f5e6 --- libs/gui/include/gui/DisplayEventReceiver.h | 1 + 1 file changed, 1 insertion(+) (limited to 'libs/gui') diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h index 8c1103bfc2..4dbf9e1929 100644 --- a/libs/gui/include/gui/DisplayEventReceiver.h +++ b/libs/gui/include/gui/DisplayEventReceiver.h @@ -119,6 +119,7 @@ public: HdcpLevelsChange hdcpLevelsChange; }; }; + static_assert(sizeof(Event) == 216); public: /* -- cgit v1.2.3-59-g8ed1b From 490ccc99d384e0901bfc0d26300738d086abd4c1 Mon Sep 17 00:00:00 2001 From: Arpit Singh Date: Tue, 30 Apr 2024 14:26:21 +0000 Subject: Rename InputConfig field SENSITIVE_FOR_TRACING to SENSITIVE_FOR_PRIVACY Making this field more generic to cater for wider use cases. In addition to input tracing this field will also be used to hide privacy sensitive input interactions from mirrored displays. Test: presubmit Bug: 325252005 Change-Id: I12c34ed5bfc320976033d2bd040b447654d906c0 --- libs/gui/include/gui/WindowInfo.h | 4 ++-- libs/input/android/os/InputConfig.aidl | 6 ++++-- .../inputflinger/dispatcher/trace/InputTracer.cpp | 2 +- services/inputflinger/tests/FakeWindows.h | 2 +- .../FrontEnd/LayerSnapshotBuilder.cpp | 4 ++-- .../tests/unittests/LayerSnapshotTest.cpp | 22 +++++++++++----------- 6 files changed, 21 insertions(+), 19 deletions(-) (limited to 'libs/gui') diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h index e4f1890c76..b73e497032 100644 --- a/libs/gui/include/gui/WindowInfo.h +++ b/libs/gui/include/gui/WindowInfo.h @@ -178,8 +178,8 @@ struct WindowInfo : public Parcelable { static_cast(os::InputConfig::CLONE), GLOBAL_STYLUS_BLOCKS_TOUCH = static_cast(os::InputConfig::GLOBAL_STYLUS_BLOCKS_TOUCH), - SENSITIVE_FOR_TRACING = - static_cast(os::InputConfig::SENSITIVE_FOR_TRACING), + SENSITIVE_FOR_PRIVACY = + static_cast(os::InputConfig::SENSITIVE_FOR_PRIVACY), // clang-format on }; diff --git a/libs/input/android/os/InputConfig.aidl b/libs/input/android/os/InputConfig.aidl index 6b97cbbc59..da62e03821 100644 --- a/libs/input/android/os/InputConfig.aidl +++ b/libs/input/android/os/InputConfig.aidl @@ -159,10 +159,12 @@ enum InputConfig { GLOBAL_STYLUS_BLOCKS_TOUCH = 1 << 17, /** - * InputConfig used to indicate that this window is sensitive for tracing. + * InputConfig used to indicate that this window is privacy sensitive. This may be used to + * redact input interactions from tracing or screen mirroring. + * * This must be set on windows that use {@link WindowManager.LayoutParams#FLAG_SECURE}, * but it may also be set without setting FLAG_SECURE. The tracing configuration will * determine how these sensitive events are eventually traced. */ - SENSITIVE_FOR_TRACING = 1 << 18, + SENSITIVE_FOR_PRIVACY = 1 << 18, } diff --git a/services/inputflinger/dispatcher/trace/InputTracer.cpp b/services/inputflinger/dispatcher/trace/InputTracer.cpp index 4931a5f5dd..a1a87afd04 100644 --- a/services/inputflinger/dispatcher/trace/InputTracer.cpp +++ b/services/inputflinger/dispatcher/trace/InputTracer.cpp @@ -88,7 +88,7 @@ InputTargetInfo getTargetInfo(const InputTarget& target) { } const auto& info = *target.windowHandle->getInfo(); const bool isSensitiveTarget = - info.inputConfig.test(gui::WindowInfo::InputConfig::SENSITIVE_FOR_TRACING); + info.inputConfig.test(gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY); return {target.windowHandle->getInfo()->ownerUid, isSensitiveTarget}; } diff --git a/services/inputflinger/tests/FakeWindows.h b/services/inputflinger/tests/FakeWindows.h index 26c2b4b1e7..6cd76b229d 100644 --- a/services/inputflinger/tests/FakeWindows.h +++ b/services/inputflinger/tests/FakeWindows.h @@ -164,7 +164,7 @@ public: using namespace ftl::flag_operators; mInfo.layoutParamsFlags &= ~gui::WindowInfo::Flag::SECURE; } - mInfo.setInputConfig(InputConfig::SENSITIVE_FOR_TRACING, secure); + mInfo.setInputConfig(InputConfig::SENSITIVE_FOR_PRIVACY, secure); } inline void setInterceptsStylus(bool interceptsStylus) { diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp index a2b53297ab..2ff0facdba 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp @@ -1061,8 +1061,8 @@ void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot, } if (snapshot.isSecure || - parentSnapshot.inputInfo.inputConfig.test(InputConfig::SENSITIVE_FOR_TRACING)) { - snapshot.inputInfo.inputConfig |= InputConfig::SENSITIVE_FOR_TRACING; + parentSnapshot.inputInfo.inputConfig.test(InputConfig::SENSITIVE_FOR_PRIVACY)) { + snapshot.inputInfo.inputConfig |= InputConfig::SENSITIVE_FOR_PRIVACY; } updateVisibility(snapshot, snapshot.isVisible); diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp index 7c6cff0576..82adadc368 100644 --- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp @@ -1204,34 +1204,34 @@ TEST_F(LayerSnapshotTest, setSensitiveForTracingConfigForSecureLayers) { UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); EXPECT_TRUE(getSnapshot(11)->inputInfo.inputConfig.test( - gui::WindowInfo::InputConfig::SENSITIVE_FOR_TRACING)); + gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY)); EXPECT_TRUE(getSnapshot(111)->inputInfo.inputConfig.test( - gui::WindowInfo::InputConfig::SENSITIVE_FOR_TRACING)); + gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY)); EXPECT_FALSE(getSnapshot(1)->inputInfo.inputConfig.test( - gui::WindowInfo::InputConfig::SENSITIVE_FOR_TRACING)); + gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY)); EXPECT_FALSE(getSnapshot(12)->inputInfo.inputConfig.test( - gui::WindowInfo::InputConfig::SENSITIVE_FOR_TRACING)); + gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY)); EXPECT_FALSE(getSnapshot(2)->inputInfo.inputConfig.test( - gui::WindowInfo::InputConfig::SENSITIVE_FOR_TRACING)); + gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY)); } TEST_F(LayerSnapshotTest, setSensitiveForTracingFromInputWindowHandle) { setInputInfo(11, [](auto& inputInfo) { - inputInfo.inputConfig |= gui::WindowInfo::InputConfig::SENSITIVE_FOR_TRACING; + inputInfo.inputConfig |= gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY; }); UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); EXPECT_TRUE(getSnapshot(11)->inputInfo.inputConfig.test( - gui::WindowInfo::InputConfig::SENSITIVE_FOR_TRACING)); + gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY)); EXPECT_TRUE(getSnapshot(111)->inputInfo.inputConfig.test( - gui::WindowInfo::InputConfig::SENSITIVE_FOR_TRACING)); + gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY)); EXPECT_FALSE(getSnapshot(1)->inputInfo.inputConfig.test( - gui::WindowInfo::InputConfig::SENSITIVE_FOR_TRACING)); + gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY)); EXPECT_FALSE(getSnapshot(12)->inputInfo.inputConfig.test( - gui::WindowInfo::InputConfig::SENSITIVE_FOR_TRACING)); + gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY)); EXPECT_FALSE(getSnapshot(2)->inputInfo.inputConfig.test( - gui::WindowInfo::InputConfig::SENSITIVE_FOR_TRACING)); + gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY)); } // b/314350323 -- cgit v1.2.3-59-g8ed1b From 13bf76a87d9113d60f39e645c5453bf18d0a158d Mon Sep 17 00:00:00 2001 From: Linnan Li Date: Sun, 5 May 2024 19:18:02 +0800 Subject: Use a strongly typed LogicalDisplayId for displayId(2/n) Currently, we use int32_t for displayId, which is not a safe type, and it may also lead to misdefinition of types. Here, we introduce LogicalDisplayId as a strong type for displayId and move all contents of constants.h into LogicalDisplayId.h. Bug: 339106983 Test: atest inputflinger_tests Test: atest InputTests Test: m checkinput Test: m libsurfaceflinger_unittest Test: presubmit Change-Id: If44e56f69553d095af5adb59b595e4a852ab32ce Signed-off-by: Linnan Li --- include/input/DisplayViewport.h | 16 +- include/input/Input.h | 15 +- include/input/InputDevice.h | 6 +- include/input/InputEventBuilders.h | 9 +- include/input/InputTransport.h | 13 +- libs/gui/DisplayInfo.cpp | 8 +- libs/gui/WindowInfo.cpp | 8 +- libs/gui/include/gui/DisplayInfo.h | 4 +- libs/gui/include/gui/WindowInfo.h | 4 +- libs/gui/include/gui/constants.h | 37 ---- libs/gui/tests/DisplayInfo_test.cpp | 2 +- libs/gui/tests/EndToEndNativeInputTest.cpp | 198 +++++++++-------- libs/gui/tests/WindowInfo_test.cpp | 2 +- libs/input/Input.cpp | 43 ++-- libs/input/InputConsumer.cpp | 23 +- libs/input/InputConsumerNoResampling.cpp | 12 +- libs/input/InputDevice.cpp | 6 +- libs/input/InputTransport.cpp | 20 +- libs/input/KeyCharacterMap.cpp | 7 +- libs/input/tests/InputEvent_test.cpp | 9 +- .../InputPublisherAndConsumerNoResampling_test.cpp | 26 ++- .../input/tests/InputPublisherAndConsumer_test.cpp | 37 ++-- libs/input/tests/MotionPredictor_test.cpp | 5 +- libs/input/tests/TouchResampling_test.cpp | 3 +- libs/input/tests/VelocityTracker_test.cpp | 5 +- libs/input/tests/VerifiedInputEvent_test.cpp | 12 +- libs/ui/include/ui/LogicalDisplayId.h | 56 +++++ services/inputflinger/InputCommonConverter.cpp | 2 +- services/inputflinger/InputFilter.cpp | 2 +- services/inputflinger/InputFilterCallbacks.cpp | 6 +- services/inputflinger/InputReaderBase.cpp | 4 +- services/inputflinger/NotifyArgs.cpp | 4 +- services/inputflinger/PointerChoreographer.cpp | 74 ++++--- services/inputflinger/PointerChoreographer.h | 52 +++-- .../benchmarks/InputDispatcher_benchmarks.cpp | 7 +- .../inputflinger/dispatcher/CancelationOptions.h | 2 +- services/inputflinger/dispatcher/Entry.cpp | 46 ++-- services/inputflinger/dispatcher/Entry.h | 25 ++- services/inputflinger/dispatcher/FocusResolver.cpp | 26 +-- services/inputflinger/dispatcher/FocusResolver.h | 22 +- .../inputflinger/dispatcher/InputDispatcher.cpp | 243 +++++++++++---------- services/inputflinger/dispatcher/InputDispatcher.h | 83 +++---- services/inputflinger/dispatcher/InputState.cpp | 3 +- services/inputflinger/dispatcher/InputState.h | 6 +- services/inputflinger/dispatcher/InputTarget.h | 1 - .../dispatcher/include/InputDispatcherInterface.h | 23 +- .../include/InputDispatcherPolicyInterface.h | 8 +- .../trace/AndroidInputEventProtoConverter.cpp | 4 +- .../trace/InputTracingBackendInterface.h | 4 +- services/inputflinger/include/InputReaderBase.h | 11 +- services/inputflinger/include/NotifyArgs.h | 22 +- services/inputflinger/include/NotifyArgsBuilders.h | 9 +- .../include/PointerChoreographerPolicyInterface.h | 3 +- .../include/PointerControllerInterface.h | 6 +- services/inputflinger/reader/InputDevice.cpp | 6 +- services/inputflinger/reader/InputReader.cpp | 7 +- services/inputflinger/reader/include/InputDevice.h | 2 +- services/inputflinger/reader/include/InputReader.h | 2 +- .../mapper/CapturedTouchpadEventConverter.cpp | 3 +- .../reader/mapper/CursorInputMapper.cpp | 13 +- .../inputflinger/reader/mapper/CursorInputMapper.h | 4 +- services/inputflinger/reader/mapper/InputMapper.h | 2 +- .../reader/mapper/JoystickInputMapper.cpp | 2 +- .../reader/mapper/KeyboardInputMapper.cpp | 6 +- .../reader/mapper/KeyboardInputMapper.h | 4 +- .../reader/mapper/RotaryEncoderInputMapper.cpp | 2 +- .../reader/mapper/TouchCursorInputMapperCommon.cpp | 4 +- .../reader/mapper/TouchCursorInputMapperCommon.h | 2 +- .../reader/mapper/TouchInputMapper.cpp | 23 +- .../inputflinger/reader/mapper/TouchInputMapper.h | 12 +- .../reader/mapper/TouchpadInputMapper.cpp | 9 +- .../reader/mapper/TouchpadInputMapper.h | 4 +- .../reader/mapper/gestures/GestureConverter.h | 4 +- .../inputflinger/tests/CursorInputMapper_test.cpp | 6 +- .../tests/FakeInputDispatcherPolicy.cpp | 6 +- .../inputflinger/tests/FakeInputDispatcherPolicy.h | 8 +- .../inputflinger/tests/FakeInputReaderPolicy.cpp | 12 +- .../inputflinger/tests/FakeInputReaderPolicy.h | 6 +- .../inputflinger/tests/FakePointerController.cpp | 17 +- .../inputflinger/tests/FakePointerController.h | 21 +- services/inputflinger/tests/FakeWindows.cpp | 14 +- services/inputflinger/tests/FakeWindows.h | 44 ++-- services/inputflinger/tests/FocusResolver_test.cpp | 45 ++-- .../inputflinger/tests/GestureConverter_test.cpp | 120 +++++----- .../tests/InputDeviceMetricsCollector_test.cpp | 3 +- .../inputflinger/tests/InputDispatcher_test.cpp | 153 +++++++------ services/inputflinger/tests/InputMapperTest.cpp | 4 +- services/inputflinger/tests/InputMapperTest.h | 2 +- .../tests/InputProcessorConverter_test.cpp | 3 +- .../inputflinger/tests/InputProcessor_test.cpp | 5 +- services/inputflinger/tests/InputReader_test.cpp | 55 ++--- services/inputflinger/tests/InputTracingTest.cpp | 4 +- .../inputflinger/tests/LatencyTracker_test.cpp | 3 +- .../tests/MultiTouchInputMapper_test.cpp | 2 +- services/inputflinger/tests/NotifyArgs_test.cpp | 2 +- .../tests/PointerChoreographer_test.cpp | 183 +++++++++------- .../tests/PreferStylusOverTouch_test.cpp | 8 +- services/inputflinger/tests/TestEventMatchers.h | 6 +- .../tests/TouchpadInputMapper_test.cpp | 2 +- .../tests/UnwantedInteractionBlocker_test.cpp | 8 +- .../inputflinger/tests/fuzzers/FuzzedInputStream.h | 2 +- .../tests/fuzzers/InputClassifierFuzzer.cpp | 2 +- .../tests/fuzzers/InputDispatcherFuzzer.cpp | 13 +- .../tests/fuzzers/InputReaderFuzzer.cpp | 5 +- .../inputflinger/tests/fuzzers/MapperHelpers.h | 2 +- services/surfaceflinger/DisplayDevice.cpp | 2 +- services/surfaceflinger/FrontEnd/DisplayInfo.h | 1 + .../FrontEnd/LayerSnapshotBuilder.cpp | 3 +- services/surfaceflinger/Layer.cpp | 8 +- services/surfaceflinger/LayerProtoHelper.cpp | 2 +- .../Tracing/TransactionProtoParser.cpp | 4 +- .../tests/unittests/TransactionProtoParserTest.cpp | 2 +- 112 files changed, 1147 insertions(+), 1036 deletions(-) delete mode 100644 libs/gui/include/gui/constants.h create mode 100644 libs/ui/include/ui/LogicalDisplayId.h (limited to 'libs/gui') diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h index b0eceefba0..97c2ab8451 100644 --- a/include/input/DisplayViewport.h +++ b/include/input/DisplayViewport.h @@ -19,7 +19,6 @@ #include #include #include -#include #include