diff options
86 files changed, 2519 insertions, 856 deletions
diff --git a/aidl/gui/android/view/Surface.aidl b/aidl/gui/android/view/Surface.aidl index bb3faaff79..668671760c 100644 --- a/aidl/gui/android/view/Surface.aidl +++ b/aidl/gui/android/view/Surface.aidl @@ -17,4 +17,4 @@ package android.view; -@JavaOnlyStableParcelable @NdkOnlyStableParcelable parcelable Surface cpp_header "gui/view/Surface.h" ndk_header "android/native_window_aidl.h"; +@JavaOnlyStableParcelable @NdkOnlyStableParcelable @RustOnlyStableParcelable parcelable Surface cpp_header "gui/view/Surface.h" ndk_header "android/native_window_aidl.h" rust_type "nativewindow::Surface"; diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp index ae56cb0ed3..07908ba5b3 100644 --- a/cmds/servicemanager/main.cpp +++ b/cmds/servicemanager/main.cpp @@ -40,15 +40,12 @@ class BinderCallback : public LooperCallback { public: static sp<BinderCallback> setupTo(const sp<Looper>& looper) { sp<BinderCallback> cb = sp<BinderCallback>::make(); + cb->mLooper = looper; - int binder_fd = -1; - IPCThreadState::self()->setupPolling(&binder_fd); - LOG_ALWAYS_FATAL_IF(binder_fd < 0, "Failed to setupPolling: %d", binder_fd); + IPCThreadState::self()->setupPolling(&cb->mBinderFd); + LOG_ALWAYS_FATAL_IF(cb->mBinderFd < 0, "Failed to setupPolling: %d", cb->mBinderFd); - int ret = looper->addFd(binder_fd, - Looper::POLL_CALLBACK, - Looper::EVENT_INPUT, - cb, + int ret = looper->addFd(cb->mBinderFd, Looper::POLL_CALLBACK, Looper::EVENT_INPUT, cb, nullptr /*data*/); LOG_ALWAYS_FATAL_IF(ret != 1, "Failed to add binder FD to Looper"); @@ -59,13 +56,26 @@ public: IPCThreadState::self()->handlePolledCommands(); return 1; // Continue receiving callbacks. } + + void repoll() { + if (!mLooper->repoll(mBinderFd)) { + ALOGE("Failed to repoll binder FD."); + } + } + +private: + sp<Looper> mLooper; + int mBinderFd = -1; }; // LooperCallback for IClientCallback class ClientCallbackCallback : public LooperCallback { public: - static sp<ClientCallbackCallback> setupTo(const sp<Looper>& looper, const sp<ServiceManager>& manager) { + static sp<ClientCallbackCallback> setupTo(const sp<Looper>& looper, + const sp<ServiceManager>& manager, + sp<BinderCallback> binderCallback) { sp<ClientCallbackCallback> cb = sp<ClientCallbackCallback>::make(manager); + cb->mBinderCallback = binderCallback; int fdTimer = timerfd_create(CLOCK_MONOTONIC, 0 /*flags*/); LOG_ALWAYS_FATAL_IF(fdTimer < 0, "Failed to timerfd_create: fd: %d err: %d", fdTimer, errno); @@ -102,12 +112,15 @@ public: } mManager->handleClientCallbacks(); + mBinderCallback->repoll(); // b/316829336 + return 1; // Continue receiving callbacks. } private: friend sp<ClientCallbackCallback>; ClientCallbackCallback(const sp<ServiceManager>& manager) : mManager(manager) {} sp<ServiceManager> mManager; + sp<BinderCallback> mBinderCallback; }; int main(int argc, char** argv) { @@ -139,8 +152,8 @@ int main(int argc, char** argv) { sp<Looper> looper = Looper::prepare(false /*allowNonCallbacks*/); - BinderCallback::setupTo(looper); - ClientCallbackCallback::setupTo(looper, manager); + sp<BinderCallback> binderCallback = BinderCallback::setupTo(looper); + ClientCallbackCallback::setupTo(looper, manager, binderCallback); #ifndef VENDORSERVICEMANAGER if (!SetProperty("servicemanager.ready", "true")) { diff --git a/include/android/asset_manager.h b/include/android/asset_manager.h index 2ac7d4d350..6420cd0064 100644 --- a/include/android/asset_manager.h +++ b/include/android/asset_manager.h @@ -29,6 +29,10 @@ #include <sys/cdefs.h> #include <sys/types.h> +#if defined(__APPLE__) +typedef off_t off64_t; // Mac OSX does not define off64_t +#endif + #ifdef __cplusplus extern "C" { #endif diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h index 30200c7e79..08d339b176 100644 --- a/include/android/performance_hint.h +++ b/include/android/performance_hint.h @@ -240,7 +240,7 @@ int APerformanceHint_setPreferPowerEfficiency( * the actual GPU duration is not measured. * * @return 0 on success. - * EINVAL if session is nullptr or any duration is an invalid number. + * EINVAL if any duration is an invalid number. * EPIPE if communication with the system service has failed. */ int APerformanceHint_reportActualWorkDuration2( @@ -269,7 +269,7 @@ void AWorkDuration_release(AWorkDuration* _Nonnull aWorkDuration) * * @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()} * @param workPeriodStartTimestampNanos The work period start timestamp in nanoseconds based on - * CLOCK_MONOTONIC about when the work starts, the timestamp must be positive. + * CLOCK_MONOTONIC about when the work starts. This timestamp must be greater than zero. */ void AWorkDuration_setWorkPeriodStartTimestampNanos(AWorkDuration* _Nonnull aWorkDuration, int64_t workPeriodStartTimestampNanos) __INTRODUCED_IN(__ANDROID_API_V__); @@ -278,8 +278,8 @@ void AWorkDuration_setWorkPeriodStartTimestampNanos(AWorkDuration* _Nonnull aWor * Sets the actual total work duration in nanoseconds. * * @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()} - * @param actualTotalDurationNanos The actual total work duration in nanoseconds, the number must be - * positive. + * @param actualTotalDurationNanos The actual total work duration in nanoseconds. This number must + * be greater than zero. */ void AWorkDuration_setActualTotalDurationNanos(AWorkDuration* _Nonnull aWorkDuration, int64_t actualTotalDurationNanos) __INTRODUCED_IN(__ANDROID_API_V__); @@ -288,8 +288,8 @@ void AWorkDuration_setActualTotalDurationNanos(AWorkDuration* _Nonnull aWorkDura * Sets the actual CPU work duration in nanoseconds. * * @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()} - * @param actualCpuDurationNanos The actual CPU work duration in nanoseconds, the number must be - * positive. + * @param actualCpuDurationNanos The actual CPU work duration in nanoseconds. This number must be + * greater than zero. */ void AWorkDuration_setActualCpuDurationNanos(AWorkDuration* _Nonnull aWorkDuration, int64_t actualCpuDurationNanos) __INTRODUCED_IN(__ANDROID_API_V__); diff --git a/include/powermanager/PowerHalController.h b/include/powermanager/PowerHalController.h index 9e426d3ea3..c50bc4a188 100644 --- a/include/powermanager/PowerHalController.h +++ b/include/powermanager/PowerHalController.h @@ -62,7 +62,15 @@ public: virtual HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> createHintSession(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) override; + virtual HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> + createHintSessionWithConfig(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, + int64_t durationNanos, + aidl::android::hardware::power::SessionTag tag, + aidl::android::hardware::power::SessionConfig* config) override; virtual HalResult<int64_t> getHintSessionPreferredRate() override; + virtual HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel( + int tgid, int uid) override; + virtual HalResult<void> closeSessionChannel(int tgid, int uid) override; private: std::mutex mConnectedHalMutex; @@ -75,7 +83,7 @@ private: std::shared_ptr<HalWrapper> initHal(); template <typename T> - HalResult<T> processHalResult(HalResult<T> result, const char* functionName); + HalResult<T> processHalResult(HalResult<T>&& result, const char* functionName); }; // ------------------------------------------------------------------------------------------------- diff --git a/include/powermanager/PowerHalWrapper.h b/include/powermanager/PowerHalWrapper.h index 4e4a1b000d..e2da014606 100644 --- a/include/powermanager/PowerHalWrapper.h +++ b/include/powermanager/PowerHalWrapper.h @@ -14,19 +14,22 @@ * limitations under the License. */ -#ifndef ANDROID_POWERHALWRAPPER_H -#define ANDROID_POWERHALWRAPPER_H +#pragma once #include <aidl/android/hardware/power/Boost.h> +#include <aidl/android/hardware/power/ChannelConfig.h> #include <aidl/android/hardware/power/IPower.h> #include <aidl/android/hardware/power/IPowerHintSession.h> #include <aidl/android/hardware/power/Mode.h> +#include <aidl/android/hardware/power/SessionConfig.h> #include <android-base/thread_annotations.h> #include <android/hardware/power/1.1/IPower.h> #include <android/hardware/power/1.2/IPower.h> #include <android/hardware/power/1.3/IPower.h> #include <binder/Status.h> +#include <utility> + namespace android { namespace power { @@ -42,44 +45,63 @@ enum class HalSupport { template <typename T> class HalResult { public: - static HalResult<T> ok(T value) { return HalResult(value); } - static HalResult<T> failed(std::string msg) { - return HalResult(std::move(msg), /* unsupported= */ false); - } + static HalResult<T> ok(T&& value) { return HalResult(std::forward<T>(value)); } + static HalResult<T> ok(T& value) { return HalResult<T>::ok(T{value}); } + static HalResult<T> failed(std::string msg) { return HalResult(msg, /* unsupported= */ false); } static HalResult<T> unsupported() { return HalResult("", /* unsupported= */ true); } - static HalResult<T> fromStatus(const binder::Status& status, T data) { + static HalResult<T> fromStatus(const binder::Status& status, T&& data) { if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) { return HalResult<T>::unsupported(); } if (status.isOk()) { - return HalResult<T>::ok(data); + return HalResult<T>::ok(std::forward<T>(data)); } return HalResult<T>::failed(std::string(status.toString8().c_str())); } - static HalResult<T> fromStatus(const ndk::ScopedAStatus& status, T data) { + static HalResult<T> fromStatus(const binder::Status& status, T& data) { + return HalResult<T>::fromStatus(status, T{data}); + } + + static HalResult<T> fromStatus(const ndk::ScopedAStatus& status, T&& data) { if (status.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) { return HalResult<T>::unsupported(); } if (status.isOk()) { - return HalResult<T>::ok(data); + return HalResult<T>::ok(std::forward<T>(data)); } return HalResult<T>::failed(std::string(status.getDescription())); } + static HalResult<T> fromStatus(const ndk::ScopedAStatus& status, T& data) { + return HalResult<T>::fromStatus(status, T{data}); + } + template <typename R> - static HalResult<T> fromReturn(hardware::Return<R>& ret, T data) { - return ret.isOk() ? HalResult<T>::ok(data) : HalResult<T>::failed(ret.description()); + static HalResult<T> fromReturn(hardware::Return<R>& ret, T&& data) { + return ret.isOk() ? HalResult<T>::ok(std::forward<T>(data)) + : HalResult<T>::failed(ret.description()); + } + + template <typename R> + static HalResult<T> fromReturn(hardware::Return<R>& ret, T& data) { + return HalResult<T>::fromReturn(ret, T{data}); } template <typename R> static HalResult<T> fromReturn(hardware::Return<R>& ret, hardware::power::V1_0::Status status, - T data) { - return ret.isOk() ? HalResult<T>::fromStatus(status, data) + T&& data) { + return ret.isOk() ? HalResult<T>::fromStatus(status, std::forward<T>(data)) : HalResult<T>::failed(ret.description()); } + template <typename R> + static HalResult<T> fromReturn(hardware::Return<R>& ret, hardware::power::V1_0::Status status, + T& data) { + return HalResult<T>::fromReturn(ret, status, T{data}); + } + // This will throw std::bad_optional_access if this result is not ok. const T& value() const { return mValue.value(); } bool isOk() const { return !mUnsupported && mValue.has_value(); } @@ -92,8 +114,8 @@ private: std::string mErrorMessage; bool mUnsupported; - explicit HalResult(T value) - : mValue(std::make_optional(value)), mErrorMessage(), mUnsupported(false) {} + explicit HalResult(T&& value) + : mValue{std::move(value)}, mErrorMessage(), mUnsupported(false) {} explicit HalResult(std::string errorMessage, bool unsupported) : mValue(), mErrorMessage(std::move(errorMessage)), mUnsupported(unsupported) {} }; @@ -158,7 +180,15 @@ public: virtual HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> createHintSession(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) = 0; + virtual HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> + createHintSessionWithConfig(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, + int64_t durationNanos, + aidl::android::hardware::power::SessionTag tag, + aidl::android::hardware::power::SessionConfig* config) = 0; virtual HalResult<int64_t> getHintSessionPreferredRate() = 0; + virtual HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(int tgid, + int uid) = 0; + virtual HalResult<void> closeSessionChannel(int tgid, int uid) = 0; }; // Empty Power HAL wrapper that ignores all api calls. @@ -173,11 +203,22 @@ public: HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> createHintSession( int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) override; + HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> + createHintSessionWithConfig(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, + int64_t durationNanos, + aidl::android::hardware::power::SessionTag tag, + aidl::android::hardware::power::SessionConfig* config) override; HalResult<int64_t> getHintSessionPreferredRate() override; + HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(int tgid, + int uid) override; + HalResult<void> closeSessionChannel(int tgid, int uid) override; + +protected: + virtual const char* getUnsupportedMessage(); }; // Wrapper for the HIDL Power HAL v1.0. -class HidlHalWrapperV1_0 : public HalWrapper { +class HidlHalWrapperV1_0 : public EmptyHalWrapper { public: explicit HidlHalWrapperV1_0(sp<hardware::power::V1_0::IPower> handleV1_0) : mHandleV1_0(std::move(handleV1_0)) {} @@ -186,14 +227,11 @@ public: HalResult<void> setBoost(aidl::android::hardware::power::Boost boost, int32_t durationMs) override; HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override; - HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> createHintSession( - int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, - int64_t durationNanos) override; - HalResult<int64_t> getHintSessionPreferredRate() override; protected: const sp<hardware::power::V1_0::IPower> mHandleV1_0; virtual HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId, uint32_t data); + const char* getUnsupportedMessage(); private: HalResult<void> setInteractive(bool enabled); @@ -238,7 +276,7 @@ protected: }; // Wrapper for the AIDL Power HAL. -class AidlHalWrapper : public HalWrapper { +class AidlHalWrapper : public EmptyHalWrapper { public: explicit AidlHalWrapper(std::shared_ptr<aidl::android::hardware::power::IPower> handle) : mHandle(std::move(handle)) {} @@ -250,7 +288,19 @@ public: HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> createHintSession( int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) override; + HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> + createHintSessionWithConfig(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, + int64_t durationNanos, + aidl::android::hardware::power::SessionTag tag, + aidl::android::hardware::power::SessionConfig* config) override; + HalResult<int64_t> getHintSessionPreferredRate() override; + HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(int tgid, + int uid) override; + HalResult<void> closeSessionChannel(int tgid, int uid) override; + +protected: + const char* getUnsupportedMessage() override; private: // Control access to the boost and mode supported arrays. @@ -274,5 +324,3 @@ private: }; // namespace power }; // namespace android - -#endif // ANDROID_POWERHALWRAPPER_H diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index e1dc7911a1..6b8e8248b8 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -416,26 +416,36 @@ public: }; struct DisplayState { - enum { + enum : uint32_t { eSurfaceChanged = 0x01, eLayerStackChanged = 0x02, eDisplayProjectionChanged = 0x04, eDisplaySizeChanged = 0x08, - eFlagsChanged = 0x10 + eFlagsChanged = 0x10, + + eAllChanged = ~0u }; + // Not for direct use. Prefer constructor below for new displays. DisplayState(); + + DisplayState(sp<IBinder> token, ui::LayerStack layerStack) + : what(eAllChanged), + token(std::move(token)), + layerStack(layerStack), + layerStackSpaceRect(Rect::INVALID_RECT), + orientedDisplaySpaceRect(Rect::INVALID_RECT) {} + void merge(const DisplayState& other); void sanitize(int32_t permissions); uint32_t what = 0; uint32_t flags = 0; sp<IBinder> token; - sp<IGraphicBufferProducer> surface; ui::LayerStack layerStack = ui::DEFAULT_LAYER_STACK; - // These states define how layers are projected onto the physical display. + // These states define how layers are projected onto the physical or virtual display. // // Layers are first clipped to `layerStackSpaceRect'. They are then translated and // scaled from `layerStackSpaceRect' to `orientedDisplaySpaceRect'. Finally, they are rotated @@ -446,10 +456,17 @@ struct DisplayState { // will be scaled by a factor of 2 and translated by (20, 10). When orientation is 1, layers // will be additionally rotated by 90 degrees around the origin clockwise and translated by (W, // 0). + // + // Rect::INVALID_RECT sizes the space to the active resolution of the physical display, or the + // default dimensions of the virtual display surface. + // ui::Rotation orientation = ui::ROTATION_0; Rect layerStackSpaceRect = Rect::EMPTY_RECT; Rect orientedDisplaySpaceRect = Rect::EMPTY_RECT; + // Exclusive to virtual displays: The sink surface into which the virtual display is rendered, + // and an optional resolution that overrides its default dimensions. + sp<IGraphicBufferProducer> surface; uint32_t width = 0; uint32_t height = 0; diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig index 576d9d569d..0c850fe0ee 100644 --- a/libs/input/input_flags.aconfig +++ b/libs/input/input_flags.aconfig @@ -97,3 +97,10 @@ flag { description: "Change the acceleration curves for mouse pointer movements to match the touchpad ones" bug: "315313622" } + +flag { + name: "rate_limit_user_activity_poke_in_dispatcher" + namespace: "input" + description: "Move user-activity poke rate-limiting from PowerManagerService to InputDispatcher." + bug: "320499729" +} diff --git a/libs/nativewindow/rust/src/lib.rs b/libs/nativewindow/rust/src/lib.rs index aab7df0c2c..22ad83463c 100644 --- a/libs/nativewindow/rust/src/lib.rs +++ b/libs/nativewindow/rust/src/lib.rs @@ -16,6 +16,8 @@ extern crate nativewindow_bindgen as ffi; +pub mod surface; + pub use ffi::{AHardwareBuffer_Format, AHardwareBuffer_UsageFlags}; use binder::{ @@ -210,7 +212,7 @@ impl Drop for HardwareBuffer { } impl Debug for HardwareBuffer { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { f.debug_struct("HardwareBuffer").field("id", &self.id()).finish() } } diff --git a/libs/nativewindow/rust/src/surface.rs b/libs/nativewindow/rust/src/surface.rs new file mode 100644 index 0000000000..25fea807b5 --- /dev/null +++ b/libs/nativewindow/rust/src/surface.rs @@ -0,0 +1,143 @@ +// 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 `ANativeWindow` and related types. + +use binder::{ + binder_impl::{BorrowedParcel, UnstructuredParcelable}, + impl_deserialize_for_unstructured_parcelable, impl_serialize_for_unstructured_parcelable, + unstable_api::{status_result, AsNative}, + StatusCode, +}; +use nativewindow_bindgen::{ + AHardwareBuffer_Format, ANativeWindow, ANativeWindow_acquire, ANativeWindow_getFormat, + ANativeWindow_getHeight, ANativeWindow_getWidth, ANativeWindow_readFromParcel, + ANativeWindow_release, ANativeWindow_writeToParcel, +}; +use std::error::Error; +use std::fmt::{self, Debug, Display, Formatter}; +use std::ptr::{null_mut, NonNull}; + +/// Wrapper around an opaque C `ANativeWindow`. +#[derive(PartialEq, Eq)] +pub struct Surface(NonNull<ANativeWindow>); + +impl Surface { + /// Returns the current width in pixels of the window surface. + pub fn width(&self) -> Result<u32, ErrorCode> { + // SAFETY: The ANativeWindow pointer we pass is guaranteed to be non-null and valid because + // it must have been allocated by `ANativeWindow_allocate` or `ANativeWindow_readFromParcel` + // and we have not yet released it. + let width = unsafe { ANativeWindow_getWidth(self.0.as_ptr()) }; + width.try_into().map_err(|_| ErrorCode(width)) + } + + /// Returns the current height in pixels of the window surface. + pub fn height(&self) -> Result<u32, ErrorCode> { + // SAFETY: The ANativeWindow pointer we pass is guaranteed to be non-null and valid because + // it must have been allocated by `ANativeWindow_allocate` or `ANativeWindow_readFromParcel` + // and we have not yet released it. + let height = unsafe { ANativeWindow_getHeight(self.0.as_ptr()) }; + height.try_into().map_err(|_| ErrorCode(height)) + } + + /// Returns the current pixel format of the window surface. + pub fn format(&self) -> Result<AHardwareBuffer_Format::Type, ErrorCode> { + // SAFETY: The ANativeWindow pointer we pass is guaranteed to be non-null and valid because + // it must have been allocated by `ANativeWindow_allocate` or `ANativeWindow_readFromParcel` + // and we have not yet released it. + let format = unsafe { ANativeWindow_getFormat(self.0.as_ptr()) }; + format.try_into().map_err(|_| ErrorCode(format)) + } +} + +impl Drop for Surface { + fn drop(&mut self) { + // SAFETY: The ANativeWindow pointer we pass is guaranteed to be non-null and valid because + // it must have been allocated by `ANativeWindow_allocate` or `ANativeWindow_readFromParcel` + // and we have not yet released it. + unsafe { ANativeWindow_release(self.0.as_ptr()) } + } +} + +impl Debug for Surface { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.debug_struct("Surface") + .field("width", &self.width()) + .field("height", &self.height()) + .field("format", &self.format()) + .finish() + } +} + +impl Clone for Surface { + fn clone(&self) -> Self { + // SAFETY: The ANativeWindow pointer we pass is guaranteed to be non-null and valid because + // it must have been allocated by `ANativeWindow_allocate` or `ANativeWindow_readFromParcel` + // and we have not yet released it. + unsafe { ANativeWindow_acquire(self.0.as_ptr()) }; + Self(self.0) + } +} + +impl UnstructuredParcelable for Surface { + fn write_to_parcel(&self, parcel: &mut BorrowedParcel) -> Result<(), StatusCode> { + let status = + // SAFETY: The ANativeWindow pointer we pass is guaranteed to be non-null and valid because + // it must have been allocated by `ANativeWindow_allocate` or `ANativeWindow_readFromParcel` + // and we have not yet released it. + unsafe { ANativeWindow_writeToParcel(self.0.as_ptr(), parcel.as_native_mut()) }; + status_result(status) + } + + fn from_parcel(parcel: &BorrowedParcel) -> Result<Self, StatusCode> { + let mut buffer = null_mut(); + + let status = + // SAFETY: Both pointers must be valid because they are obtained from references. + // `ANativeWindow_readFromParcel` doesn't store them or do anything else special + // with them. If it returns success then it will have allocated a new + // `ANativeWindow` and incremented the reference count, so we can use it until we + // release it. + unsafe { ANativeWindow_readFromParcel(parcel.as_native(), &mut buffer) }; + + status_result(status)?; + + Ok(Self( + NonNull::new(buffer) + .expect("ANativeWindow_readFromParcel returned success but didn't allocate buffer"), + )) + } +} + +impl_deserialize_for_unstructured_parcelable!(Surface); +impl_serialize_for_unstructured_parcelable!(Surface); + +// SAFETY: The underlying *ANativeWindow can be moved between threads. +unsafe impl Send for Surface {} + +// SAFETY: The underlying *ANativeWindow can be used from multiple threads concurrently. +unsafe impl Sync for Surface {} + +/// An error code returned by methods on [`Surface`]. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct ErrorCode(i32); + +impl Error for ErrorCode {} + +impl Display for ErrorCode { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "Error {}", self.0) + } +} diff --git a/libs/nativewindow/rust/sys/nativewindow_bindings.h b/libs/nativewindow/rust/sys/nativewindow_bindings.h index 4525a42502..5689f7df94 100644 --- a/libs/nativewindow/rust/sys/nativewindow_bindings.h +++ b/libs/nativewindow/rust/sys/nativewindow_bindings.h @@ -19,3 +19,4 @@ #include <android/hardware_buffer_aidl.h> #include <android/hdr_metadata.h> #include <android/native_window.h> +#include <android/native_window_aidl.h> diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp index 3e1ac33d57..233134d2db 100644 --- a/libs/renderengine/RenderEngine.cpp +++ b/libs/renderengine/RenderEngine.cpp @@ -28,28 +28,28 @@ namespace android { namespace renderengine { std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) { - switch (args.renderEngineType) { - case RenderEngineType::SKIA_GL: + if (args.threaded == Threaded::YES) { + switch (args.graphicsApi) { + case GraphicsApi::GL: + ALOGD("Threaded RenderEngine with SkiaGL Backend"); + return renderengine::threaded::RenderEngineThreaded::create([args]() { + return android::renderengine::skia::SkiaGLRenderEngine::create(args); + }); + case GraphicsApi::VK: + ALOGD("Threaded RenderEngine with SkiaVK Backend"); + return renderengine::threaded::RenderEngineThreaded::create([args]() { + return android::renderengine::skia::SkiaVkRenderEngine::create(args); + }); + } + } + + switch (args.graphicsApi) { + case GraphicsApi::GL: ALOGD("RenderEngine with SkiaGL Backend"); return renderengine::skia::SkiaGLRenderEngine::create(args); - case RenderEngineType::SKIA_VK: + case GraphicsApi::VK: ALOGD("RenderEngine with SkiaVK Backend"); return renderengine::skia::SkiaVkRenderEngine::create(args); - case RenderEngineType::SKIA_GL_THREADED: { - ALOGD("Threaded RenderEngine with SkiaGL Backend"); - return renderengine::threaded::RenderEngineThreaded::create( - [args]() { - return android::renderengine::skia::SkiaGLRenderEngine::create(args); - }, - args.renderEngineType); - } - case RenderEngineType::SKIA_VK_THREADED: - ALOGD("Threaded RenderEngine with SkiaVK Backend"); - return renderengine::threaded::RenderEngineThreaded::create( - [args]() { - return android::renderengine::skia::SkiaVkRenderEngine::create(args); - }, - args.renderEngineType); } } diff --git a/libs/renderengine/benchmark/RenderEngineBench.cpp b/libs/renderengine/benchmark/RenderEngineBench.cpp index a7f1df9a9b..101f519e55 100644 --- a/libs/renderengine/benchmark/RenderEngineBench.cpp +++ b/libs/renderengine/benchmark/RenderEngineBench.cpp @@ -30,46 +30,6 @@ using namespace android; using namespace android::renderengine; /////////////////////////////////////////////////////////////////////////////// -// Helpers for Benchmark::Apply -/////////////////////////////////////////////////////////////////////////////// - -std::string RenderEngineTypeName(RenderEngine::RenderEngineType type) { - switch (type) { - case RenderEngine::RenderEngineType::SKIA_GL_THREADED: - return "skiaglthreaded"; - case RenderEngine::RenderEngineType::SKIA_GL: - return "skiagl"; - case RenderEngine::RenderEngineType::SKIA_VK: - return "skiavk"; - case RenderEngine::RenderEngineType::SKIA_VK_THREADED: - return "skiavkthreaded"; - } -} - -/** - * Passed (indirectly - see RunSkiaGLThreaded) to Benchmark::Apply to create a - * Benchmark which specifies which RenderEngineType it uses. - * - * This simplifies calling ->Arg(type)->Arg(type) and provides strings to make - * it obvious which version is being run. - * - * @param b The benchmark family - * @param type The type of RenderEngine to use. - */ -static void AddRenderEngineType(benchmark::internal::Benchmark* b, - RenderEngine::RenderEngineType type) { - b->Arg(static_cast<int64_t>(type)); - b->ArgName(RenderEngineTypeName(type)); -} - -/** - * Run a benchmark once using SKIA_GL_THREADED. - */ -static void RunSkiaGLThreaded(benchmark::internal::Benchmark* b) { - AddRenderEngineType(b, RenderEngine::RenderEngineType::SKIA_GL_THREADED); -} - -/////////////////////////////////////////////////////////////////////////////// // Helpers for calling drawLayers /////////////////////////////////////////////////////////////////////////////// @@ -104,7 +64,8 @@ std::pair<uint32_t, uint32_t> getDisplaySize() { return std::pair<uint32_t, uint32_t>(width, height); } -static std::unique_ptr<RenderEngine> createRenderEngine(RenderEngine::RenderEngineType type) { +static std::unique_ptr<RenderEngine> createRenderEngine(RenderEngine::Threaded threaded, + RenderEngine::GraphicsApi graphicsApi) { auto args = RenderEngineCreationArgs::Builder() .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888)) .setImageCacheSize(1) @@ -112,7 +73,8 @@ static std::unique_ptr<RenderEngine> createRenderEngine(RenderEngine::RenderEngi .setPrecacheToneMapperShaderOnly(false) .setSupportsBackgroundBlur(true) .setContextPriority(RenderEngine::ContextPriority::REALTIME) - .setRenderEngineType(type) + .setThreaded(threaded) + .setGraphicsApi(graphicsApi) .build(); return RenderEngine::create(args); } @@ -214,8 +176,11 @@ static void benchDrawLayers(RenderEngine& re, const std::vector<LayerSettings>& // Benchmarks /////////////////////////////////////////////////////////////////////////////// -void BM_blur(benchmark::State& benchState) { - auto re = createRenderEngine(static_cast<RenderEngine::RenderEngineType>(benchState.range())); +template <class... Args> +void BM_blur(benchmark::State& benchState, Args&&... args) { + auto args_tuple = std::make_tuple(std::move(args)...); + auto re = createRenderEngine(static_cast<RenderEngine::Threaded>(std::get<0>(args_tuple)), + static_cast<RenderEngine::GraphicsApi>(std::get<1>(args_tuple))); // Initially use cpu access so we can decode into it with AImageDecoder. auto [width, height] = getDisplaySize(); @@ -259,4 +224,5 @@ void BM_blur(benchmark::State& benchState) { benchDrawLayers(*re, layers, benchState, "blurred"); } -BENCHMARK(BM_blur)->Apply(RunSkiaGLThreaded); +BENCHMARK_CAPTURE(BM_blur, SkiaGLThreaded, RenderEngine::Threaded::YES, + RenderEngine::GraphicsApi::GL); diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h index 818d0350c0..7047358e62 100644 --- a/libs/renderengine/include/renderengine/RenderEngine.h +++ b/libs/renderengine/include/renderengine/RenderEngine.h @@ -33,7 +33,7 @@ #include <memory> /** - * Allows to set RenderEngine backend to GLES (default) or SkiaGL (NOT yet supported). + * Allows to override the RenderEngine backend. */ #define PROPERTY_DEBUG_RENDERENGINE_BACKEND "debug.renderengine.backend" @@ -92,11 +92,14 @@ public: REALTIME = 4, }; - enum class RenderEngineType { - SKIA_GL = 3, - SKIA_GL_THREADED = 4, - SKIA_VK = 5, - SKIA_VK_THREADED = 6, + enum class Threaded { + NO, + YES, + }; + + enum class GraphicsApi { + GL, + VK, }; static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args); @@ -176,10 +179,9 @@ public: // query is required to be thread safe. virtual bool supportsBackgroundBlur() = 0; - // Returns the current type of RenderEngine instance that was created. // TODO(b/180767535): This is only implemented to allow for backend-specific behavior, which // we should not allow in general, so remove this. - RenderEngineType getRenderEngineType() const { return mRenderEngineType; } + bool isThreaded() const { return mThreaded == Threaded::YES; } static void validateInputBufferUsage(const sp<GraphicBuffer>&); static void validateOutputBufferUsage(const sp<GraphicBuffer>&); @@ -191,9 +193,9 @@ public: virtual void setEnableTracing(bool /*tracingEnabled*/) {} protected: - RenderEngine() : RenderEngine(RenderEngineType::SKIA_GL) {} + RenderEngine() : RenderEngine(Threaded::NO) {} - RenderEngine(RenderEngineType type) : mRenderEngineType(type) {} + RenderEngine(Threaded threaded) : mThreaded(threaded) {} // Maps GPU resources for this buffer. // Note that work may be deferred to an additional thread, i.e. this call @@ -228,7 +230,7 @@ protected: friend class impl::ExternalTexture; friend class threaded::RenderEngineThreaded; friend class RenderEngineTest_cleanupPostRender_cleansUpOnce_Test; - const RenderEngineType mRenderEngineType; + const Threaded mThreaded; // Update protectedContext mode depending on whether or not any layer has a protected buffer. void updateProtectedContext(const std::vector<LayerSettings>&, @@ -251,7 +253,8 @@ struct RenderEngineCreationArgs { bool precacheToneMapperShaderOnly; bool supportsBackgroundBlur; RenderEngine::ContextPriority contextPriority; - RenderEngine::RenderEngineType renderEngineType; + RenderEngine::Threaded threaded; + RenderEngine::GraphicsApi graphicsApi; struct Builder; @@ -261,14 +264,16 @@ private: bool _enableProtectedContext, bool _precacheToneMapperShaderOnly, bool _supportsBackgroundBlur, RenderEngine::ContextPriority _contextPriority, - RenderEngine::RenderEngineType _renderEngineType) + RenderEngine::Threaded _threaded, + RenderEngine::GraphicsApi _graphicsApi) : pixelFormat(_pixelFormat), imageCacheSize(_imageCacheSize), enableProtectedContext(_enableProtectedContext), precacheToneMapperShaderOnly(_precacheToneMapperShaderOnly), supportsBackgroundBlur(_supportsBackgroundBlur), contextPriority(_contextPriority), - renderEngineType(_renderEngineType) {} + threaded(_threaded), + graphicsApi(_graphicsApi) {} RenderEngineCreationArgs() = delete; }; @@ -299,14 +304,18 @@ struct RenderEngineCreationArgs::Builder { this->contextPriority = contextPriority; return *this; } - Builder& setRenderEngineType(RenderEngine::RenderEngineType renderEngineType) { - this->renderEngineType = renderEngineType; + Builder& setThreaded(RenderEngine::Threaded threaded) { + this->threaded = threaded; + return *this; + } + Builder& setGraphicsApi(RenderEngine::GraphicsApi graphicsApi) { + this->graphicsApi = graphicsApi; return *this; } RenderEngineCreationArgs build() const { return RenderEngineCreationArgs(pixelFormat, imageCacheSize, enableProtectedContext, precacheToneMapperShaderOnly, supportsBackgroundBlur, - contextPriority, renderEngineType); + contextPriority, threaded, graphicsApi); } private: @@ -317,8 +326,8 @@ private: bool precacheToneMapperShaderOnly = false; bool supportsBackgroundBlur = false; RenderEngine::ContextPriority contextPriority = RenderEngine::ContextPriority::MEDIUM; - RenderEngine::RenderEngineType renderEngineType = - RenderEngine::RenderEngineType::SKIA_GL_THREADED; + RenderEngine::Threaded threaded = RenderEngine::Threaded::YES; + RenderEngine::GraphicsApi graphicsApi = RenderEngine::GraphicsApi::GL; }; } // namespace renderengine diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp index d688b51793..fea4129ec0 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp @@ -268,7 +268,7 @@ EGLConfig SkiaGLRenderEngine::chooseEglConfig(EGLDisplay display, int format, bo SkiaGLRenderEngine::SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLContext ctxt, EGLSurface placeholder, EGLContext protectedContext, EGLSurface protectedPlaceholder) - : SkiaRenderEngine(args.renderEngineType, static_cast<PixelFormat>(args.pixelFormat), + : SkiaRenderEngine(args.threaded, static_cast<PixelFormat>(args.pixelFormat), args.supportsBackgroundBlur), mEGLDisplay(display), mEGLContext(ctxt), diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp index 3729be6ca2..fc84bbf283 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaRenderEngine.cpp @@ -269,9 +269,9 @@ void SkiaRenderEngine::setEnableTracing(bool tracingEnabled) { SkAndroidFrameworkTraceUtil::setEnableTracing(tracingEnabled); } -SkiaRenderEngine::SkiaRenderEngine(RenderEngineType type, PixelFormat pixelFormat, +SkiaRenderEngine::SkiaRenderEngine(Threaded threaded, PixelFormat pixelFormat, bool supportsBackgroundBlur) - : RenderEngine(type), mDefaultPixelFormat(pixelFormat) { + : RenderEngine(threaded), mDefaultPixelFormat(pixelFormat) { if (supportsBackgroundBlur) { ALOGD("Background Blurs Enabled"); mBlurFilter = new KawaseBlurFilter(); @@ -389,10 +389,9 @@ void SkiaRenderEngine::ensureGrContextsCreated() { void SkiaRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) { // Only run this if RE is running on its own thread. This - // way the access to GL operations is guaranteed to be happening on the + // way the access to GL/VK operations is guaranteed to be happening on the // same thread. - if (mRenderEngineType != RenderEngineType::SKIA_GL_THREADED && - mRenderEngineType != RenderEngineType::SKIA_VK_THREADED) { + if (!isThreaded()) { return; } // We don't attempt to map a buffer if the buffer contains protected content. In GL this is diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h index ac134afa64..e88d44cca6 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.h +++ b/libs/renderengine/skia/SkiaRenderEngine.h @@ -59,7 +59,7 @@ class BlurFilter; class SkiaRenderEngine : public RenderEngine { public: static std::unique_ptr<SkiaRenderEngine> create(const RenderEngineCreationArgs& args); - SkiaRenderEngine(RenderEngineType type, PixelFormat pixelFormat, bool supportsBackgroundBlur); + SkiaRenderEngine(Threaded, PixelFormat pixelFormat, bool supportsBackgroundBlur); ~SkiaRenderEngine() override; std::future<void> primeCache(bool shouldPrimeUltraHDR) override final; diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp index ba20d1f223..3af85c064e 100644 --- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp @@ -596,7 +596,7 @@ std::unique_ptr<SkiaVkRenderEngine> SkiaVkRenderEngine::create( } SkiaVkRenderEngine::SkiaVkRenderEngine(const RenderEngineCreationArgs& args) - : SkiaRenderEngine(args.renderEngineType, static_cast<PixelFormat>(args.pixelFormat), + : SkiaRenderEngine(args.threaded, static_cast<PixelFormat>(args.pixelFormat), args.supportsBackgroundBlur) {} SkiaVkRenderEngine::~SkiaVkRenderEngine() { diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp index 11d4fdebdc..4c18704f52 100644 --- a/libs/renderengine/tests/RenderEngineTest.cpp +++ b/libs/renderengine/tests/RenderEngineTest.cpp @@ -106,25 +106,9 @@ public: virtual ~RenderEngineFactory() = default; virtual std::string name() = 0; - virtual renderengine::RenderEngine::RenderEngineType type() = 0; - virtual std::unique_ptr<renderengine::RenderEngine> createRenderEngine() = 0; - virtual bool typeSupported() = 0; -}; - -class SkiaVkRenderEngineFactory : public RenderEngineFactory { -public: - std::string name() override { return "SkiaVkRenderEngineFactory"; } - - renderengine::RenderEngine::RenderEngineType type() { - return renderengine::RenderEngine::RenderEngineType::SKIA_VK; - } - - std::unique_ptr<renderengine::RenderEngine> createRenderEngine() override { - std::unique_ptr<renderengine::RenderEngine> re = createSkiaVkRenderEngine(); - return re; - } - - std::unique_ptr<renderengine::skia::SkiaVkRenderEngine> createSkiaVkRenderEngine() { + virtual renderengine::RenderEngine::GraphicsApi graphicsApi() = 0; + virtual bool apiSupported() = 0; + std::unique_ptr<renderengine::RenderEngine> createRenderEngine() { renderengine::RenderEngineCreationArgs reCreationArgs = renderengine::RenderEngineCreationArgs::Builder() .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888)) @@ -133,65 +117,35 @@ public: .setPrecacheToneMapperShaderOnly(false) .setSupportsBackgroundBlur(true) .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM) - .setRenderEngineType(type()) + .setThreaded(renderengine::RenderEngine::Threaded::NO) + .setGraphicsApi(graphicsApi()) .build(); - return renderengine::skia::SkiaVkRenderEngine::create(reCreationArgs); + return renderengine::RenderEngine::create(reCreationArgs); } - - bool typeSupported() override { - return skia::SkiaVkRenderEngine::canSupportSkiaVkRenderEngine(); - } - void skip() { GTEST_SKIP(); } }; -class SkiaGLESRenderEngineFactory : public RenderEngineFactory { +class SkiaVkRenderEngineFactory : public RenderEngineFactory { public: - std::string name() override { return "SkiaGLRenderEngineFactory"; } + std::string name() override { return "SkiaVkRenderEngineFactory"; } - renderengine::RenderEngine::RenderEngineType type() { - return renderengine::RenderEngine::RenderEngineType::SKIA_GL; + renderengine::RenderEngine::GraphicsApi graphicsApi() override { + return renderengine::RenderEngine::GraphicsApi::VK; } - std::unique_ptr<renderengine::RenderEngine> createRenderEngine() override { - renderengine::RenderEngineCreationArgs reCreationArgs = - renderengine::RenderEngineCreationArgs::Builder() - .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888)) - .setImageCacheSize(1) - .setEnableProtectedContext(false) - .setPrecacheToneMapperShaderOnly(false) - .setSupportsBackgroundBlur(true) - .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM) - .setRenderEngineType(type()) - .build(); - return renderengine::skia::SkiaGLRenderEngine::create(reCreationArgs); + bool apiSupported() override { + return skia::SkiaVkRenderEngine::canSupportSkiaVkRenderEngine(); } - - bool typeSupported() override { return true; } }; -class SkiaGLESCMRenderEngineFactory : public RenderEngineFactory { +class SkiaGLESRenderEngineFactory : public RenderEngineFactory { public: - std::string name() override { return "SkiaGLCMRenderEngineFactory"; } - - renderengine::RenderEngine::RenderEngineType type() { - return renderengine::RenderEngine::RenderEngineType::SKIA_GL; - } + std::string name() override { return "SkiaGLRenderEngineFactory"; } - std::unique_ptr<renderengine::RenderEngine> createRenderEngine() override { - renderengine::RenderEngineCreationArgs reCreationArgs = - renderengine::RenderEngineCreationArgs::Builder() - .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888)) - .setImageCacheSize(1) - .setEnableProtectedContext(false) - .setPrecacheToneMapperShaderOnly(false) - .setSupportsBackgroundBlur(true) - .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM) - .setRenderEngineType(type()) - .build(); - return renderengine::skia::SkiaGLRenderEngine::create(reCreationArgs); + renderengine::RenderEngine::GraphicsApi graphicsApi() { + return renderengine::RenderEngine::GraphicsApi::GL; } - bool typeSupported() override { return true; } + bool apiSupported() override { return true; } }; class RenderEngineTest : public ::testing::TestWithParam<std::shared_ptr<RenderEngineFactory>> { @@ -1526,7 +1480,7 @@ INSTANTIATE_TEST_SUITE_P(PerRenderEngineType, RenderEngineTest, std::make_shared<SkiaVkRenderEngineFactory>())); TEST_P(RenderEngineTest, drawLayers_noLayersToDraw) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1534,7 +1488,7 @@ TEST_P(RenderEngineTest, drawLayers_noLayersToDraw) { } TEST_P(RenderEngineTest, drawLayers_fillRedBufferAndEmptyBuffer) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1561,7 +1515,7 @@ TEST_P(RenderEngineTest, drawLayers_fillRedBufferAndEmptyBuffer) { } TEST_P(RenderEngineTest, drawLayers_withoutBuffers_withColorTransform) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1595,7 +1549,7 @@ TEST_P(RenderEngineTest, drawLayers_withoutBuffers_withColorTransform) { } TEST_P(RenderEngineTest, drawLayers_nullOutputBuffer) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1616,7 +1570,7 @@ TEST_P(RenderEngineTest, drawLayers_nullOutputBuffer) { } TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1624,7 +1578,7 @@ TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1632,7 +1586,7 @@ TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1640,7 +1594,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1648,7 +1602,7 @@ TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1656,7 +1610,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1664,7 +1618,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1672,7 +1626,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1680,7 +1634,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1688,7 +1642,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1696,7 +1650,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1706,7 +1660,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_colorSource) { TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_sourceDataspace) { const auto& renderEngineFactory = GetParam(); // skip for non color management - if (!renderEngineFactory->typeSupported()) { + if (!renderEngineFactory->apiSupported()) { GTEST_SKIP(); } @@ -1717,7 +1671,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_sourceDataspace) { TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_outputDataspace) { const auto& renderEngineFactory = GetParam(); // skip for non color management - if (!renderEngineFactory->typeSupported()) { + if (!renderEngineFactory->apiSupported()) { GTEST_SKIP(); } @@ -1726,7 +1680,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_outputDataspace) { } TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1734,7 +1688,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1742,7 +1696,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_color } TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1750,7 +1704,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1758,7 +1712,7 @@ TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_colorSource) } TEST_P(RenderEngineTest, drawLayers_overlayCorners_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1766,7 +1720,7 @@ TEST_P(RenderEngineTest, drawLayers_overlayCorners_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1774,7 +1728,7 @@ TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_opaqueBufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1782,7 +1736,7 @@ TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_opaqueBufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1790,7 +1744,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_opaqueBufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1798,7 +1752,7 @@ TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_opaqueBufferSource) } TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1806,7 +1760,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_opaqueBufferSource) } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1814,7 +1768,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_opaqueBufferSource } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1822,7 +1776,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_opaqueBufferSourc } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1830,7 +1784,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_opaqueBufferSour } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1838,7 +1792,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_opaqueBufferSour } TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1846,7 +1800,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_opaqueBufferSource) } TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1856,7 +1810,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_opaqueBufferSource) TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_opaqueBufferSource) { const auto& renderEngineFactory = GetParam(); // skip for non color management - if (!renderEngineFactory->typeSupported()) { + if (!renderEngineFactory->apiSupported()) { GTEST_SKIP(); } @@ -1867,7 +1821,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_o TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_opaqueBufferSource) { const auto& renderEngineFactory = GetParam(); // skip for non color management - if (!renderEngineFactory->typeSupported()) { + if (!renderEngineFactory->apiSupported()) { GTEST_SKIP(); } @@ -1876,7 +1830,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_o } TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1884,7 +1838,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_opaqueBufferSource) } TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1892,7 +1846,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_opaqu } TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1900,7 +1854,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_opaqueBufferSour } TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1908,7 +1862,7 @@ TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_opaqueBuffer } TEST_P(RenderEngineTest, drawLayers_overlayCorners_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1916,7 +1870,7 @@ TEST_P(RenderEngineTest, drawLayers_overlayCorners_opaqueBufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1924,7 +1878,7 @@ TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1932,7 +1886,7 @@ TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1940,7 +1894,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1948,7 +1902,7 @@ TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1956,7 +1910,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1964,7 +1918,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1972,7 +1926,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1980,7 +1934,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1988,7 +1942,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1996,7 +1950,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2006,7 +1960,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_bufferSource) { TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_bufferSource) { const auto& renderEngineFactory = GetParam(); // skip for non color management - if (!renderEngineFactory->typeSupported()) { + if (!renderEngineFactory->apiSupported()) { GTEST_SKIP(); } @@ -2017,7 +1971,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_b TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_bufferSource) { const auto& renderEngineFactory = GetParam(); // skip for non color management - if (!renderEngineFactory->typeSupported()) { + if (!renderEngineFactory->apiSupported()) { GTEST_SKIP(); } @@ -2026,7 +1980,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_b } TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2034,7 +1988,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2042,7 +1996,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_buffe } TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2050,7 +2004,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2058,7 +2012,7 @@ TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_bufferSource } TEST_P(RenderEngineTest, drawLayers_overlayCorners_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2066,7 +2020,7 @@ TEST_P(RenderEngineTest, drawLayers_overlayCorners_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferTextureTransform) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2074,7 +2028,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferTextureTransform) { } TEST_P(RenderEngineTest, drawLayers_fillBuffer_premultipliesAlpha) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2082,7 +2036,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBuffer_premultipliesAlpha) { } TEST_P(RenderEngineTest, drawLayers_fillBuffer_withoutPremultiplyingAlpha) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2090,7 +2044,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBuffer_withoutPremultiplyingAlpha) { } TEST_P(RenderEngineTest, drawLayers_fillShadow_castsWithoutCasterLayer) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2108,7 +2062,7 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_castsWithoutCasterLayer) { } TEST_P(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2131,7 +2085,7 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) { } TEST_P(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2155,7 +2109,7 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) { } TEST_P(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2180,7 +2134,7 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) { } TEST_P(RenderEngineTest, drawLayers_fillShadow_casterWithRoundedCorner) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2206,7 +2160,7 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_casterWithRoundedCorner) { } TEST_P(RenderEngineTest, drawLayers_fillShadow_translucentCasterWithAlpha) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2235,7 +2189,7 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_translucentCasterWithAlpha) { } TEST_P(RenderEngineTest, cleanupPostRender_cleansUpOnce) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2271,7 +2225,7 @@ TEST_P(RenderEngineTest, cleanupPostRender_cleansUpOnce) { if (mRE->canSkipPostRenderCleanup()) { // Skia's Vk backend may keep the texture alive beyond drawLayersInternal, so // it never gets added to the cleanup list. In those cases, we can skip. - EXPECT_TRUE(GetParam()->type() == renderengine::RenderEngine::RenderEngineType::SKIA_VK); + EXPECT_TRUE(GetParam()->graphicsApi() == renderengine::RenderEngine::GraphicsApi::VK); } else { mRE->cleanupPostRender(); EXPECT_TRUE(mRE->canSkipPostRenderCleanup()); @@ -2279,7 +2233,7 @@ TEST_P(RenderEngineTest, cleanupPostRender_cleansUpOnce) { } TEST_P(RenderEngineTest, testRoundedCornersCrop) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2332,7 +2286,7 @@ TEST_P(RenderEngineTest, testRoundedCornersCrop) { } TEST_P(RenderEngineTest, testRoundedCornersParentCrop) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2380,7 +2334,7 @@ TEST_P(RenderEngineTest, testRoundedCornersParentCrop) { } TEST_P(RenderEngineTest, testRoundedCornersParentCropSmallBounds) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2417,7 +2371,7 @@ TEST_P(RenderEngineTest, testRoundedCornersParentCropSmallBounds) { } TEST_P(RenderEngineTest, testRoundedCornersXY) { - if (GetParam()->type() != renderengine::RenderEngine::RenderEngineType::SKIA_GL) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } @@ -2460,7 +2414,7 @@ TEST_P(RenderEngineTest, testRoundedCornersXY) { } TEST_P(RenderEngineTest, testClear) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2492,7 +2446,7 @@ TEST_P(RenderEngineTest, testClear) { } TEST_P(RenderEngineTest, testDisableBlendingBuffer) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2543,7 +2497,7 @@ TEST_P(RenderEngineTest, testDisableBlendingBuffer) { } TEST_P(RenderEngineTest, testBorder) { - if (GetParam()->type() != renderengine::RenderEngine::RenderEngineType::SKIA_GL) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } @@ -2588,7 +2542,7 @@ TEST_P(RenderEngineTest, testBorder) { } TEST_P(RenderEngineTest, testDimming) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2663,7 +2617,7 @@ TEST_P(RenderEngineTest, testDimming) { } TEST_P(RenderEngineTest, testDimming_inGammaSpace) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2741,7 +2695,7 @@ TEST_P(RenderEngineTest, testDimming_inGammaSpace) { } TEST_P(RenderEngineTest, testDimming_inGammaSpace_withDisplayColorTransform) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2804,7 +2758,7 @@ TEST_P(RenderEngineTest, testDimming_inGammaSpace_withDisplayColorTransform) { } TEST_P(RenderEngineTest, testDimming_inGammaSpace_withDisplayColorTransform_deviceHandles) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2868,7 +2822,7 @@ TEST_P(RenderEngineTest, testDimming_inGammaSpace_withDisplayColorTransform_devi } TEST_P(RenderEngineTest, testDimming_withoutTargetLuminance) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2922,7 +2876,7 @@ TEST_P(RenderEngineTest, testDimming_withoutTargetLuminance) { } TEST_P(RenderEngineTest, test_isOpaque) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2972,7 +2926,7 @@ TEST_P(RenderEngineTest, test_isOpaque) { } TEST_P(RenderEngineTest, test_tonemapPQMatches) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } @@ -2989,7 +2943,7 @@ TEST_P(RenderEngineTest, test_tonemapPQMatches) { } TEST_P(RenderEngineTest, test_tonemapHLGMatches) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } @@ -3006,7 +2960,7 @@ TEST_P(RenderEngineTest, test_tonemapHLGMatches) { } TEST_P(RenderEngineTest, r8_behaves_as_mask) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -3066,7 +3020,7 @@ TEST_P(RenderEngineTest, r8_behaves_as_mask) { } TEST_P(RenderEngineTest, r8_respects_color_transform) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -3131,7 +3085,7 @@ TEST_P(RenderEngineTest, r8_respects_color_transform) { } TEST_P(RenderEngineTest, r8_respects_color_transform_when_device_handles) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -3199,7 +3153,7 @@ TEST_P(RenderEngineTest, r8_respects_color_transform_when_device_handles) { } TEST_P(RenderEngineTest, primeShaderCache) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp index 1b9adba063..d56dbb2a7b 100644 --- a/libs/renderengine/tests/RenderEngineThreadedTest.cpp +++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp @@ -35,8 +35,7 @@ struct RenderEngineThreadedTest : public ::testing::Test { void SetUp() override { mThreadedRE = renderengine::threaded::RenderEngineThreaded::create( - [this]() { return std::unique_ptr<renderengine::RenderEngine>(mRenderEngine); }, - renderengine::RenderEngine::RenderEngineType::SKIA_GL_THREADED); + [this]() { return std::unique_ptr<renderengine::RenderEngine>(mRenderEngine); }); } std::unique_ptr<renderengine::threaded::RenderEngineThreaded> mThreadedRE; diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp index f58f543854..f4cebc05ec 100644 --- a/libs/renderengine/threaded/RenderEngineThreaded.cpp +++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp @@ -33,13 +33,12 @@ namespace android { namespace renderengine { namespace threaded { -std::unique_ptr<RenderEngineThreaded> RenderEngineThreaded::create(CreateInstanceFactory factory, - RenderEngineType type) { - return std::make_unique<RenderEngineThreaded>(std::move(factory), type); +std::unique_ptr<RenderEngineThreaded> RenderEngineThreaded::create(CreateInstanceFactory factory) { + return std::make_unique<RenderEngineThreaded>(std::move(factory)); } -RenderEngineThreaded::RenderEngineThreaded(CreateInstanceFactory factory, RenderEngineType type) - : RenderEngine(type) { +RenderEngineThreaded::RenderEngineThreaded(CreateInstanceFactory factory) + : RenderEngine(Threaded::YES) { ATRACE_CALL(); std::lock_guard lockThread(mThreadMutex); diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h index 3f1e67f285..d440c961e7 100644 --- a/libs/renderengine/threaded/RenderEngineThreaded.h +++ b/libs/renderengine/threaded/RenderEngineThreaded.h @@ -37,10 +37,9 @@ using CreateInstanceFactory = std::function<std::unique_ptr<renderengine::Render */ class RenderEngineThreaded : public RenderEngine { public: - static std::unique_ptr<RenderEngineThreaded> create(CreateInstanceFactory factory, - RenderEngineType type); + static std::unique_ptr<RenderEngineThreaded> create(CreateInstanceFactory factory); - RenderEngineThreaded(CreateInstanceFactory factory, RenderEngineType type); + RenderEngineThreaded(CreateInstanceFactory factory); ~RenderEngineThreaded() override; std::future<void> primeCache(bool shouldPrimeUltraHDR) override; diff --git a/services/inputflinger/InputFilter.cpp b/services/inputflinger/InputFilter.cpp index 72c6f1a73b..1ada5e5678 100644 --- a/services/inputflinger/InputFilter.cpp +++ b/services/inputflinger/InputFilter.cpp @@ -118,6 +118,15 @@ void InputFilter::setAccessibilityBounceKeysThreshold(nsecs_t threshold) { } } +void InputFilter::setAccessibilitySlowKeysThreshold(nsecs_t threshold) { + std::scoped_lock _l(mLock); + + if (mConfig.slowKeysThresholdNs != threshold) { + mConfig.slowKeysThresholdNs = threshold; + notifyConfigurationChangedLocked(); + } +} + void InputFilter::setAccessibilityStickyKeysEnabled(bool enabled) { std::scoped_lock _l(mLock); diff --git a/services/inputflinger/InputFilter.h b/services/inputflinger/InputFilter.h index 153d29dd53..4ddc9f4f6b 100644 --- a/services/inputflinger/InputFilter.h +++ b/services/inputflinger/InputFilter.h @@ -35,6 +35,7 @@ public: */ virtual void dump(std::string& dump) = 0; virtual void setAccessibilityBounceKeysThreshold(nsecs_t threshold) = 0; + virtual void setAccessibilitySlowKeysThreshold(nsecs_t threshold) = 0; virtual void setAccessibilityStickyKeysEnabled(bool enabled) = 0; }; @@ -61,6 +62,7 @@ public: void notifyDeviceReset(const NotifyDeviceResetArgs& args) override; void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override; void setAccessibilityBounceKeysThreshold(nsecs_t threshold) override; + void setAccessibilitySlowKeysThreshold(nsecs_t threshold) override; void setAccessibilityStickyKeysEnabled(bool enabled) override; void dump(std::string& dump) override; diff --git a/services/inputflinger/InputFilterCallbacks.cpp b/services/inputflinger/InputFilterCallbacks.cpp index a8759b7cbd..6c3144230f 100644 --- a/services/inputflinger/InputFilterCallbacks.cpp +++ b/services/inputflinger/InputFilterCallbacks.cpp @@ -17,6 +17,11 @@ #define LOG_TAG "InputFilterCallbacks" #include "InputFilterCallbacks.h" +#include <aidl/com/android/server/inputflinger/BnInputThread.h> +#include <android/binder_auto_utils.h> +#include <utils/StrongPointer.h> +#include <utils/Thread.h> +#include <functional> namespace android { @@ -29,6 +34,47 @@ NotifyKeyArgs keyEventToNotifyKeyArgs(const AidlKeyEvent& event) { event.scanCode, event.metaState, event.downTime); } +namespace { + +using namespace aidl::com::android::server::inputflinger; + +class InputFilterThreadImpl : public Thread { +public: + explicit InputFilterThreadImpl(std::function<void()> loop) + : Thread(/*canCallJava=*/true), mThreadLoop(loop) {} + + ~InputFilterThreadImpl() {} + +private: + std::function<void()> mThreadLoop; + + bool threadLoop() override { + mThreadLoop(); + return true; + } +}; + +class InputFilterThread : public BnInputThread { +public: + InputFilterThread(std::shared_ptr<IInputThreadCallback> callback) : mCallback(callback) { + mThread = sp<InputFilterThreadImpl>::make([this]() { loopOnce(); }); + mThread->run("InputFilterThread", ANDROID_PRIORITY_URGENT_DISPLAY); + } + + ndk::ScopedAStatus finish() override { + mThread->requestExit(); + return ndk::ScopedAStatus::ok(); + } + +private: + sp<Thread> mThread; + std::shared_ptr<IInputThreadCallback> mCallback; + + void loopOnce() { LOG_ALWAYS_FATAL_IF(!mCallback->loopOnce().isOk()); } +}; + +} // namespace + InputFilterCallbacks::InputFilterCallbacks(InputListenerInterface& listener, InputFilterPolicyInterface& policy) : mNextListener(listener), mPolicy(policy) {} @@ -49,6 +95,13 @@ ndk::ScopedAStatus InputFilterCallbacks::onModifierStateChanged(int32_t modifier return ndk::ScopedAStatus::ok(); } +ndk::ScopedAStatus InputFilterCallbacks::createInputFilterThread( + const std::shared_ptr<IInputThreadCallback>& callback, + std::shared_ptr<IInputThread>* aidl_return) { + *aidl_return = ndk::SharedRefBase::make<InputFilterThread>(callback); + return ndk::ScopedAStatus::ok(); +} + uint32_t InputFilterCallbacks::getModifierState() { std::scoped_lock _l(mLock); return mStickyModifierState.modifierState; diff --git a/services/inputflinger/InputFilterCallbacks.h b/services/inputflinger/InputFilterCallbacks.h index 31c160aeb9..a74955b5c6 100644 --- a/services/inputflinger/InputFilterCallbacks.h +++ b/services/inputflinger/InputFilterCallbacks.h @@ -19,6 +19,7 @@ #include <aidl/com/android/server/inputflinger/IInputFlingerRust.h> #include <android/binder_auto_utils.h> #include <utils/Mutex.h> +#include <memory> #include <mutex> #include "InputFilterPolicyInterface.h" #include "InputListener.h" @@ -31,6 +32,9 @@ namespace android { using IInputFilter = aidl::com::android::server::inputflinger::IInputFilter; using AidlKeyEvent = aidl::com::android::server::inputflinger::KeyEvent; +using aidl::com::android::server::inputflinger::IInputThread; +using IInputThreadCallback = + aidl::com::android::server::inputflinger::IInputThread::IInputThreadCallback; class InputFilterCallbacks : public IInputFilter::BnInputFilterCallbacks { public: @@ -53,6 +57,9 @@ private: ndk::ScopedAStatus sendKeyEvent(const AidlKeyEvent& event) override; ndk::ScopedAStatus onModifierStateChanged(int32_t modifierState, int32_t lockedModifierState) override; + ndk::ScopedAStatus createInputFilterThread( + const std::shared_ptr<IInputThreadCallback>& callback, + std::shared_ptr<IInputThread>* aidl_return) override; }; } // namespace android
\ No newline at end of file diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp index 4571ef4481..3ac4285304 100644 --- a/services/inputflinger/PointerChoreographer.cpp +++ b/services/inputflinger/PointerChoreographer.cpp @@ -109,7 +109,9 @@ NotifyMotionArgs PointerChoreographer::processMouseEventLocked(const NotifyMotio const float deltaX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); const float deltaY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); pc.move(deltaX, deltaY); - pc.unfade(PointerControllerInterface::Transition::IMMEDIATE); + if (canUnfadeOnDisplay(displayId)) { + pc.unfade(PointerControllerInterface::Transition::IMMEDIATE); + } const auto [x, y] = pc.getPosition(); NotifyMotionArgs newArgs(args); @@ -131,7 +133,9 @@ NotifyMotionArgs PointerChoreographer::processTouchpadEventLocked(const NotifyMo const float deltaX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); const float deltaY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); pc.move(deltaX, deltaY); - pc.unfade(PointerControllerInterface::Transition::IMMEDIATE); + if (canUnfadeOnDisplay(displayId)) { + pc.unfade(PointerControllerInterface::Transition::IMMEDIATE); + } const auto [x, y] = pc.getPosition(); newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); @@ -140,7 +144,9 @@ NotifyMotionArgs PointerChoreographer::processTouchpadEventLocked(const NotifyMo newArgs.yCursorPosition = y; } else { // This is a trackpad gesture with fake finger(s) that should not move the mouse pointer. - pc.unfade(PointerControllerInterface::Transition::IMMEDIATE); + if (canUnfadeOnDisplay(displayId)) { + pc.unfade(PointerControllerInterface::Transition::IMMEDIATE); + } const auto [x, y] = pc.getPosition(); for (uint32_t i = 0; i < newArgs.getPointerCount(); i++) { @@ -223,7 +229,7 @@ void PointerChoreographer::processStylusHoverEventLocked(const NotifyMotionArgs& if (args.action == AMOTION_EVENT_ACTION_HOVER_EXIT) { pc.fade(PointerControllerInterface::Transition::IMMEDIATE); pc.updatePointerIcon(PointerIconStyle::TYPE_NOT_SPECIFIED); - } else { + } else if (canUnfadeOnDisplay(args.displayId)) { pc.unfade(PointerControllerInterface::Transition::IMMEDIATE); } } @@ -323,6 +329,10 @@ InputDeviceInfo* PointerChoreographer::findInputDeviceLocked(DeviceId deviceId) return it != mInputDeviceInfos.end() ? &(*it) : nullptr; } +bool PointerChoreographer::canUnfadeOnDisplay(int32_t displayId) { + return mDisplaysWithPointersHidden.find(displayId) == mDisplaysWithPointersHidden.end(); +} + void PointerChoreographer::updatePointerControllersLocked() { std::set<int32_t /*displayId*/> mouseDisplaysToKeep; std::set<DeviceId> touchDevicesToKeep; @@ -342,7 +352,7 @@ void PointerChoreographer::updatePointerControllersLocked() { mMousePointersByDisplay.try_emplace(displayId, getMouseControllerConstructor(displayId)); auto [_, isNewMouseDevice] = mMouseDevices.emplace(info.getId()); - if (isNewMouseDevice || isNewMousePointer) { + if ((isNewMouseDevice || isNewMousePointer) && canUnfadeOnDisplay(displayId)) { mousePointerIt->second->unfade(PointerControllerInterface::Transition::IMMEDIATE); } } @@ -513,6 +523,28 @@ bool PointerChoreographer::setPointerIcon( return true; } +void PointerChoreographer::setPointerIconVisibility(int32_t displayId, bool visible) { + std::scoped_lock lock(mLock); + if (visible) { + mDisplaysWithPointersHidden.erase(displayId); + // We do not unfade the icons here, because we don't know when the last event happened. + return; + } + + mDisplaysWithPointersHidden.emplace(displayId); + + // Hide any icons that are currently visible on the display. + if (auto it = mMousePointersByDisplay.find(displayId); it != mMousePointersByDisplay.end()) { + const auto& [_, controller] = *it; + controller->fade(PointerControllerInterface::Transition::IMMEDIATE); + } + for (const auto& [_, controller] : mStylusPointersByDevice) { + if (controller->getDisplayId() == displayId) { + controller->fade(PointerControllerInterface::Transition::IMMEDIATE); + } + } +} + PointerChoreographer::ControllerConstructor PointerChoreographer::getMouseControllerConstructor( int32_t displayId) { std::function<std::shared_ptr<PointerControllerInterface>()> ctor = diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h index f46419ec2e..6aab3aade0 100644 --- a/services/inputflinger/PointerChoreographer.h +++ b/services/inputflinger/PointerChoreographer.h @@ -67,6 +67,11 @@ public: */ virtual bool setPointerIcon(std::variant<std::unique_ptr<SpriteIcon>, PointerIconStyle> icon, int32_t displayId, DeviceId deviceId) = 0; + /** + * Set whether pointer icons for mice, touchpads, and styluses should be visible on the + * given display. + */ + virtual void setPointerIconVisibility(int32_t displayId, bool visible) = 0; /** * This method may be called on any thread (usually by the input manager on a binder thread). @@ -89,6 +94,7 @@ public: void setStylusPointerIconEnabled(bool enabled) override; bool setPointerIcon(std::variant<std::unique_ptr<SpriteIcon>, PointerIconStyle> icon, int32_t displayId, DeviceId deviceId) override; + void setPointerIconVisibility(int32_t displayId, bool visible) override; void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override; void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override; @@ -110,6 +116,7 @@ private: std::pair<int32_t, PointerControllerInterface&> getDisplayIdAndMouseControllerLocked( int32_t associatedDisplayId) REQUIRES(mLock); InputDeviceInfo* findInputDeviceLocked(DeviceId deviceId) REQUIRES(mLock); + bool canUnfadeOnDisplay(int32_t displayId) REQUIRES(mLock); NotifyMotionArgs processMotion(const NotifyMotionArgs& args); NotifyMotionArgs processMouseEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock); @@ -143,6 +150,7 @@ private: std::vector<DisplayViewport> mViewports GUARDED_BY(mLock); bool mShowTouchesEnabled GUARDED_BY(mLock); bool mStylusPointerIconEnabled GUARDED_BY(mLock); + std::set<int32_t /*displayId*/> mDisplaysWithPointersHidden; }; } // namespace android diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl index 2921d30b22..994d1c4b1a 100644 --- a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl +++ b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl @@ -17,6 +17,8 @@ package com.android.server.inputflinger; import com.android.server.inputflinger.DeviceInfo; +import com.android.server.inputflinger.IInputThread; +import com.android.server.inputflinger.IInputThread.IInputThreadCallback; import com.android.server.inputflinger.InputFilterConfiguration; import com.android.server.inputflinger.KeyEvent; @@ -36,6 +38,9 @@ interface IInputFilter { /** Sends back modifier state */ void onModifierStateChanged(int modifierState, int lockedModifierState); + + /** Creates an Input filter thread */ + IInputThread createInputFilterThread(in IInputThreadCallback callback); } /** Returns if InputFilter is enabled */ diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/IInputThread.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/IInputThread.aidl new file mode 100644 index 0000000000..2f6b8fc6ff --- /dev/null +++ b/services/inputflinger/aidl/com/android/server/inputflinger/IInputThread.aidl @@ -0,0 +1,45 @@ +/* + * 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 com.android.server.inputflinger; + +/** Interface to handle and run things on an InputThread + * Exposes main functionality of InputThread.h to rust which internally used system/core/libutils + * infrastructure. + * + * <p> + * NOTE: Tried using rust provided threading infrastructure but that uses std::thread which doesn't + * have JNI support and can't call into Java policy that we use currently. libutils provided + * Thread.h also recommends against using std::thread and using the provided infrastructure that + * already provides way of attaching JniEnv to the created thread. So, we are using this interface + * to expose the InputThread infrastructure to rust. + * </p> + * TODO(b/321769871): Implement the threading infrastructure with JniEnv support in rust + */ +interface IInputThread { + /** Finish input thread (if not running, this call does nothing) */ + void finish(); + + /** Callbacks from C++ to call into inputflinger rust components */ + interface IInputThreadCallback { + /** + * The created thread will keep looping and calling this function. + * It's the responsibility of RUST component to appropriately put the thread to sleep and + * wake according to the use case. + */ + void loopOnce(); + } +}
\ No newline at end of file diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl index 38b161203b..9984a6a9ea 100644 --- a/services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl +++ b/services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl @@ -22,6 +22,8 @@ package com.android.server.inputflinger; parcelable InputFilterConfiguration { // Threshold value for Bounce keys filter (check bounce_keys_filter.rs) long bounceKeysThresholdNs; - // If sticky keys filter is enabled + // If sticky keys filter is enabled (check sticky_keys_filter.rs) boolean stickyKeysEnabled; + // Threshold value for Slow keys filter (check slow_keys_filter.rs) + long slowKeysThresholdNs; }
\ No newline at end of file diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 1085c942dd..c349a5857f 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -35,7 +35,6 @@ #include <input/PrintTools.h> #include <input/TraceTools.h> #include <openssl/mem.h> -#include <powermanager/PowerManager.h> #include <unistd.h> #include <utils/Trace.h> @@ -100,6 +99,9 @@ const std::chrono::duration DEFAULT_INPUT_DISPATCHING_TIMEOUT = std::chrono::mil android::os::IInputConstants::UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS * HwTimeoutMultiplier()); +// The default minimum time gap between two user activity poke events. +const std::chrono::milliseconds DEFAULT_USER_ACTIVITY_POKE_INTERVAL = 100ms; + const std::chrono::duration STALE_EVENT_TIMEOUT = std::chrono::seconds(10) * HwTimeoutMultiplier(); // Log a warning when an event takes longer than this to process, even if an ANR does not occur. @@ -778,6 +780,25 @@ Result<void> validateWindowInfosUpdate(const gui::WindowInfosUpdate& update) { return {}; } +int32_t getUserActivityEventType(const EventEntry& eventEntry) { + switch (eventEntry.type) { + case EventEntry::Type::KEY: { + return USER_ACTIVITY_EVENT_BUTTON; + } + case EventEntry::Type::MOTION: { + const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry); + if (MotionEvent::isTouchEvent(motionEntry.source, motionEntry.action)) { + return USER_ACTIVITY_EVENT_TOUCH; + } + return USER_ACTIVITY_EVENT_OTHER; + } + default: { + LOG_ALWAYS_FATAL("%s events are not user activity", + ftl::enum_string(eventEntry.type).c_str()); + } + } +} + } // namespace // --- InputDispatcher --- @@ -791,6 +812,7 @@ InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy, mPendingEvent(nullptr), mLastDropReason(DropReason::NOT_DROPPED), mIdGenerator(IdGenerator::Source::INPUT_DISPATCHER), + mMinTimeBetweenUserActivityPokes(DEFAULT_USER_ACTIVITY_POKE_INTERVAL), mNextUnblockedEvent(nullptr), mMonitorDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT), mDispatchEnabled(false), @@ -813,6 +835,8 @@ InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy, if (traceBackend) { // TODO: Create input tracer instance. } + + mLastUserActivityTimes.fill(0); } InputDispatcher::~InputDispatcher() { @@ -3140,6 +3164,21 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { // Not poking user activity if the event type does not represent a user activity return; } + + const int32_t eventType = getUserActivityEventType(eventEntry); + if (input_flags::rate_limit_user_activity_poke_in_dispatcher()) { + // Note that we're directly getting the time diff between the current event and the previous + // event. This is assuming that the first user event always happens at a timestamp that is + // greater than `mMinTimeBetweenUserActivityPokes` (otherwise, the first user event will + // wrongly be dropped). In real life, `mMinTimeBetweenUserActivityPokes` is a much smaller + // value than the potential first user activity event time, so this is ok. + std::chrono::nanoseconds timeSinceLastEvent = + std::chrono::nanoseconds(eventEntry.eventTime - mLastUserActivityTimes[eventType]); + if (timeSinceLastEvent < mMinTimeBetweenUserActivityPokes) { + return; + } + } + int32_t displayId = getTargetDisplayId(eventEntry); sp<WindowInfoHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId); const WindowInfo* windowDisablingUserActivityInfo = nullptr; @@ -3150,7 +3189,6 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { } } - int32_t eventType = USER_ACTIVITY_EVENT_OTHER; switch (eventEntry.type) { case EventEntry::Type::MOTION: { const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry); @@ -3164,9 +3202,6 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { } return; } - if (MotionEvent::isTouchEvent(motionEntry.source, motionEntry.action)) { - eventType = USER_ACTIVITY_EVENT_TOUCH; - } break; } case EventEntry::Type::KEY: { @@ -3190,7 +3225,6 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { return; } - eventType = USER_ACTIVITY_EVENT_BUTTON; break; } default: { @@ -3200,6 +3234,7 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { } } + mLastUserActivityTimes[eventType] = eventEntry.eventTime; auto command = [this, eventTime = eventEntry.eventTime, eventType, displayId]() REQUIRES(mLock) { scoped_unlock unlock(mLock); @@ -5292,6 +5327,14 @@ void InputDispatcher::setFocusedApplicationLocked( resetNoFocusedWindowTimeoutLocked(); } +void InputDispatcher::setMinTimeBetweenUserActivityPokes(std::chrono::milliseconds interval) { + if (interval.count() < 0) { + LOG_ALWAYS_FATAL("Minimum time between user activity pokes should be >= 0"); + } + std::scoped_lock _l(mLock); + mMinTimeBetweenUserActivityPokes = interval; +} + /** * Sets the focused display, which is responsible for receiving focus-dispatched input events where * the display not specified. diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 1e11b27d2f..e635852662 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -41,6 +41,7 @@ #include <input/Input.h> #include <input/InputTransport.h> #include <limits.h> +#include <powermanager/PowerManager.h> #include <stddef.h> #include <unistd.h> #include <utils/BitSet.h> @@ -116,6 +117,7 @@ public: int32_t displayId, const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) override; void setFocusedDisplay(int32_t displayId) override; + void setMinTimeBetweenUserActivityPokes(std::chrono::milliseconds interval) override; void setInputDispatchMode(bool enabled, bool frozen) override; void setInputFilterEnabled(bool enabled) override; bool setInTouchMode(bool inTouchMode, gui::Pid pid, gui::Uid uid, bool hasPermission, @@ -211,6 +213,11 @@ private: int64_t mWindowInfosVsyncId GUARDED_BY(mLock); + std::chrono::milliseconds mMinTimeBetweenUserActivityPokes GUARDED_BY(mLock); + + /** Stores the latest user-activity poke event times per user activity types. */ + std::array<nsecs_t, USER_ACTIVITY_EVENT_LAST + 1> mLastUserActivityTimes GUARDED_BY(mLock); + // With each iteration, InputDispatcher nominally processes one queued event, // a timeout, or a response from an input consumer. // This method should only be called on the input dispatcher's own thread. diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h index 001dc6cf7b..c8f3d05ade 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h @@ -101,6 +101,9 @@ public: */ virtual void setFocusedDisplay(int32_t displayId) = 0; + /** Sets the minimum time between user activity pokes. */ + virtual void setMinTimeBetweenUserActivityPokes(std::chrono::milliseconds interval) = 0; + /* Sets the input dispatching mode. * * This method may be called on any thread (usually by the input manager). diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index 40359a42c9..9abfef14dc 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -131,10 +131,10 @@ struct InputReaderConfiguration { // Currently only used when the enable_new_mouse_pointer_ballistics flag is enabled. int32_t mousePointerSpeed; - // Whether to apply an acceleration curve to pointer movements from mice. + // Displays on which an acceleration curve shouldn't be applied for pointer movements from mice. // // Currently only used when the enable_new_mouse_pointer_ballistics flag is enabled. - bool mousePointerAccelerationEnabled; + std::set<int32_t> displaysWithMousePointerAccelerationDisabled; // Velocity control parameters for mouse pointer movements. // @@ -243,7 +243,7 @@ struct InputReaderConfiguration { InputReaderConfiguration() : virtualKeyQuietTime(0), mousePointerSpeed(0), - mousePointerAccelerationEnabled(true), + displaysWithMousePointerAccelerationDisabled(), pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f, static_cast<float>( android::os::IInputConstants:: diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index 4cebd646db..d207ed1655 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -159,14 +159,14 @@ std::list<NotifyArgs> CursorInputMapper::reconfigure(nsecs_t when, out.push_back(NotifyDeviceResetArgs(getContext()->getNextId(), when, getDeviceId())); } - if (!changes.any() || changes.test(InputReaderConfiguration::Change::POINTER_SPEED) || + if (!changes.any() || changes.test(InputReaderConfiguration::Change::DISPLAY_INFO) || configurePointerCapture) { - configureOnChangePointerSpeed(readerConfig); + configureOnChangeDisplayInfo(readerConfig); } - if (!changes.any() || changes.test(InputReaderConfiguration::Change::DISPLAY_INFO) || + if (!changes.any() || changes.test(InputReaderConfiguration::Change::POINTER_SPEED) || configurePointerCapture) { - configureOnChangeDisplayInfo(readerConfig); + configureOnChangePointerSpeed(readerConfig); } return out; } @@ -510,7 +510,8 @@ void CursorInputMapper::configureOnChangePointerSpeed(const InputReaderConfigura } else { if (mEnableNewMousePointerBallistics) { mNewPointerVelocityControl.setAccelerationEnabled( - config.mousePointerAccelerationEnabled); + config.displaysWithMousePointerAccelerationDisabled.count( + mDisplayId.value_or(ADISPLAY_ID_NONE)) == 0); mNewPointerVelocityControl.setCurve( createAccelerationCurveForPointerSensitivity(config.mousePointerSpeed)); } else { diff --git a/services/inputflinger/reader/mapper/SlopController.cpp b/services/inputflinger/reader/mapper/SlopController.cpp index f79219f151..9ec02a6f86 100644 --- a/services/inputflinger/reader/mapper/SlopController.cpp +++ b/services/inputflinger/reader/mapper/SlopController.cpp @@ -54,11 +54,13 @@ float SlopController::consumeEvent(nsecs_t eventTimeNanos, float value) { mCumulativeValue += value; if (abs(mCumulativeValue) >= mSlopThreshold) { + ALOGD("SlopController: did not drop event with value .%3f", value); mHasSlopBeenMet = true; // Return the amount of value that exceeds the slop. return signOf(value) * (abs(mCumulativeValue) - mSlopThreshold); } + ALOGD("SlopController: dropping event with value .%3f", value); return 0; } diff --git a/services/inputflinger/rust/Android.bp b/services/inputflinger/rust/Android.bp index 2803805619..9e6dbe432d 100644 --- a/services/inputflinger/rust/Android.bp +++ b/services/inputflinger/rust/Android.bp @@ -42,6 +42,7 @@ rust_defaults { "libbinder_rs", "liblog_rust", "liblogger", + "libnix", ], host_supported: true, } diff --git a/services/inputflinger/rust/bounce_keys_filter.rs b/services/inputflinger/rust/bounce_keys_filter.rs index 894b881638..2d5039a1b1 100644 --- a/services/inputflinger/rust/bounce_keys_filter.rs +++ b/services/inputflinger/rust/bounce_keys_filter.rs @@ -118,6 +118,10 @@ impl Filter for BounceKeysFilter { } self.next.notify_devices_changed(device_infos); } + + fn destroy(&mut self) { + self.next.destroy(); + } } #[cfg(test)] diff --git a/services/inputflinger/rust/input_filter.rs b/services/inputflinger/rust/input_filter.rs index e94a71fbf8..a544fa36ae 100644 --- a/services/inputflinger/rust/input_filter.rs +++ b/services/inputflinger/rust/input_filter.rs @@ -22,11 +22,14 @@ use binder::{Interface, Strong}; use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ DeviceInfo::DeviceInfo, IInputFilter::{IInputFilter, IInputFilterCallbacks::IInputFilterCallbacks}, + IInputThread::{IInputThread, IInputThreadCallback::IInputThreadCallback}, InputFilterConfiguration::InputFilterConfiguration, KeyEvent::KeyEvent, }; use crate::bounce_keys_filter::BounceKeysFilter; +use crate::input_filter_thread::InputFilterThread; +use crate::slow_keys_filter::SlowKeysFilter; use crate::sticky_keys_filter::StickyKeysFilter; use log::{error, info}; use std::sync::{Arc, Mutex, RwLock}; @@ -35,6 +38,7 @@ use std::sync::{Arc, Mutex, RwLock}; pub trait Filter { fn notify_key(&mut self, event: &KeyEvent); fn notify_devices_changed(&mut self, device_infos: &[DeviceInfo]); + fn destroy(&mut self); } struct InputFilterState { @@ -50,6 +54,7 @@ pub struct InputFilter { // Access to mutable references to mutable state (includes access to filters, enabled, etc.) is // guarded by Mutex for thread safety state: Mutex<InputFilterState>, + input_filter_thread: InputFilterThread, } impl Interface for InputFilter {} @@ -67,7 +72,11 @@ impl InputFilter { first_filter: Box<dyn Filter + Send + Sync>, callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>, ) -> InputFilter { - Self { callbacks, state: Mutex::new(InputFilterState { first_filter, enabled: false }) } + Self { + callbacks: callbacks.clone(), + state: Mutex::new(InputFilterState { first_filter, enabled: false }), + input_filter_thread: InputFilterThread::new(InputFilterThreadCreator::new(callbacks)), + } } } @@ -89,24 +98,36 @@ impl IInputFilter for InputFilter { } fn notifyConfigurationChanged(&self, config: &InputFilterConfiguration) -> binder::Result<()> { - let mut state = self.state.lock().unwrap(); - let mut first_filter: Box<dyn Filter + Send + Sync> = - Box::new(BaseFilter::new(self.callbacks.clone())); - if config.stickyKeysEnabled { - first_filter = Box::new(StickyKeysFilter::new( - first_filter, - ModifierStateListener::new(self.callbacks.clone()), - )); - state.enabled = true; - info!("Sticky keys filter is installed"); - } - if config.bounceKeysThresholdNs > 0 { - first_filter = - Box::new(BounceKeysFilter::new(first_filter, config.bounceKeysThresholdNs)); - state.enabled = true; - info!("Bounce keys filter is installed"); + { + let mut state = self.state.lock().unwrap(); + state.first_filter.destroy(); + let mut first_filter: Box<dyn Filter + Send + Sync> = + Box::new(BaseFilter::new(self.callbacks.clone())); + if config.stickyKeysEnabled { + first_filter = Box::new(StickyKeysFilter::new( + first_filter, + ModifierStateListener::new(self.callbacks.clone()), + )); + state.enabled = true; + info!("Sticky keys filter is installed"); + } + if config.slowKeysThresholdNs > 0 { + first_filter = Box::new(SlowKeysFilter::new( + first_filter, + config.slowKeysThresholdNs, + self.input_filter_thread.clone(), + )); + state.enabled = true; + info!("Slow keys filter is installed"); + } + if config.bounceKeysThresholdNs > 0 { + first_filter = + Box::new(BounceKeysFilter::new(first_filter, config.bounceKeysThresholdNs)); + state.enabled = true; + info!("Bounce keys filter is installed"); + } + state.first_filter = first_filter; } - state.first_filter = first_filter; Result::Ok(()) } } @@ -132,27 +153,51 @@ impl Filter for BaseFilter { fn notify_devices_changed(&mut self, _device_infos: &[DeviceInfo]) { // do nothing } -} -pub struct ModifierStateListener { - callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>, + fn destroy(&mut self) { + // do nothing + } } +/// This struct wraps around IInputFilterCallbacks restricting access to only +/// {@code onModifierStateChanged()} method of the callback. +#[derive(Clone)] +pub struct ModifierStateListener(Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>); + impl ModifierStateListener { - /// Create a new InputFilter instance. pub fn new(callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>) -> ModifierStateListener { - Self { callbacks } + Self(callbacks) } pub fn modifier_state_changed(&self, modifier_state: u32, locked_modifier_state: u32) { let _ = self - .callbacks + .0 .read() .unwrap() .onModifierStateChanged(modifier_state as i32, locked_modifier_state as i32); } } +/// This struct wraps around IInputFilterCallbacks restricting access to only +/// {@code createInputFilterThread()} method of the callback. +#[derive(Clone)] +pub struct InputFilterThreadCreator(Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>); + +impl InputFilterThreadCreator { + pub fn new( + callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>, + ) -> InputFilterThreadCreator { + Self(callbacks) + } + + pub fn create( + &self, + input_thread_callback: &Strong<dyn IInputThreadCallback>, + ) -> Strong<dyn IInputThread> { + self.0.read().unwrap().createInputFilterThread(input_thread_callback).unwrap() + } +} + #[cfg(test)] mod tests { use crate::input_filter::{ @@ -218,7 +263,7 @@ mod tests { let input_filter = InputFilter::new(Strong::new(Box::new(test_callbacks))); let result = input_filter.notifyConfigurationChanged(&InputFilterConfiguration { bounceKeysThresholdNs: 100, - stickyKeysEnabled: false, + ..Default::default() }); assert!(result.is_ok()); let result = input_filter.isEnabled(); @@ -231,8 +276,8 @@ mod tests { let test_callbacks = TestCallbacks::new(); let input_filter = InputFilter::new(Strong::new(Box::new(test_callbacks))); let result = input_filter.notifyConfigurationChanged(&InputFilterConfiguration { - bounceKeysThresholdNs: 0, stickyKeysEnabled: true, + ..Default::default() }); assert!(result.is_ok()); let result = input_filter.isEnabled(); @@ -240,6 +285,33 @@ mod tests { assert!(result.unwrap()); } + #[test] + fn test_notify_configuration_changed_enabled_slow_keys() { + let test_callbacks = TestCallbacks::new(); + let input_filter = InputFilter::new(Strong::new(Box::new(test_callbacks))); + let result = input_filter.notifyConfigurationChanged(&InputFilterConfiguration { + slowKeysThresholdNs: 100, + ..Default::default() + }); + assert!(result.is_ok()); + let result = input_filter.isEnabled(); + assert!(result.is_ok()); + assert!(result.unwrap()); + } + + #[test] + fn test_notify_configuration_changed_destroys_existing_filters() { + let test_filter = TestFilter::new(); + let test_callbacks = TestCallbacks::new(); + let input_filter = InputFilter::create_input_filter( + Box::new(test_filter.clone()), + Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks)))), + ); + let _ = input_filter + .notifyConfigurationChanged(&InputFilterConfiguration { ..Default::default() }); + assert!(test_filter.is_destroy_called()); + } + fn create_key_event() -> KeyEvent { KeyEvent { id: 1, @@ -271,6 +343,7 @@ pub mod test_filter { struct TestFilterInner { is_device_changed_called: bool, last_event: Option<KeyEvent>, + is_destroy_called: bool, } #[derive(Default, Clone)] @@ -296,6 +369,10 @@ pub mod test_filter { pub fn is_device_changed_called(&self) -> bool { self.0.read().unwrap().is_device_changed_called } + + pub fn is_destroy_called(&self) -> bool { + self.0.read().unwrap().is_destroy_called + } } impl Filter for TestFilter { @@ -305,14 +382,19 @@ pub mod test_filter { fn notify_devices_changed(&mut self, _device_infos: &[DeviceInfo]) { self.inner().is_device_changed_called = true; } + fn destroy(&mut self) { + self.inner().is_destroy_called = true; + } } } #[cfg(test)] pub mod test_callbacks { - use binder::Interface; + use binder::{BinderFeatures, Interface, Strong}; use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ - IInputFilter::IInputFilterCallbacks::IInputFilterCallbacks, KeyEvent::KeyEvent, + IInputFilter::IInputFilterCallbacks::IInputFilterCallbacks, + IInputThread::{BnInputThread, IInputThread, IInputThreadCallback::IInputThreadCallback}, + KeyEvent::KeyEvent, }; use std::sync::{Arc, RwLock, RwLockWriteGuard}; @@ -321,6 +403,7 @@ pub mod test_callbacks { last_modifier_state: u32, last_locked_modifier_state: u32, last_event: Option<KeyEvent>, + test_thread: Option<TestThread>, } #[derive(Default, Clone)] @@ -354,6 +437,17 @@ pub mod test_callbacks { pub fn get_last_locked_modifier_state(&self) -> u32 { self.0.read().unwrap().last_locked_modifier_state } + + pub fn is_thread_created(&self) -> bool { + self.0.read().unwrap().test_thread.is_some() + } + + pub fn is_thread_finished(&self) -> bool { + if let Some(test_thread) = &self.0.read().unwrap().test_thread { + return test_thread.is_finish_called(); + } + false + } } impl IInputFilterCallbacks for TestCallbacks { @@ -371,5 +465,45 @@ pub mod test_callbacks { self.inner().last_locked_modifier_state = locked_modifier_state as u32; Result::Ok(()) } + + fn createInputFilterThread( + &self, + _callback: &Strong<dyn IInputThreadCallback>, + ) -> std::result::Result<Strong<dyn IInputThread>, binder::Status> { + let test_thread = TestThread::new(); + self.inner().test_thread = Some(test_thread.clone()); + Result::Ok(BnInputThread::new_binder(test_thread, BinderFeatures::default())) + } + } + + #[derive(Default)] + struct TestThreadInner { + is_finish_called: bool, + } + + #[derive(Default, Clone)] + struct TestThread(Arc<RwLock<TestThreadInner>>); + + impl Interface for TestThread {} + + impl TestThread { + pub fn new() -> Self { + Default::default() + } + + fn inner(&self) -> RwLockWriteGuard<'_, TestThreadInner> { + self.0.write().unwrap() + } + + pub fn is_finish_called(&self) -> bool { + self.0.read().unwrap().is_finish_called + } + } + + impl IInputThread for TestThread { + fn finish(&self) -> binder::Result<()> { + self.inner().is_finish_called = true; + Result::Ok(()) + } } } diff --git a/services/inputflinger/rust/input_filter_thread.rs b/services/inputflinger/rust/input_filter_thread.rs new file mode 100644 index 0000000000..2d503aee70 --- /dev/null +++ b/services/inputflinger/rust/input_filter_thread.rs @@ -0,0 +1,452 @@ +/* + * 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. + */ + +//! Input filter thread implementation in rust. +//! Using IInputFilter.aidl interface to create ever looping thread with JNI support, rest of +//! thread handling is done from rust side. +//! +//! NOTE: Tried using rust provided threading infrastructure but that uses std::thread which doesn't +//! have JNI support and can't call into Java policy that we use currently. libutils provided +//! Thread.h also recommends against using std::thread and using the provided infrastructure that +//! already provides way of attaching JniEnv to the created thread. So, we are using an AIDL +//! interface to expose the InputThread infrastructure to rust. + +use crate::input_filter::InputFilterThreadCreator; +use binder::{BinderFeatures, Interface, Strong}; +use com_android_server_inputflinger::aidl::com::android::server::inputflinger::IInputThread::{ + IInputThread, IInputThreadCallback::BnInputThreadCallback, + IInputThreadCallback::IInputThreadCallback, +}; +use log::{debug, error}; +use nix::{sys::time::TimeValLike, time::clock_gettime, time::ClockId}; +use std::sync::{Arc, RwLock, RwLockWriteGuard}; +use std::time::Duration; +use std::{thread, thread::Thread}; + +/// Interface to receive callback from Input filter thread +pub trait ThreadCallback { + /// Calls back after the requested timeout expires. + /// {@see InputFilterThread.request_timeout_at_time(...)} + /// + /// NOTE: In case of multiple requests, the timeout request which is earliest in time, will be + /// fulfilled and notified to all the listeners. It's up to the listeners to re-request another + /// timeout in the future. + fn notify_timeout_expired(&self, when_nanos: i64); + /// Unique name for the listener, which will be used to uniquely identify the listener. + fn name(&self) -> &str; +} + +#[derive(Clone)] +pub struct InputFilterThread { + thread_creator: InputFilterThreadCreator, + thread_callback_handler: ThreadCallbackHandler, + inner: Arc<RwLock<InputFilterThreadInner>>, +} + +struct InputFilterThreadInner { + cpp_thread: Option<Strong<dyn IInputThread>>, + looper: Option<Thread>, + next_timeout: i64, + is_finishing: bool, +} + +impl InputFilterThread { + /// Create a new InputFilterThread instance. + /// NOTE: This will create a new thread. Clone the existing instance to reuse the same thread. + pub fn new(thread_creator: InputFilterThreadCreator) -> InputFilterThread { + Self { + thread_creator, + thread_callback_handler: ThreadCallbackHandler::new(), + inner: Arc::new(RwLock::new(InputFilterThreadInner { + cpp_thread: None, + looper: None, + next_timeout: i64::MAX, + is_finishing: false, + })), + } + } + + /// Listener requesting a timeout in future will receive a callback at or before the requested + /// time on the input filter thread. + /// {@see ThreadCallback.notify_timeout_expired(...)} + pub fn request_timeout_at_time(&self, when_nanos: i64) { + let filter_thread = &mut self.filter_thread(); + if when_nanos < filter_thread.next_timeout { + filter_thread.next_timeout = when_nanos; + if let Some(looper) = &filter_thread.looper { + looper.unpark(); + } + } + } + + /// Registers a callback listener. + /// + /// NOTE: If a listener with the same name already exists when registering using + /// {@see InputFilterThread.register_thread_callback(...)}, we will ignore the listener. You + /// must clear any previously registered listeners using + /// {@see InputFilterThread.unregister_thread_callback(...) before registering the new listener. + /// + /// NOTE: Also, registering a callback will start the looper if not already started. + pub fn register_thread_callback(&self, callback: Box<dyn ThreadCallback + Send + Sync>) { + self.thread_callback_handler.register_thread_callback(callback); + self.start(); + } + + /// Unregisters a callback listener. + /// + /// NOTE: Unregistering a callback will stop the looper if not other callback registered. + pub fn unregister_thread_callback(&self, callback: Box<dyn ThreadCallback + Send + Sync>) { + self.thread_callback_handler.unregister_thread_callback(callback); + // Stop the thread if no registered callbacks exist. We will recreate the thread when new + // callbacks are registered. + let has_callbacks = self.thread_callback_handler.has_callbacks(); + if !has_callbacks { + self.stop(); + } + } + + fn start(&self) { + debug!("InputFilterThread: start thread"); + let filter_thread = &mut self.filter_thread(); + if filter_thread.cpp_thread.is_none() { + filter_thread.cpp_thread = Some(self.thread_creator.create( + &BnInputThreadCallback::new_binder(self.clone(), BinderFeatures::default()), + )); + filter_thread.looper = None; + filter_thread.is_finishing = false; + } + } + + fn stop(&self) { + debug!("InputFilterThread: stop thread"); + let filter_thread = &mut self.filter_thread(); + filter_thread.is_finishing = true; + if let Some(looper) = &filter_thread.looper { + looper.unpark(); + } + if let Some(cpp_thread) = &filter_thread.cpp_thread { + let _ = cpp_thread.finish(); + } + // Clear all references + filter_thread.cpp_thread = None; + filter_thread.looper = None; + } + + fn loop_once(&self, now: i64) { + let mut wake_up_time = i64::MAX; + let mut timeout_expired = false; + { + // acquire thread lock + let filter_thread = &mut self.filter_thread(); + if filter_thread.is_finishing { + // Thread is finishing so don't block processing on it and let it loop. + return; + } + if filter_thread.next_timeout != i64::MAX { + if filter_thread.next_timeout <= now { + timeout_expired = true; + filter_thread.next_timeout = i64::MAX; + } else { + wake_up_time = filter_thread.next_timeout; + } + } + if filter_thread.looper.is_none() { + filter_thread.looper = Some(std::thread::current()); + } + } // release thread lock + if timeout_expired { + self.thread_callback_handler.notify_timeout_expired(now); + } + if wake_up_time == i64::MAX { + thread::park(); + } else { + let duration_now = Duration::from_nanos(now as u64); + let duration_wake_up = Duration::from_nanos(wake_up_time as u64); + thread::park_timeout(duration_wake_up - duration_now); + } + } + + fn filter_thread(&self) -> RwLockWriteGuard<'_, InputFilterThreadInner> { + self.inner.write().unwrap() + } +} + +impl Interface for InputFilterThread {} + +impl IInputThreadCallback for InputFilterThread { + fn loopOnce(&self) -> binder::Result<()> { + self.loop_once(clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds()); + Result::Ok(()) + } +} + +#[derive(Default, Clone)] +struct ThreadCallbackHandler(Arc<RwLock<ThreadCallbackHandlerInner>>); + +#[derive(Default)] +struct ThreadCallbackHandlerInner { + callbacks: Vec<Box<dyn ThreadCallback + Send + Sync>>, +} + +impl ThreadCallbackHandler { + fn new() -> Self { + Default::default() + } + + fn has_callbacks(&self) -> bool { + !&self.0.read().unwrap().callbacks.is_empty() + } + + fn register_thread_callback(&self, callback: Box<dyn ThreadCallback + Send + Sync>) { + let callbacks = &mut self.0.write().unwrap().callbacks; + if callbacks.iter().any(|x| x.name() == callback.name()) { + error!( + "InputFilterThread: register_thread_callback, callback {:?} already exists!", + callback.name() + ); + return; + } + debug!( + "InputFilterThread: register_thread_callback, callback {:?} added!", + callback.name() + ); + callbacks.push(callback); + } + + fn unregister_thread_callback(&self, callback: Box<dyn ThreadCallback + Send + Sync>) { + let callbacks = &mut self.0.write().unwrap().callbacks; + if let Some(index) = callbacks.iter().position(|x| x.name() == callback.name()) { + callbacks.remove(index); + debug!( + "InputFilterThread: unregister_thread_callback, callback {:?} removed!", + callback.name() + ); + return; + } + error!( + "InputFilterThread: unregister_thread_callback, callback {:?} doesn't exist", + callback.name() + ); + } + + fn notify_timeout_expired(&self, when_nanos: i64) { + let callbacks = &self.0.read().unwrap().callbacks; + for callback in callbacks.iter() { + callback.notify_timeout_expired(when_nanos); + } + } +} + +#[cfg(test)] +mod tests { + use crate::input_filter::test_callbacks::TestCallbacks; + use crate::input_filter_thread::{ + test_thread::TestThread, test_thread_callback::TestThreadCallback, + }; + + #[test] + fn test_register_callback_creates_cpp_thread() { + let test_callbacks = TestCallbacks::new(); + let test_thread = TestThread::new(test_callbacks.clone()); + let test_thread_callback = TestThreadCallback::new(); + test_thread.register_thread_callback(test_thread_callback); + assert!(test_callbacks.is_thread_created()); + } + + #[test] + fn test_unregister_callback_finishes_cpp_thread() { + let test_callbacks = TestCallbacks::new(); + let test_thread = TestThread::new(test_callbacks.clone()); + let test_thread_callback = TestThreadCallback::new(); + test_thread.register_thread_callback(test_thread_callback.clone()); + test_thread.unregister_thread_callback(test_thread_callback); + assert!(test_callbacks.is_thread_finished()); + } + + #[test] + fn test_notify_timeout_called_after_timeout_expired() { + let test_callbacks = TestCallbacks::new(); + let test_thread = TestThread::new(test_callbacks.clone()); + let test_thread_callback = TestThreadCallback::new(); + test_thread.register_thread_callback(test_thread_callback.clone()); + test_thread.start_looper(); + + test_thread.request_timeout_at_time(500); + test_thread.dispatch_next(); + + test_thread.move_time_forward(500); + + test_thread.stop_looper(); + assert!(test_thread_callback.is_notify_timeout_called()); + } + + #[test] + fn test_notify_timeout_not_called_before_timeout_expired() { + let test_callbacks = TestCallbacks::new(); + let test_thread = TestThread::new(test_callbacks.clone()); + let test_thread_callback = TestThreadCallback::new(); + test_thread.register_thread_callback(test_thread_callback.clone()); + test_thread.start_looper(); + + test_thread.request_timeout_at_time(500); + test_thread.dispatch_next(); + + test_thread.move_time_forward(100); + + test_thread.stop_looper(); + assert!(!test_thread_callback.is_notify_timeout_called()); + } +} + +#[cfg(test)] +pub mod test_thread { + + use crate::input_filter::{test_callbacks::TestCallbacks, InputFilterThreadCreator}; + use crate::input_filter_thread::{test_thread_callback::TestThreadCallback, InputFilterThread}; + use binder::Strong; + use std::sync::{ + atomic::AtomicBool, atomic::AtomicI64, atomic::Ordering, Arc, RwLock, RwLockWriteGuard, + }; + use std::time::Duration; + + #[derive(Clone)] + pub struct TestThread { + input_thread: InputFilterThread, + inner: Arc<RwLock<TestThreadInner>>, + exit_flag: Arc<AtomicBool>, + now: Arc<AtomicI64>, + } + + struct TestThreadInner { + join_handle: Option<std::thread::JoinHandle<()>>, + } + + impl TestThread { + pub fn new(callbacks: TestCallbacks) -> TestThread { + Self { + input_thread: InputFilterThread::new(InputFilterThreadCreator::new(Arc::new( + RwLock::new(Strong::new(Box::new(callbacks))), + ))), + inner: Arc::new(RwLock::new(TestThreadInner { join_handle: None })), + exit_flag: Arc::new(AtomicBool::new(false)), + now: Arc::new(AtomicI64::new(0)), + } + } + + fn inner(&self) -> RwLockWriteGuard<'_, TestThreadInner> { + self.inner.write().unwrap() + } + + pub fn get_input_thread(&self) -> InputFilterThread { + self.input_thread.clone() + } + + pub fn register_thread_callback(&self, thread_callback: TestThreadCallback) { + self.input_thread.register_thread_callback(Box::new(thread_callback)); + } + + pub fn unregister_thread_callback(&self, thread_callback: TestThreadCallback) { + self.input_thread.unregister_thread_callback(Box::new(thread_callback)); + } + + pub fn start_looper(&self) { + self.exit_flag.store(false, Ordering::Relaxed); + let clone = self.clone(); + let join_handle = std::thread::Builder::new() + .name("test_thread".to_string()) + .spawn(move || { + while !clone.exit_flag.load(Ordering::Relaxed) { + clone.loop_once(); + } + }) + .unwrap(); + self.inner().join_handle = Some(join_handle); + // Sleep until the looper thread starts + std::thread::sleep(Duration::from_millis(10)); + } + + pub fn stop_looper(&self) { + self.exit_flag.store(true, Ordering::Relaxed); + { + let mut inner = self.inner(); + if let Some(join_handle) = &inner.join_handle { + join_handle.thread().unpark(); + } + inner.join_handle.take().map(std::thread::JoinHandle::join); + inner.join_handle = None; + } + self.exit_flag.store(false, Ordering::Relaxed); + } + + pub fn move_time_forward(&self, value: i64) { + let _ = self.now.fetch_add(value, Ordering::Relaxed); + self.dispatch_next(); + } + + pub fn dispatch_next(&self) { + if let Some(join_handle) = &self.inner().join_handle { + join_handle.thread().unpark(); + } + // Sleep until the looper thread runs a loop + std::thread::sleep(Duration::from_millis(10)); + } + + fn loop_once(&self) { + self.input_thread.loop_once(self.now.load(Ordering::Relaxed)); + } + + pub fn request_timeout_at_time(&self, when_nanos: i64) { + self.input_thread.request_timeout_at_time(when_nanos); + } + } +} + +#[cfg(test)] +pub mod test_thread_callback { + use crate::input_filter_thread::ThreadCallback; + use std::sync::{Arc, RwLock, RwLockWriteGuard}; + + #[derive(Default)] + struct TestThreadCallbackInner { + is_notify_timeout_called: bool, + } + + #[derive(Default, Clone)] + pub struct TestThreadCallback(Arc<RwLock<TestThreadCallbackInner>>); + + impl TestThreadCallback { + pub fn new() -> Self { + Default::default() + } + + fn inner(&self) -> RwLockWriteGuard<'_, TestThreadCallbackInner> { + self.0.write().unwrap() + } + + pub fn is_notify_timeout_called(&self) -> bool { + self.0.read().unwrap().is_notify_timeout_called + } + } + + impl ThreadCallback for TestThreadCallback { + fn notify_timeout_expired(&self, _when_nanos: i64) { + self.inner().is_notify_timeout_called = true; + } + fn name(&self) -> &str { + "TestThreadCallback" + } + } +} diff --git a/services/inputflinger/rust/lib.rs b/services/inputflinger/rust/lib.rs index fa16898835..da72134065 100644 --- a/services/inputflinger/rust/lib.rs +++ b/services/inputflinger/rust/lib.rs @@ -21,6 +21,8 @@ mod bounce_keys_filter; mod input_filter; +mod input_filter_thread; +mod slow_keys_filter; mod sticky_keys_filter; use crate::input_filter::InputFilter; diff --git a/services/inputflinger/rust/slow_keys_filter.rs b/services/inputflinger/rust/slow_keys_filter.rs new file mode 100644 index 0000000000..01165b57fa --- /dev/null +++ b/services/inputflinger/rust/slow_keys_filter.rs @@ -0,0 +1,382 @@ +/* + * 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. + */ + +//! Slow keys input filter implementation. +//! Slow keys is an accessibility feature to aid users who have physical disabilities, that allows +//! the user to specify the duration for which one must press-and-hold a key before the system +//! accepts the keypress. +use crate::input_filter::Filter; +use crate::input_filter_thread::{InputFilterThread, ThreadCallback}; +use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source; +use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ + DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction, +}; +use log::debug; +use std::collections::HashSet; +use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}; + +#[derive(Debug)] +struct OngoingKeyDown { + scancode: i32, + device_id: i32, + down_time: i64, +} + +struct SlowKeysFilterInner { + next: Box<dyn Filter + Send + Sync>, + slow_key_threshold_ns: i64, + external_devices: HashSet<i32>, + // This tracks KeyEvents that are blocked by Slow keys filter and will be passed through if the + // press duration exceeds the slow keys threshold. + pending_down_events: Vec<KeyEvent>, + // This tracks KeyEvent streams that have press duration greater than the slow keys threshold, + // hence any future ACTION_DOWN (if repeats are handled on HW side) or ACTION_UP are allowed to + // pass through without waiting. + ongoing_down_events: Vec<OngoingKeyDown>, + input_filter_thread: InputFilterThread, +} + +#[derive(Clone)] +pub struct SlowKeysFilter(Arc<RwLock<SlowKeysFilterInner>>); + +impl SlowKeysFilter { + /// Create a new SlowKeysFilter instance. + pub fn new( + next: Box<dyn Filter + Send + Sync>, + slow_key_threshold_ns: i64, + input_filter_thread: InputFilterThread, + ) -> SlowKeysFilter { + let filter = Self(Arc::new(RwLock::new(SlowKeysFilterInner { + next, + slow_key_threshold_ns, + external_devices: HashSet::new(), + pending_down_events: Vec::new(), + ongoing_down_events: Vec::new(), + input_filter_thread: input_filter_thread.clone(), + }))); + input_filter_thread.register_thread_callback(Box::new(filter.clone())); + filter + } + + fn read_inner(&self) -> RwLockReadGuard<'_, SlowKeysFilterInner> { + self.0.read().unwrap() + } + + fn write_inner(&self) -> RwLockWriteGuard<'_, SlowKeysFilterInner> { + self.0.write().unwrap() + } + + fn request_next_callback(&self) { + let slow_filter = &self.read_inner(); + if slow_filter.pending_down_events.is_empty() { + return; + } + if let Some(event) = slow_filter.pending_down_events.iter().min_by_key(|x| x.downTime) { + slow_filter.input_filter_thread.request_timeout_at_time(event.downTime); + } + } +} + +impl Filter for SlowKeysFilter { + fn notify_key(&mut self, event: &KeyEvent) { + { + // acquire write lock + let mut slow_filter = self.write_inner(); + if !(slow_filter.external_devices.contains(&event.deviceId) + && event.source == Source::KEYBOARD) + { + slow_filter.next.notify_key(event); + return; + } + // Pass all events through if key down has already been processed + // Do update the downtime before sending the events through + if let Some(index) = slow_filter + .ongoing_down_events + .iter() + .position(|x| x.device_id == event.deviceId && x.scancode == event.scanCode) + { + let mut new_event = *event; + new_event.downTime = slow_filter.ongoing_down_events[index].down_time; + slow_filter.next.notify_key(&new_event); + if event.action == KeyEventAction::UP { + slow_filter.ongoing_down_events.remove(index); + } + return; + } + match event.action { + KeyEventAction::DOWN => { + if slow_filter + .pending_down_events + .iter() + .any(|x| x.deviceId == event.deviceId && x.scanCode == event.scanCode) + { + debug!("Dropping key down event since another pending down event exists"); + return; + } + let mut pending_event = *event; + pending_event.downTime += slow_filter.slow_key_threshold_ns; + pending_event.eventTime = pending_event.downTime; + slow_filter.pending_down_events.push(pending_event); + } + KeyEventAction::UP => { + debug!("Dropping key up event due to insufficient press duration"); + if let Some(index) = slow_filter + .pending_down_events + .iter() + .position(|x| x.deviceId == event.deviceId && x.scanCode == event.scanCode) + { + slow_filter.pending_down_events.remove(index); + } + } + _ => (), + } + } // release write lock + self.request_next_callback(); + } + + fn notify_devices_changed(&mut self, device_infos: &[DeviceInfo]) { + let mut slow_filter = self.write_inner(); + slow_filter + .pending_down_events + .retain(|event| device_infos.iter().any(|x| event.deviceId == x.deviceId)); + slow_filter + .ongoing_down_events + .retain(|event| device_infos.iter().any(|x| event.device_id == x.deviceId)); + slow_filter.external_devices.clear(); + for device_info in device_infos { + if device_info.external { + slow_filter.external_devices.insert(device_info.deviceId); + } + } + slow_filter.next.notify_devices_changed(device_infos); + } + + fn destroy(&mut self) { + let mut slow_filter = self.write_inner(); + slow_filter.input_filter_thread.unregister_thread_callback(Box::new(self.clone())); + slow_filter.next.destroy(); + } +} + +impl ThreadCallback for SlowKeysFilter { + fn notify_timeout_expired(&self, when_nanos: i64) { + { + // acquire write lock + let slow_filter = &mut self.write_inner(); + for event in slow_filter.pending_down_events.clone() { + if event.downTime <= when_nanos { + slow_filter.next.notify_key(&event); + slow_filter.ongoing_down_events.push(OngoingKeyDown { + scancode: event.scanCode, + device_id: event.deviceId, + down_time: event.downTime, + }); + } + } + slow_filter.pending_down_events.retain(|event| event.downTime > when_nanos); + } // release write lock + self.request_next_callback(); + } + + fn name(&self) -> &str { + "slow_keys_filter" + } +} + +#[cfg(test)] +mod tests { + use crate::input_filter::{test_callbacks::TestCallbacks, test_filter::TestFilter, Filter}; + use crate::input_filter_thread::test_thread::TestThread; + use crate::slow_keys_filter::SlowKeysFilter; + use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source; + use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ + DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction, + }; + + static BASE_KEY_EVENT: KeyEvent = KeyEvent { + id: 1, + deviceId: 1, + downTime: 0, + readTime: 0, + eventTime: 0, + source: Source::KEYBOARD, + displayId: 0, + policyFlags: 0, + action: KeyEventAction::DOWN, + flags: 0, + keyCode: 1, + scanCode: 0, + metaState: 0, + }; + + #[test] + fn test_is_notify_key_for_internal_keyboard_not_blocked() { + let test_callbacks = TestCallbacks::new(); + let test_thread = TestThread::new(test_callbacks.clone()); + let next = TestFilter::new(); + let mut filter = setup_filter_with_internal_device( + Box::new(next.clone()), + test_thread.clone(), + 1, /* device_id */ + 100, /* threshold */ + ); + test_thread.start_looper(); + + let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert_eq!(next.last_event().unwrap(), event); + } + + #[test] + fn test_is_notify_key_for_external_stylus_not_blocked() { + let test_callbacks = TestCallbacks::new(); + let test_thread = TestThread::new(test_callbacks.clone()); + let next = TestFilter::new(); + let mut filter = setup_filter_with_external_device( + Box::new(next.clone()), + test_thread.clone(), + 1, /* device_id */ + 100, /* threshold */ + ); + test_thread.start_looper(); + + let event = + KeyEvent { action: KeyEventAction::DOWN, source: Source::STYLUS, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert_eq!(next.last_event().unwrap(), event); + } + + #[test] + fn test_notify_key_for_external_keyboard_when_key_pressed_for_threshold_time() { + let test_callbacks = TestCallbacks::new(); + let test_thread = TestThread::new(test_callbacks.clone()); + let next = TestFilter::new(); + let mut filter = setup_filter_with_external_device( + Box::new(next.clone()), + test_thread.clone(), + 1, /* device_id */ + 100, /* threshold */ + ); + test_thread.start_looper(); + + filter.notify_key(&KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT }); + assert!(next.last_event().is_none()); + test_thread.dispatch_next(); + + test_thread.move_time_forward(100); + + test_thread.stop_looper(); + assert_eq!( + next.last_event().unwrap(), + KeyEvent { + action: KeyEventAction::DOWN, + downTime: 100, + eventTime: 100, + ..BASE_KEY_EVENT + } + ); + } + + #[test] + fn test_notify_key_for_external_keyboard_when_key_not_pressed_for_threshold_time() { + let test_callbacks = TestCallbacks::new(); + let test_thread = TestThread::new(test_callbacks.clone()); + let next = TestFilter::new(); + let mut filter = setup_filter_with_external_device( + Box::new(next.clone()), + test_thread.clone(), + 1, /* device_id */ + 100, /* threshold */ + ); + test_thread.start_looper(); + + filter.notify_key(&KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT }); + test_thread.dispatch_next(); + + test_thread.move_time_forward(10); + + filter.notify_key(&KeyEvent { action: KeyEventAction::UP, ..BASE_KEY_EVENT }); + test_thread.dispatch_next(); + + test_thread.stop_looper(); + assert!(next.last_event().is_none()); + } + + #[test] + fn test_notify_key_for_external_keyboard_when_device_removed_before_threshold_time() { + let test_callbacks = TestCallbacks::new(); + let test_thread = TestThread::new(test_callbacks.clone()); + let next = TestFilter::new(); + let mut filter = setup_filter_with_external_device( + Box::new(next.clone()), + test_thread.clone(), + 1, /* device_id */ + 100, /* threshold */ + ); + test_thread.start_looper(); + + filter.notify_key(&KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT }); + assert!(next.last_event().is_none()); + test_thread.dispatch_next(); + + filter.notify_devices_changed(&[]); + test_thread.dispatch_next(); + + test_thread.move_time_forward(100); + + test_thread.stop_looper(); + assert!(next.last_event().is_none()); + } + + fn setup_filter_with_external_device( + next: Box<dyn Filter + Send + Sync>, + test_thread: TestThread, + device_id: i32, + threshold: i64, + ) -> SlowKeysFilter { + setup_filter_with_devices( + next, + test_thread, + &[DeviceInfo { deviceId: device_id, external: true }], + threshold, + ) + } + + fn setup_filter_with_internal_device( + next: Box<dyn Filter + Send + Sync>, + test_thread: TestThread, + device_id: i32, + threshold: i64, + ) -> SlowKeysFilter { + setup_filter_with_devices( + next, + test_thread, + &[DeviceInfo { deviceId: device_id, external: false }], + threshold, + ) + } + + fn setup_filter_with_devices( + next: Box<dyn Filter + Send + Sync>, + test_thread: TestThread, + devices: &[DeviceInfo], + threshold: i64, + ) -> SlowKeysFilter { + let mut filter = SlowKeysFilter::new(next, threshold, test_thread.get_input_thread()); + filter.notify_devices_changed(devices); + filter + } +} diff --git a/services/inputflinger/rust/sticky_keys_filter.rs b/services/inputflinger/rust/sticky_keys_filter.rs index da581b82bf..6c2277c813 100644 --- a/services/inputflinger/rust/sticky_keys_filter.rs +++ b/services/inputflinger/rust/sticky_keys_filter.rs @@ -142,6 +142,10 @@ impl Filter for StickyKeysFilter { } self.next.notify_devices_changed(device_infos); } + + fn destroy(&mut self) { + self.next.destroy(); + } } fn is_modifier_key(keycode: i32) -> bool { diff --git a/services/inputflinger/tests/CursorInputMapper_test.cpp b/services/inputflinger/tests/CursorInputMapper_test.cpp index e104543523..7b793d8e86 100644 --- a/services/inputflinger/tests/CursorInputMapper_test.cpp +++ b/services/inputflinger/tests/CursorInputMapper_test.cpp @@ -1324,6 +1324,42 @@ TEST_F(CursorInputMapperUnitTestWithNewBallistics, PointerCaptureDisablesVelocit ASSERT_EQ(20, relY2); } +TEST_F(CursorInputMapperUnitTestWithNewBallistics, ConfigureAccelerationWithAssociatedViewport) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation0); + mReaderConfiguration.setDisplayViewports({primaryViewport}); + createDevice(); + ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, primaryViewport); + mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration); + + std::list<NotifyArgs> args; + + // Verify that acceleration is being applied by default by checking that the movement is scaled. + args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(HOVER_MOVE), WithDisplayId(DISPLAY_ID))))); + const auto& coords = get<NotifyMotionArgs>(args.back()).pointerCoords[0]; + ASSERT_GT(coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X), 10.f); + ASSERT_GT(coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y), 20.f); + + // Disable acceleration for the display, and verify that acceleration is no longer applied. + mReaderConfiguration.displaysWithMousePointerAccelerationDisabled.emplace(DISPLAY_ID); + args += mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, + InputReaderConfiguration::Change::POINTER_SPEED); + args.clear(); + + args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(HOVER_MOVE), WithDisplayId(DISPLAY_ID), + WithRelativeMotion(10, 20))))); +} + namespace { // Minimum timestamp separation between subsequent input events from a Bluetooth device. diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index b6ae0b3d69..f1f4a61e73 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -156,6 +156,20 @@ class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface { sp<IBinder> token{}; gui::Pid pid{gui::Pid::INVALID}; }; + /* Stores data about a user-activity-poke event from the dispatcher. */ + struct UserActivityPokeEvent { + nsecs_t eventTime; + int32_t eventType; + int32_t displayId; + + bool operator==(const UserActivityPokeEvent& rhs) const = default; + + friend std::ostream& operator<<(std::ostream& os, const UserActivityPokeEvent& ev) { + os << "UserActivityPokeEvent[time=" << ev.eventTime << ", eventType=" << ev.eventType + << ", displayId=" << ev.displayId << "]"; + return os; + } + }; public: FakeInputDispatcherPolicy() = default; @@ -351,14 +365,36 @@ public: void setStaleEventTimeout(std::chrono::nanoseconds timeout) { mStaleEventTimeout = timeout; } - void assertUserActivityPoked() { - std::scoped_lock lock(mLock); - ASSERT_TRUE(mPokedUserActivity) << "Expected user activity to have been poked"; + void assertUserActivityNotPoked() { + std::unique_lock lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + + std::optional<UserActivityPokeEvent> pokeEvent = + getItemFromStorageLockedInterruptible(500ms, mUserActivityPokeEvents, lock, + mNotifyUserActivity); + + ASSERT_FALSE(pokeEvent) << "Expected user activity not to have been poked"; } - void assertUserActivityNotPoked() { - std::scoped_lock lock(mLock); - ASSERT_FALSE(mPokedUserActivity) << "Expected user activity not to have been poked"; + /** + * Asserts that a user activity poke has happened. The earliest recorded poke event will be + * cleared after this call. + * + * If an expected UserActivityPokeEvent is provided, asserts that the given event is the + * earliest recorded poke event. + */ + void assertUserActivityPoked(std::optional<UserActivityPokeEvent> expectedPokeEvent = {}) { + std::unique_lock lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + + std::optional<UserActivityPokeEvent> pokeEvent = + getItemFromStorageLockedInterruptible(500ms, mUserActivityPokeEvents, lock, + mNotifyUserActivity); + ASSERT_TRUE(pokeEvent) << "Expected a user poke event"; + + if (expectedPokeEvent) { + ASSERT_EQ(expectedPokeEvent, *pokeEvent); + } } void assertNotifyDeviceInteractionWasCalled(int32_t deviceId, std::set<gui::Uid> uids) { @@ -414,7 +450,9 @@ private: sp<IBinder> mDropTargetWindowToken GUARDED_BY(mLock); bool mNotifyDropWindowWasCalled GUARDED_BY(mLock) = false; - bool mPokedUserActivity GUARDED_BY(mLock) = false; + + std::condition_variable mNotifyUserActivity; + std::queue<UserActivityPokeEvent> mUserActivityPokeEvents; std::chrono::milliseconds mInterceptKeyTimeout = 0ms; @@ -576,9 +614,10 @@ private: NotifySwitchArgs(InputEvent::nextId(), when, policyFlags, switchValues, switchMask); } - void pokeUserActivity(nsecs_t, int32_t, int32_t) override { + void pokeUserActivity(nsecs_t eventTime, int32_t eventType, int32_t displayId) override { std::scoped_lock lock(mLock); - mPokedUserActivity = true; + mNotifyUserActivity.notify_all(); + mUserActivityPokeEvents.push({eventTime, eventType, displayId}); } bool isStaleEvent(nsecs_t currentTime, nsecs_t eventTime) override { @@ -7690,6 +7729,130 @@ TEST_F(InputFilterInjectionPolicyTest, RegularInjectedEvents_ReceiveVirtualDevic /*resolvedDeviceId=*/VIRTUAL_KEYBOARD_ID, /*flags=*/0); } +class InputDispatcherUserActivityPokeTests : public InputDispatcherTest { +protected: + virtual void SetUp() override { + InputDispatcherTest::SetUp(); + + std::shared_ptr<FakeApplicationHandle> application = + std::make_shared<FakeApplicationHandle>(); + application->setDispatchingTimeout(100ms); + mWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "TestWindow", + ADISPLAY_ID_DEFAULT); + mWindow->setFrame(Rect(0, 0, 100, 100)); + mWindow->setDispatchingTimeout(100ms); + mWindow->setFocusable(true); + + // Set focused application. + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); + + mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0}); + setFocusedWindow(mWindow); + mWindow->consumeFocusEvent(true); + } + + void notifyAndConsumeMotion(int32_t action, uint32_t source, int32_t displayId, + nsecs_t eventTime) { + mDispatcher->notifyMotion(MotionArgsBuilder(action, source) + .displayId(displayId) + .eventTime(eventTime) + .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) + .build()); + mWindow->consumeMotionEvent(WithMotionAction(action)); + } + +private: + sp<FakeWindowHandle> mWindow; +}; + +TEST_F_WITH_FLAGS( + InputDispatcherUserActivityPokeTests, MinPokeTimeObserved, + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags, + rate_limit_user_activity_poke_in_dispatcher))) { + mDispatcher->setMinTimeBetweenUserActivityPokes(50ms); + + // First event of type TOUCH. Should poke. + notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + milliseconds_to_nanoseconds(50)); + mFakePolicy->assertUserActivityPoked( + {{milliseconds_to_nanoseconds(50), USER_ACTIVITY_EVENT_TOUCH, ADISPLAY_ID_DEFAULT}}); + + // 80ns > 50ns has passed since previous TOUCH event. Should poke. + notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + milliseconds_to_nanoseconds(130)); + mFakePolicy->assertUserActivityPoked( + {{milliseconds_to_nanoseconds(130), USER_ACTIVITY_EVENT_TOUCH, ADISPLAY_ID_DEFAULT}}); + + // First event of type OTHER. Should poke (despite being within 50ns of previous TOUCH event). + notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER, ADISPLAY_ID_DEFAULT, + milliseconds_to_nanoseconds(135)); + mFakePolicy->assertUserActivityPoked( + {{milliseconds_to_nanoseconds(135), USER_ACTIVITY_EVENT_OTHER, ADISPLAY_ID_DEFAULT}}); + + // Within 50ns of previous TOUCH event. Should NOT poke. + notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + milliseconds_to_nanoseconds(140)); + mFakePolicy->assertUserActivityNotPoked(); + + // Within 50ns of previous OTHER event. Should NOT poke. + notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER, ADISPLAY_ID_DEFAULT, + milliseconds_to_nanoseconds(150)); + mFakePolicy->assertUserActivityNotPoked(); + + // Within 50ns of previous TOUCH event (which was at time 130). Should NOT poke. + // Note that STYLUS is mapped to TOUCH user activity, since it's a pointer-type source. + notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_STYLUS, ADISPLAY_ID_DEFAULT, + milliseconds_to_nanoseconds(160)); + mFakePolicy->assertUserActivityNotPoked(); + + // 65ns > 50ns has passed since previous OTHER event. Should poke. + notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER, ADISPLAY_ID_DEFAULT, + milliseconds_to_nanoseconds(200)); + mFakePolicy->assertUserActivityPoked( + {{milliseconds_to_nanoseconds(200), USER_ACTIVITY_EVENT_OTHER, ADISPLAY_ID_DEFAULT}}); + + // 170ns > 50ns has passed since previous TOUCH event. Should poke. + notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_STYLUS, ADISPLAY_ID_DEFAULT, + milliseconds_to_nanoseconds(300)); + mFakePolicy->assertUserActivityPoked( + {{milliseconds_to_nanoseconds(300), USER_ACTIVITY_EVENT_TOUCH, ADISPLAY_ID_DEFAULT}}); + + // Assert that there's no more user activity poke event. + mFakePolicy->assertUserActivityNotPoked(); +} + +TEST_F_WITH_FLAGS( + InputDispatcherUserActivityPokeTests, DefaultMinPokeTimeOf100MsUsed, + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags, + rate_limit_user_activity_poke_in_dispatcher))) { + notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + milliseconds_to_nanoseconds(200)); + mFakePolicy->assertUserActivityPoked( + {{milliseconds_to_nanoseconds(200), USER_ACTIVITY_EVENT_TOUCH, ADISPLAY_ID_DEFAULT}}); + + notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + milliseconds_to_nanoseconds(280)); + mFakePolicy->assertUserActivityNotPoked(); + + notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + milliseconds_to_nanoseconds(340)); + mFakePolicy->assertUserActivityPoked( + {{milliseconds_to_nanoseconds(340), USER_ACTIVITY_EVENT_TOUCH, ADISPLAY_ID_DEFAULT}}); +} + +TEST_F_WITH_FLAGS( + InputDispatcherUserActivityPokeTests, ZeroMinPokeTimeDisablesRateLimiting, + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags, + rate_limit_user_activity_poke_in_dispatcher))) { + mDispatcher->setMinTimeBetweenUserActivityPokes(0ms); + + notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, 20); + mFakePolicy->assertUserActivityPoked(); + + notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, 30); + mFakePolicy->assertUserActivityPoked(); +} + class InputDispatcherOnPointerDownOutsideFocus : public InputDispatcherTest { virtual void SetUp() override { InputDispatcherTest::SetUp(); diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp index 1ac043c679..e9e50619ee 100644 --- a/services/inputflinger/tests/PointerChoreographer_test.cpp +++ b/services/inputflinger/tests/PointerChoreographer_test.cpp @@ -1557,4 +1557,122 @@ TEST_F(PointerChoreographerTest, SetsPointerIconForMouseAndStylus) { mousePc->assertPointerIconNotSet(); } +TEST_F(PointerChoreographerTest, SetPointerIconVisibilityHidesPointerOnDisplay) { + // Make sure there are two PointerControllers on different displays. + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID})); + mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE), + generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, ANOTHER_DISPLAY_ID)}}); + auto firstMousePc = assertPointerControllerCreated(ControllerType::MOUSE); + ASSERT_EQ(DISPLAY_ID, firstMousePc->getDisplayId()); + auto secondMousePc = assertPointerControllerCreated(ControllerType::MOUSE); + ASSERT_EQ(ANOTHER_DISPLAY_ID, secondMousePc->getDisplayId()); + + // Both pointers should be visible. + ASSERT_TRUE(firstMousePc->isPointerShown()); + ASSERT_TRUE(secondMousePc->isPointerShown()); + + // Hide the icon on the second display. + mChoreographer.setPointerIconVisibility(ANOTHER_DISPLAY_ID, false); + ASSERT_TRUE(firstMousePc->isPointerShown()); + ASSERT_FALSE(secondMousePc->isPointerShown()); + + // Move and set pointer icons for both mice. The second pointer should still be hidden. + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE) + .pointer(MOUSE_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE) + .pointer(MOUSE_POINTER) + .deviceId(SECOND_DEVICE_ID) + .displayId(ANOTHER_DISPLAY_ID) + .build()); + ASSERT_TRUE(mChoreographer.setPointerIcon(PointerIconStyle::TYPE_TEXT, DISPLAY_ID, DEVICE_ID)); + ASSERT_TRUE(mChoreographer.setPointerIcon(PointerIconStyle::TYPE_TEXT, ANOTHER_DISPLAY_ID, + SECOND_DEVICE_ID)); + firstMousePc->assertPointerIconSet(PointerIconStyle::TYPE_TEXT); + secondMousePc->assertPointerIconSet(PointerIconStyle::TYPE_TEXT); + ASSERT_TRUE(firstMousePc->isPointerShown()); + ASSERT_FALSE(secondMousePc->isPointerShown()); + + // Allow the icon to be visible on the second display, and move the mouse. + mChoreographer.setPointerIconVisibility(ANOTHER_DISPLAY_ID, true); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE) + .pointer(MOUSE_POINTER) + .deviceId(SECOND_DEVICE_ID) + .displayId(ANOTHER_DISPLAY_ID) + .build()); + ASSERT_TRUE(firstMousePc->isPointerShown()); + ASSERT_TRUE(secondMousePc->isPointerShown()); +} + +TEST_F(PointerChoreographerTest, SetPointerIconVisibilityHidesPointerWhenDeviceConnected) { + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + + // Hide the pointer on the display, and then connect the mouse. + mChoreographer.setPointerIconVisibility(DISPLAY_ID, false); + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}}); + auto mousePc = assertPointerControllerCreated(ControllerType::MOUSE); + ASSERT_EQ(DISPLAY_ID, mousePc->getDisplayId()); + + // The pointer should not be visible. + ASSERT_FALSE(mousePc->isPointerShown()); +} + +TEST_F(PointerChoreographerTest, SetPointerIconVisibilityHidesPointerForTouchpad) { + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + + // Hide the pointer on the display. + mChoreographer.setPointerIconVisibility(DISPLAY_ID, false); + + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, + ADISPLAY_ID_NONE)}}); + auto touchpadPc = assertPointerControllerCreated(ControllerType::MOUSE); + ASSERT_EQ(DISPLAY_ID, touchpadPc->getDisplayId()); + + mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, + AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD) + .pointer(TOUCHPAD_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + + // The pointer should not be visible. + ASSERT_FALSE(touchpadPc->isPointerShown()); +} + +TEST_F(PointerChoreographerTest, SetPointerIconVisibilityHidesPointerForStylus) { + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + mChoreographer.setStylusPointerIconEnabled(true); + + // Hide the pointer on the display. + mChoreographer.setPointerIconVisibility(DISPLAY_ID, false); + + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}}); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS) + .pointer(STYLUS_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + ASSERT_TRUE(mChoreographer.setPointerIcon(PointerIconStyle::TYPE_TEXT, DISPLAY_ID, DEVICE_ID)); + auto pc = assertPointerControllerCreated(ControllerType::STYLUS); + pc->assertPointerIconSet(PointerIconStyle::TYPE_TEXT); + + // The pointer should not be visible. + ASSERT_FALSE(pc->isPointerShown()); +} + } // namespace android diff --git a/services/powermanager/PowerHalController.cpp b/services/powermanager/PowerHalController.cpp index 9a23c848c9..bc178bce8f 100644 --- a/services/powermanager/PowerHalController.cpp +++ b/services/powermanager/PowerHalController.cpp @@ -80,7 +80,7 @@ std::shared_ptr<HalWrapper> PowerHalController::initHal() { // Check if a call to Power HAL function failed; if so, log the failure and // invalidate the current Power HAL handle. template <typename T> -HalResult<T> PowerHalController::processHalResult(HalResult<T> result, const char* fnName) { +HalResult<T> PowerHalController::processHalResult(HalResult<T>&& result, const char* fnName) { if (result.isFailed()) { ALOGE("%s failed: %s", fnName, result.errorMessage()); std::lock_guard<std::mutex> lock(mConnectedHalMutex); @@ -88,21 +88,19 @@ HalResult<T> PowerHalController::processHalResult(HalResult<T> result, const cha mConnectedHal = nullptr; mHalConnector->reset(); } - return result; + return std::move(result); } HalResult<void> PowerHalController::setBoost(aidl::android::hardware::power::Boost boost, int32_t durationMs) { std::shared_ptr<HalWrapper> handle = initHal(); - auto result = handle->setBoost(boost, durationMs); - return processHalResult(result, "setBoost"); + return processHalResult(handle->setBoost(boost, durationMs), "setBoost"); } HalResult<void> PowerHalController::setMode(aidl::android::hardware::power::Mode mode, bool enabled) { std::shared_ptr<HalWrapper> handle = initHal(); - auto result = handle->setMode(mode, enabled); - return processHalResult(result, "setMode"); + return processHalResult(handle->setMode(mode, enabled), "setMode"); } HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> @@ -110,14 +108,35 @@ PowerHalController::createHintSession(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) { std::shared_ptr<HalWrapper> handle = initHal(); - auto result = handle->createHintSession(tgid, uid, threadIds, durationNanos); - return processHalResult(result, "createHintSession"); + return processHalResult(handle->createHintSession(tgid, uid, threadIds, durationNanos), + "createHintSession"); +} + +HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> +PowerHalController::createHintSessionWithConfig( + int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos, + aidl::android::hardware::power::SessionTag tag, + aidl::android::hardware::power::SessionConfig* config) { + std::shared_ptr<HalWrapper> handle = initHal(); + return processHalResult(handle->createHintSessionWithConfig(tgid, uid, threadIds, durationNanos, + tag, config), + "createHintSessionWithConfig"); } HalResult<int64_t> PowerHalController::getHintSessionPreferredRate() { std::shared_ptr<HalWrapper> handle = initHal(); - auto result = handle->getHintSessionPreferredRate(); - return processHalResult(result, "getHintSessionPreferredRate"); + return processHalResult(handle->getHintSessionPreferredRate(), "getHintSessionPreferredRate"); +} + +HalResult<aidl::android::hardware::power::ChannelConfig> PowerHalController::getSessionChannel( + int tgid, int uid) { + std::shared_ptr<HalWrapper> handle = initHal(); + return processHalResult(handle->getSessionChannel(tgid, uid), "getSessionChannel"); +} + +HalResult<void> PowerHalController::closeSessionChannel(int tgid, int uid) { + std::shared_ptr<HalWrapper> handle = initHal(); + return processHalResult(handle->closeSessionChannel(tgid, uid), "closeSessionChannel"); } } // namespace power diff --git a/services/powermanager/PowerHalWrapper.cpp b/services/powermanager/PowerHalWrapper.cpp index 76afbfc646..1009100cc2 100644 --- a/services/powermanager/PowerHalWrapper.cpp +++ b/services/powermanager/PowerHalWrapper.cpp @@ -42,37 +42,58 @@ inline HalResult<void> toHalResult(const ndk::ScopedAStatus& result) { // ------------------------------------------------------------------------------------------------- HalResult<void> EmptyHalWrapper::setBoost(Aidl::Boost boost, int32_t durationMs) { - ALOGV("Skipped setBoost %s with duration %dms because Power HAL not available", - toString(boost).c_str(), durationMs); + ALOGV("Skipped setBoost %s with duration %dms because %s", toString(boost).c_str(), durationMs, + getUnsupportedMessage()); return HalResult<void>::unsupported(); } HalResult<void> EmptyHalWrapper::setMode(Aidl::Mode mode, bool enabled) { - ALOGV("Skipped setMode %s to %s because Power HAL not available", toString(mode).c_str(), - enabled ? "true" : "false"); + ALOGV("Skipped setMode %s to %s because %s", toString(mode).c_str(), enabled ? "true" : "false", + getUnsupportedMessage()); return HalResult<void>::unsupported(); } HalResult<std::shared_ptr<Aidl::IPowerHintSession>> EmptyHalWrapper::createHintSession( int32_t, int32_t, const std::vector<int32_t>& threadIds, int64_t) { - ALOGV("Skipped createHintSession(task num=%zu) because Power HAL not available", - threadIds.size()); + ALOGV("Skipped createHintSession(task num=%zu) because %s", threadIds.size(), + getUnsupportedMessage()); + return HalResult<std::shared_ptr<Aidl::IPowerHintSession>>::unsupported(); +} + +HalResult<std::shared_ptr<Aidl::IPowerHintSession>> EmptyHalWrapper::createHintSessionWithConfig( + int32_t, int32_t, const std::vector<int32_t>& threadIds, int64_t, Aidl::SessionTag, + Aidl::SessionConfig*) { + ALOGV("Skipped createHintSessionWithConfig(task num=%zu) because %s", threadIds.size(), + getUnsupportedMessage()); return HalResult<std::shared_ptr<Aidl::IPowerHintSession>>::unsupported(); } HalResult<int64_t> EmptyHalWrapper::getHintSessionPreferredRate() { - ALOGV("Skipped getHintSessionPreferredRate because Power HAL not available"); + ALOGV("Skipped getHintSessionPreferredRate because %s", getUnsupportedMessage()); return HalResult<int64_t>::unsupported(); } +HalResult<Aidl::ChannelConfig> EmptyHalWrapper::getSessionChannel(int, int) { + ALOGV("Skipped getSessionChannel because %s", getUnsupportedMessage()); + return HalResult<Aidl::ChannelConfig>::unsupported(); +} + +HalResult<void> EmptyHalWrapper::closeSessionChannel(int, int) { + ALOGV("Skipped closeSessionChannel because %s", getUnsupportedMessage()); + return HalResult<void>::unsupported(); +} + +const char* EmptyHalWrapper::getUnsupportedMessage() { + return "Power HAL is not supported"; +} + // ------------------------------------------------------------------------------------------------- HalResult<void> HidlHalWrapperV1_0::setBoost(Aidl::Boost boost, int32_t durationMs) { if (boost == Aidl::Boost::INTERACTION) { return sendPowerHint(V1_3::PowerHint::INTERACTION, durationMs); } else { - ALOGV("Skipped setBoost %s because Power HAL AIDL not available", toString(boost).c_str()); - return HalResult<void>::unsupported(); + return EmptyHalWrapper::setBoost(boost, durationMs); } } @@ -92,9 +113,7 @@ HalResult<void> HidlHalWrapperV1_0::setMode(Aidl::Mode mode, bool enabled) { case Aidl::Mode::DOUBLE_TAP_TO_WAKE: return setFeature(V1_0::Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE, enabled); default: - ALOGV("Skipped setMode %s because Power HAL AIDL not available", - toString(mode).c_str()); - return HalResult<void>::unsupported(); + return EmptyHalWrapper::setMode(mode, enabled); } } @@ -113,16 +132,8 @@ HalResult<void> HidlHalWrapperV1_0::setFeature(V1_0::Feature feature, bool enabl return HalResult<void>::fromReturn(ret); } -HalResult<std::shared_ptr<Aidl::IPowerHintSession>> HidlHalWrapperV1_0::createHintSession( - int32_t, int32_t, const std::vector<int32_t>& threadIds, int64_t) { - ALOGV("Skipped createHintSession(task num=%zu) because Power HAL not available", - threadIds.size()); - return HalResult<std::shared_ptr<Aidl::IPowerHintSession>>::unsupported(); -} - -HalResult<int64_t> HidlHalWrapperV1_0::getHintSessionPreferredRate() { - ALOGV("Skipped getHintSessionPreferredRate because Power HAL not available"); - return HalResult<int64_t>::unsupported(); +const char* HidlHalWrapperV1_0::getUnsupportedMessage() { + return "Power HAL AIDL is not supported"; } // ------------------------------------------------------------------------------------------------- @@ -191,7 +202,7 @@ HalResult<void> AidlHalWrapper::setBoost(Aidl::Boost boost, int32_t durationMs) // Quick return if boost is not supported by HAL if (idx >= mBoostSupportedArray.size() || mBoostSupportedArray[idx] == HalSupport::OFF) { - ALOGV("Skipped setBoost %s because Power HAL doesn't support it", toString(boost).c_str()); + ALOGV("Skipped setBoost %s because %s", toString(boost).c_str(), getUnsupportedMessage()); return HalResult<void>::unsupported(); } @@ -207,8 +218,8 @@ HalResult<void> AidlHalWrapper::setBoost(Aidl::Boost boost, int32_t durationMs) mBoostSupportedArray[idx] = isSupported ? HalSupport::ON : HalSupport::OFF; if (!isSupported) { - ALOGV("Skipped setBoost %s because Power HAL doesn't support it", - toString(boost).c_str()); + ALOGV("Skipped setBoost %s because %s", toString(boost).c_str(), + getUnsupportedMessage()); return HalResult<void>::unsupported(); } } @@ -223,7 +234,7 @@ HalResult<void> AidlHalWrapper::setMode(Aidl::Mode mode, bool enabled) { // Quick return if mode is not supported by HAL if (idx >= mModeSupportedArray.size() || mModeSupportedArray[idx] == HalSupport::OFF) { - ALOGV("Skipped setMode %s because Power HAL doesn't support it", toString(mode).c_str()); + ALOGV("Skipped setMode %s because %s", toString(mode).c_str(), getUnsupportedMessage()); return HalResult<void>::unsupported(); } @@ -236,8 +247,7 @@ HalResult<void> AidlHalWrapper::setMode(Aidl::Mode mode, bool enabled) { mModeSupportedArray[idx] = isSupported ? HalSupport::ON : HalSupport::OFF; if (!isSupported) { - ALOGV("Skipped setMode %s because Power HAL doesn't support it", - toString(mode).c_str()); + ALOGV("Skipped setMode %s because %s", toString(mode).c_str(), getUnsupportedMessage()); return HalResult<void>::unsupported(); } } @@ -251,7 +261,17 @@ HalResult<std::shared_ptr<Aidl::IPowerHintSession>> AidlHalWrapper::createHintSe std::shared_ptr<Aidl::IPowerHintSession> appSession; return HalResult<std::shared_ptr<Aidl::IPowerHintSession>>:: fromStatus(mHandle->createHintSession(tgid, uid, threadIds, durationNanos, &appSession), - appSession); + std::move(appSession)); +} + +HalResult<std::shared_ptr<Aidl::IPowerHintSession>> AidlHalWrapper::createHintSessionWithConfig( + int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos, + Aidl::SessionTag tag, Aidl::SessionConfig* config) { + std::shared_ptr<Aidl::IPowerHintSession> appSession; + return HalResult<std::shared_ptr<Aidl::IPowerHintSession>>:: + fromStatus(mHandle->createHintSessionWithConfig(tgid, uid, threadIds, durationNanos, + tag, config, &appSession), + std::move(appSession)); } HalResult<int64_t> AidlHalWrapper::getHintSessionPreferredRate() { @@ -260,6 +280,20 @@ HalResult<int64_t> AidlHalWrapper::getHintSessionPreferredRate() { return HalResult<int64_t>::fromStatus(result, rate); } +HalResult<Aidl::ChannelConfig> AidlHalWrapper::getSessionChannel(int tgid, int uid) { + Aidl::ChannelConfig config; + auto result = mHandle->getSessionChannel(tgid, uid, &config); + return HalResult<Aidl::ChannelConfig>::fromStatus(result, std::move(config)); +} + +HalResult<void> AidlHalWrapper::closeSessionChannel(int tgid, int uid) { + return toHalResult(mHandle->closeSessionChannel(tgid, uid)); +} + +const char* AidlHalWrapper::getUnsupportedMessage() { + return "Power HAL doesn't support it"; +} + // ------------------------------------------------------------------------------------------------- } // namespace power diff --git a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp index 3d2cf293ed..a7202969ad 100644 --- a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp +++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp @@ -256,6 +256,23 @@ TEST_F(PowerHalWrapperAidlTest, TestCreateHintSessionSuccessful) { ASSERT_TRUE(result.isOk()); } +TEST_F(PowerHalWrapperAidlTest, TestCreateHintSessionWithConfigSuccessful) { + std::vector<int> threadIds{gettid()}; + int32_t tgid = 999; + int32_t uid = 1001; + int64_t durationNanos = 16666666L; + SessionTag tag = SessionTag::OTHER; + SessionConfig out; + EXPECT_CALL(*mMockHal.get(), + createHintSessionWithConfig(Eq(tgid), Eq(uid), Eq(threadIds), Eq(durationNanos), + Eq(tag), _, _)) + .Times(Exactly(1)) + .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok()))); + auto result = + mWrapper->createHintSessionWithConfig(tgid, uid, threadIds, durationNanos, tag, &out); + ASSERT_TRUE(result.isOk()); +} + TEST_F(PowerHalWrapperAidlTest, TestCreateHintSessionFailed) { int32_t tgid = 999; int32_t uid = 1001; @@ -279,3 +296,18 @@ TEST_F(PowerHalWrapperAidlTest, TestGetHintSessionPreferredRate) { int64_t rate = result.value(); ASSERT_GE(0, rate); } + +TEST_F(PowerHalWrapperAidlTest, TestSessionChannel) { + int32_t tgid = 999; + int32_t uid = 1001; + EXPECT_CALL(*mMockHal.get(), getSessionChannel(Eq(tgid), Eq(uid), _)) + .Times(Exactly(1)) + .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok()))); + EXPECT_CALL(*mMockHal.get(), closeSessionChannel(Eq(tgid), Eq(uid))) + .Times(Exactly(1)) + .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok()))); + auto createResult = mWrapper->getSessionChannel(tgid, uid); + ASSERT_TRUE(createResult.isOk()); + auto closeResult = mWrapper->closeSessionChannel(tgid, uid); + ASSERT_TRUE(closeResult.isOk()); +} diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h index c71c517d94..39748b8417 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h @@ -60,7 +60,7 @@ public: virtual void createClientCompositionCache(uint32_t cacheSize) = 0; // Sends the brightness setting to HWC - virtual void applyDisplayBrightness(const bool applyImmediately) = 0; + virtual void applyDisplayBrightness(bool applyImmediately) = 0; protected: ~Display() = default; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h index c53b46140b..2dc9a1a49a 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h @@ -73,7 +73,7 @@ public: const compositionengine::DisplayColorProfileCreationArgs&) override; void createRenderSurface(const compositionengine::RenderSurfaceCreationArgs&) override; void createClientCompositionCache(uint32_t cacheSize) override; - void applyDisplayBrightness(const bool applyImmediately) override; + void applyDisplayBrightness(bool applyImmediately) override; void setSecure(bool secure) override; // Internal helpers used by chooseCompositionStrategy() diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp index d907bf538d..6428d089a7 100644 --- a/services/surfaceflinger/CompositionEngine/src/Display.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp @@ -204,13 +204,12 @@ void Display::setReleasedLayers(const compositionengine::CompositionRefreshArgs& setReleasedLayers(std::move(releasedLayers)); } -void Display::applyDisplayBrightness(const bool applyImmediately) { - auto& hwc = getCompositionEngine().getHwComposer(); - const auto halDisplayId = HalDisplayId::tryCast(*getDisplayId()); - if (const auto physicalDisplayId = PhysicalDisplayId::tryCast(*halDisplayId); - physicalDisplayId && getState().displayBrightness) { +void Display::applyDisplayBrightness(bool applyImmediately) { + if (const auto displayId = ftl::Optional(getDisplayId()).and_then(PhysicalDisplayId::tryCast); + displayId && getState().displayBrightness) { + auto& hwc = getCompositionEngine().getHwComposer(); const status_t result = - hwc.setDisplayBrightness(*physicalDisplayId, *getState().displayBrightness, + hwc.setDisplayBrightness(*displayId, *getState().displayBrightness, getState().displayBrightnessNits, Hwc2::Composer::DisplayBrightnessOptions{ .applyImmediately = applyImmediately}) diff --git a/services/surfaceflinger/Display/DisplayModeRequest.h b/services/surfaceflinger/Display/DisplayModeRequest.h index c0e77bb5fc..d07cdf55d2 100644 --- a/services/surfaceflinger/Display/DisplayModeRequest.h +++ b/services/surfaceflinger/Display/DisplayModeRequest.h @@ -27,9 +27,6 @@ struct DisplayModeRequest { // Whether to emit DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE. bool emitEvent = false; - - // Whether to force the request to be applied, even if the mode is unchanged. - bool force = false; }; inline bool operator==(const DisplayModeRequest& lhs, const DisplayModeRequest& rhs) { diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index b96264b65e..4f81482814 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -24,7 +24,6 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS -#include <common/FlagManager.h> #include <compositionengine/CompositionEngine.h> #include <compositionengine/Display.h> #include <compositionengine/DisplayColorProfile.h> @@ -68,6 +67,7 @@ DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs& args) mActiveModeFpsTrace(concatId("ActiveModeFps")), mRenderRateFpsTrace(concatId("RenderRateFps")), mPhysicalOrientation(args.physicalOrientation), + mPowerMode(ftl::Concat("PowerMode ", getId().value).c_str(), args.initialPowerMode), mIsPrimary(args.isPrimary), mRequestedRefreshRate(args.requestedRefreshRate), mRefreshRateSelector(std::move(args.refreshRateSelector)), @@ -106,9 +106,7 @@ DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs& args) mCompositionDisplay->getRenderSurface()->initialize(); - if (const auto powerModeOpt = args.initialPowerMode) { - setPowerMode(*powerModeOpt); - } + setPowerMode(args.initialPowerMode); // initialize the display orientation transform. setProjection(ui::ROTATION_0, Rect::INVALID_RECT, Rect::INVALID_RECT); @@ -173,6 +171,7 @@ auto DisplayDevice::getFrontEndInfo() const -> frontend::DisplayInfo { } void DisplayDevice::setPowerMode(hal::PowerMode mode) { + // TODO(b/241285876): Skip this for virtual displays. if (mode == hal::PowerMode::OFF || mode == hal::PowerMode::ON) { if (mStagedBrightness && mBrightness != mStagedBrightness) { getCompositionDisplay()->setNextBrightness(*mStagedBrightness); @@ -182,33 +181,26 @@ void DisplayDevice::setPowerMode(hal::PowerMode mode) { getCompositionDisplay()->applyDisplayBrightness(true); } - if (mPowerMode) { - *mPowerMode = mode; - } else { - mPowerMode.emplace("PowerMode -" + to_string(getId()), mode); - } + mPowerMode = mode; getCompositionDisplay()->setCompositionEnabled(isPoweredOn()); } void DisplayDevice::tracePowerMode() { - // assign the same value for tracing - if (mPowerMode) { - const hal::PowerMode powerMode = *mPowerMode; - *mPowerMode = powerMode; - } + // Assign the same value for tracing. + mPowerMode = mPowerMode.get(); } void DisplayDevice::enableLayerCaching(bool enable) { getCompositionDisplay()->setLayerCachingEnabled(enable); } -std::optional<hal::PowerMode> DisplayDevice::getPowerMode() const { +hal::PowerMode DisplayDevice::getPowerMode() const { return mPowerMode; } bool DisplayDevice::isPoweredOn() const { - return mPowerMode && *mPowerMode != hal::PowerMode::OFF; + return mPowerMode != hal::PowerMode::OFF; } void DisplayDevice::setActiveMode(DisplayModeId modeId, Fps vsyncRate, Fps renderFps) { @@ -222,17 +214,6 @@ void DisplayDevice::setActiveMode(DisplayModeId modeId, Fps vsyncRate, Fps rende bool DisplayDevice::initiateModeChange(display::DisplayModeRequest&& desiredMode, const hal::VsyncPeriodChangeConstraints& constraints, hal::VsyncPeriodChangeTimeline& outTimeline) { - // TODO(b/255635711): Flow the DisplayModeRequest through the desired/pending/active states. For - // now, `desiredMode` and `mDesiredModeOpt` are one and the same, but the latter is not cleared - // until the next `SF::initiateDisplayModeChanges`. However, the desired mode has been consumed - // at this point, so clear the `force` flag to prevent an endless loop of `initiateModeChange`. - if (FlagManager::getInstance().connected_display()) { - std::scoped_lock lock(mDesiredModeLock); - if (mDesiredModeOpt) { - mDesiredModeOpt->force = false; - } - } - mPendingModeOpt = std::move(desiredMode); mIsModeSetPending = true; @@ -538,7 +519,8 @@ void DisplayDevice::animateOverlay() { } } -auto DisplayDevice::setDesiredMode(display::DisplayModeRequest&& desiredMode) -> DesiredModeAction { +auto DisplayDevice::setDesiredMode(display::DisplayModeRequest&& desiredMode, bool force) + -> DesiredModeAction { ATRACE_CALL(); const auto& desiredModePtr = desiredMode.mode.modePtr; @@ -546,26 +528,20 @@ auto DisplayDevice::setDesiredMode(display::DisplayModeRequest&& desiredMode) -> LOG_ALWAYS_FATAL_IF(getPhysicalId() != desiredModePtr->getPhysicalDisplayId(), "DisplayId mismatch"); - // TODO (b/318533819): Stringize DisplayModeRequest. - ALOGD("%s(%s, force=%s)", __func__, to_string(*desiredModePtr).c_str(), - desiredMode.force ? "true" : "false"); + ALOGV("%s(%s)", __func__, to_string(*desiredModePtr).c_str()); std::scoped_lock lock(mDesiredModeLock); if (mDesiredModeOpt) { // A mode transition was already scheduled, so just override the desired mode. const bool emitEvent = mDesiredModeOpt->emitEvent; - const bool force = mDesiredModeOpt->force; mDesiredModeOpt = std::move(desiredMode); mDesiredModeOpt->emitEvent |= emitEvent; - if (FlagManager::getInstance().connected_display()) { - mDesiredModeOpt->force |= force; - } return DesiredModeAction::None; } // If the desired mode is already active... const auto activeMode = refreshRateSelector().getActiveMode(); - if (!desiredMode.force && activeMode.modePtr->getId() == desiredModePtr->getId()) { + if (!force && activeMode.modePtr->getId() == desiredModePtr->getId()) { if (activeMode == desiredMode.mode) { return DesiredModeAction::None; } diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index 46534de25e..4ab6321064 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -173,8 +173,8 @@ public: /* ------------------------------------------------------------------------ * Display power mode management. */ - std::optional<hardware::graphics::composer::hal::PowerMode> getPowerMode() const; - void setPowerMode(hardware::graphics::composer::hal::PowerMode mode); + hardware::graphics::composer::hal::PowerMode getPowerMode() const; + void setPowerMode(hardware::graphics::composer::hal::PowerMode); bool isPoweredOn() const; void tracePowerMode(); @@ -189,7 +189,8 @@ public: enum class DesiredModeAction { None, InitiateDisplayModeSwitch, InitiateRenderRateSwitch }; - DesiredModeAction setDesiredMode(display::DisplayModeRequest&&) EXCLUDES(mDesiredModeLock); + DesiredModeAction setDesiredMode(display::DisplayModeRequest&&, bool force = false) + EXCLUDES(mDesiredModeLock); using DisplayModeRequestOpt = ftl::Optional<display::DisplayModeRequest>; @@ -270,9 +271,7 @@ private: ui::Rotation mOrientation = ui::ROTATION_0; bool mIsOrientationChanged = false; - // Allow nullopt as initial power mode. - using TracedPowerMode = TracedOrdinal<hardware::graphics::composer::hal::PowerMode>; - std::optional<TracedPowerMode> mPowerMode; + TracedOrdinal<hardware::graphics::composer::hal::PowerMode> mPowerMode; std::optional<float> mStagedBrightness; std::optional<float> mBrightness; @@ -362,7 +361,8 @@ struct DisplayDeviceCreationArgs { HdrCapabilities hdrCapabilities; int32_t supportedPerFrameMetadata{0}; std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> hwcColorModes; - std::optional<hardware::graphics::composer::hal::PowerMode> initialPowerMode; + hardware::graphics::composer::hal::PowerMode initialPowerMode{ + hardware::graphics::composer::hal::PowerMode::OFF}; bool isPrimary{false}; DisplayModeId activeModeId; // Refer to DisplayDevice::mRequestedRefreshRate, for virtual display only diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp index 64a8ae7fcd..17f6f31441 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp @@ -330,7 +330,11 @@ std::string AidlComposer::dumpDebugInfo() { t.join(); close(pipefds[0]); - return str; + + std::string hash; + mAidlComposer->getInterfaceHash(&hash); + return std::string(mAidlComposer->descriptor) + + " version:" + std::to_string(mComposerInterfaceVersion) + " hash:" + hash + str; } void AidlComposer::registerCallback(HWC2::ComposerCallback& callback) { diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index db66f5b549..704ece516d 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -27,7 +27,6 @@ #include "HWC2.h" #include <android/configuration.h> -#include <common/FlagManager.h> #include <ui/Fence.h> #include <ui/FloatRect.h> #include <ui/GraphicBuffer.h> @@ -417,19 +416,7 @@ Error Display::setActiveConfigWithConstraints(hal::HWConfigId configId, VsyncPeriodChangeTimeline* outTimeline) { ALOGV("[%" PRIu64 "] setActiveConfigWithConstraints", mId); - // FIXME (b/319505580): At least the first config set on an external display must be - // `setActiveConfig`, so skip over the block that calls `setActiveConfigWithConstraints` - // for simplicity. - ui::DisplayConnectionType type = ui::DisplayConnectionType::Internal; - const bool connected_display = FlagManager::getInstance().connected_display(); - if (connected_display) { - if (auto err = getConnectionType(&type); err != Error::NONE) { - return err; - } - } - - if (isVsyncPeriodSwitchSupported() && - (!connected_display || type != ui::DisplayConnectionType::External)) { + if (isVsyncPeriodSwitchSupported()) { Hwc2::IComposerClient::VsyncPeriodChangeConstraints hwc2Constraints; hwc2Constraints.desiredTimeNanos = constraints.desiredTimeNanos; hwc2Constraints.seamlessRequired = constraints.seamlessRequired; diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp index c4ff9cc6be..12ab2c284a 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp @@ -291,6 +291,7 @@ std::vector<Capability> HidlComposer::getCapabilities() { std::string HidlComposer::dumpDebugInfo() { std::string info; + info += std::string(mComposer->descriptor) + "\n"; mComposer->dumpDebugInfo([&](const auto& tmpInfo) { info = tmpInfo.c_str(); }); return info; diff --git a/services/surfaceflinger/FpsReporter.cpp b/services/surfaceflinger/FpsReporter.cpp index 155cf4da58..a47348fbc4 100644 --- a/services/surfaceflinger/FpsReporter.cpp +++ b/services/surfaceflinger/FpsReporter.cpp @@ -26,13 +26,12 @@ namespace android { -FpsReporter::FpsReporter(frametimeline::FrameTimeline& frameTimeline, SurfaceFlinger& flinger, - std::unique_ptr<Clock> clock) - : mFrameTimeline(frameTimeline), mFlinger(flinger), mClock(std::move(clock)) { +FpsReporter::FpsReporter(frametimeline::FrameTimeline& frameTimeline, std::unique_ptr<Clock> clock) + : mFrameTimeline(frameTimeline), mClock(std::move(clock)) { LOG_ALWAYS_FATAL_IF(mClock == nullptr, "Passed in null clock when constructing FpsReporter!"); } -void FpsReporter::dispatchLayerFps() { +void FpsReporter::dispatchLayerFps(const frontend::LayerHierarchy& layerHierarchy) { const auto now = mClock->now(); if (now - mLastDispatch < kMinDispatchDuration) { return; @@ -52,31 +51,42 @@ void FpsReporter::dispatchLayerFps() { } std::unordered_set<int32_t> seenTasks; - std::vector<std::pair<TrackedListener, sp<Layer>>> listenersAndLayersToReport; + std::vector<std::pair<TrackedListener, const frontend::LayerHierarchy*>> + listenersAndLayersToReport; - mFlinger.mCurrentState.traverse([&](Layer* layer) { - auto& currentState = layer->getDrawingState(); - if (currentState.metadata.has(gui::METADATA_TASK_ID)) { - int32_t taskId = currentState.metadata.getInt32(gui::METADATA_TASK_ID, 0); + layerHierarchy.traverse([&](const frontend::LayerHierarchy& hierarchy, + const frontend::LayerHierarchy::TraversalPath& traversalPath) { + if (traversalPath.variant == frontend::LayerHierarchy::Variant::Detached) { + return false; + } + const auto& metadata = hierarchy.getLayer()->metadata; + if (metadata.has(gui::METADATA_TASK_ID)) { + int32_t taskId = metadata.getInt32(gui::METADATA_TASK_ID, 0); if (seenTasks.count(taskId) == 0) { // localListeners is expected to be tiny for (TrackedListener& listener : localListeners) { if (listener.taskId == taskId) { seenTasks.insert(taskId); - listenersAndLayersToReport.push_back( - {listener, sp<Layer>::fromExisting(layer)}); + listenersAndLayersToReport.push_back({listener, &hierarchy}); break; } } } } + return true; }); - for (const auto& [listener, layer] : listenersAndLayersToReport) { + for (const auto& [listener, hierarchy] : listenersAndLayersToReport) { std::unordered_set<int32_t> layerIds; - layer->traverse(LayerVector::StateSet::Current, - [&](Layer* layer) { layerIds.insert(layer->getSequence()); }); + hierarchy->traverse([&](const frontend::LayerHierarchy& hierarchy, + const frontend::LayerHierarchy::TraversalPath& traversalPath) { + if (traversalPath.variant == frontend::LayerHierarchy::Variant::Detached) { + return false; + } + layerIds.insert(static_cast<int32_t>(hierarchy.getLayer()->id)); + return true; + }); listener.listener->onFpsReported(mFrameTimeline.computeFps(layerIds)); } diff --git a/services/surfaceflinger/FpsReporter.h b/services/surfaceflinger/FpsReporter.h index 438b1aa362..01f1e07b26 100644 --- a/services/surfaceflinger/FpsReporter.h +++ b/services/surfaceflinger/FpsReporter.h @@ -24,6 +24,7 @@ #include "Clock.h" #include "FrameTimeline/FrameTimeline.h" +#include "FrontEnd/LayerHierarchy.h" #include "WpHash.h" namespace android { @@ -33,13 +34,13 @@ class SurfaceFlinger; class FpsReporter : public IBinder::DeathRecipient { public: - FpsReporter(frametimeline::FrameTimeline& frameTimeline, SurfaceFlinger& flinger, + FpsReporter(frametimeline::FrameTimeline& frameTimeline, std::unique_ptr<Clock> clock = std::make_unique<SteadyClock>()); // Dispatches updated layer fps values for the registered listeners // This method promotes Layer weak pointers and performs layer stack traversals, so mStateLock // must be held when calling this method. - void dispatchLayerFps() EXCLUDES(mMutex); + void dispatchLayerFps(const frontend::LayerHierarchy&) EXCLUDES(mMutex); // Override for IBinder::DeathRecipient void binderDied(const wp<IBinder>&) override; @@ -58,7 +59,6 @@ private: }; frametimeline::FrameTimeline& mFrameTimeline; - SurfaceFlinger& mFlinger; static const constexpr std::chrono::steady_clock::duration kMinDispatchDuration = std::chrono::milliseconds(500); std::unique_ptr<Clock> mClock; diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp index cf8b3bfdbd..a145e59eb5 100644 --- a/services/surfaceflinger/Scheduler/MessageQueue.cpp +++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp @@ -75,9 +75,9 @@ void MessageQueue::vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, ns mHandler->dispatchFrame(vsyncId, expectedVsyncTime); } -void MessageQueue::initVsync(std::shared_ptr<scheduler::VSyncDispatch> dispatch, - frametimeline::TokenManager& tokenManager, - std::chrono::nanoseconds workDuration) { +void MessageQueue::initVsyncInternal(std::shared_ptr<scheduler::VSyncDispatch> dispatch, + frametimeline::TokenManager& tokenManager, + std::chrono::nanoseconds workDuration) { std::unique_ptr<scheduler::VSyncCallbackRegistration> oldRegistration; { std::lock_guard lock(mVsync.mutex); @@ -87,7 +87,7 @@ void MessageQueue::initVsync(std::shared_ptr<scheduler::VSyncDispatch> dispatch, } // See comments in onNewVsyncSchedule. Today, oldRegistration should be - // empty, but nothing prevents us from calling initVsync multiple times, so + // empty, but nothing prevents us from calling initVsyncInternal multiple times, so // go ahead and destruct it outside the lock for safety. oldRegistration.reset(); } diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h index a523147733..edb424b5b9 100644 --- a/services/surfaceflinger/Scheduler/MessageQueue.h +++ b/services/surfaceflinger/Scheduler/MessageQueue.h @@ -65,8 +65,9 @@ class MessageQueue { public: virtual ~MessageQueue() = default; - virtual void initVsync(std::shared_ptr<scheduler::VSyncDispatch>, frametimeline::TokenManager&, - std::chrono::nanoseconds workDuration) = 0; + virtual void initVsyncInternal(std::shared_ptr<scheduler::VSyncDispatch>, + frametimeline::TokenManager&, + std::chrono::nanoseconds workDuration) = 0; virtual void destroyVsync() = 0; virtual void setDuration(std::chrono::nanoseconds workDuration) = 0; virtual void waitMessage() = 0; @@ -137,8 +138,8 @@ private: public: explicit MessageQueue(ICompositor&); - void initVsync(std::shared_ptr<scheduler::VSyncDispatch>, frametimeline::TokenManager&, - std::chrono::nanoseconds workDuration) override; + void initVsyncInternal(std::shared_ptr<scheduler::VSyncDispatch>, frametimeline::TokenManager&, + std::chrono::nanoseconds workDuration) override; void destroyVsync() override; void setDuration(std::chrono::nanoseconds workDuration) override; diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h index 67e1b9c2ea..d51af9ac88 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateStats.h +++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h @@ -49,10 +49,10 @@ class RefreshRateStats { public: // TODO(b/185535769): Inject clock to avoid sleeping in tests. - RefreshRateStats(TimeStats& timeStats, Fps currentRefreshRate, PowerMode currentPowerMode) + RefreshRateStats(TimeStats& timeStats, Fps currentRefreshRate) : mTimeStats(timeStats), mCurrentRefreshRate(currentRefreshRate), - mCurrentPowerMode(currentPowerMode) {} + mCurrentPowerMode(PowerMode::OFF) {} void setPowerMode(PowerMode mode) { if (mCurrentPowerMode == mode) { diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 27ca17f481..c76d4bd982 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -77,8 +77,7 @@ Scheduler::Scheduler(ICompositor& compositor, ISchedulerCallback& callback, Feat mFeatures(features), mVsyncConfiguration(factory.createVsyncConfiguration(activeRefreshRate)), mVsyncModulator(sp<VsyncModulator>::make(mVsyncConfiguration->getCurrentConfigs())), - mRefreshRateStats(std::make_unique<RefreshRateStats>(timeStats, activeRefreshRate, - hal::PowerMode::OFF)), + mRefreshRateStats(std::make_unique<RefreshRateStats>(timeStats, activeRefreshRate)), mSchedulerCallback(callback), mVsyncTrackerCallback(vsyncTrackerCallback) {} @@ -96,6 +95,11 @@ Scheduler::~Scheduler() { demotePacesetterDisplay(); } +void Scheduler::initVsync(frametimeline::TokenManager& tokenManager, + std::chrono::nanoseconds workDuration) { + Impl::initVsyncInternal(getVsyncSchedule()->getDispatch(), tokenManager, workDuration); +} + void Scheduler::startTimers() { using namespace sysprop; using namespace std::string_literals; @@ -642,13 +646,13 @@ Fps Scheduler::getNextFrameInterval(PhysicalDisplayId id, return Fps{}; } const Display& display = *displayOpt; - const nsecs_t threshold = - display.selectorPtr->getActiveMode().modePtr->getVsyncRate().getPeriodNsecs() / 2; - const nsecs_t nextVsyncTime = - display.schedulePtr->getTracker() - .nextAnticipatedVSyncTimeFrom(currentExpectedPresentTime.ns() + threshold, - currentExpectedPresentTime.ns()); - return Fps::fromPeriodNsecs(nextVsyncTime - currentExpectedPresentTime.ns()); + const Duration threshold = + display.selectorPtr->getActiveMode().modePtr->getVsyncRate().getPeriod() / 2; + const TimePoint nextVsyncTime = + display.schedulePtr->vsyncDeadlineAfter(currentExpectedPresentTime + threshold, + currentExpectedPresentTime); + const Duration frameInterval = nextVsyncTime - currentExpectedPresentTime; + return Fps::fromPeriodNsecs(frameInterval.ns()); } void Scheduler::resync() { diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index f62f1baf7c..99126220b0 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -131,7 +131,7 @@ public: void run(); - using Impl::initVsync; + void initVsync(frametimeline::TokenManager&, std::chrono::nanoseconds workDuration); using Impl::getScheduledFrameTime; using Impl::setDuration; diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp index db6a1871a7..3491600d8e 100644 --- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp +++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp @@ -89,8 +89,12 @@ Period VsyncSchedule::minFramePeriod() const { return period(); } -TimePoint VsyncSchedule::vsyncDeadlineAfter(TimePoint timePoint) const { - return TimePoint::fromNs(mTracker->nextAnticipatedVSyncTimeFrom(timePoint.ns())); +TimePoint VsyncSchedule::vsyncDeadlineAfter(TimePoint timePoint, + ftl::Optional<TimePoint> lastVsyncOpt) const { + return TimePoint::fromNs( + mTracker->nextAnticipatedVSyncTimeFrom(timePoint.ns(), + lastVsyncOpt.transform( + [](TimePoint t) { return t.ns(); }))); } void VsyncSchedule::dump(std::string& out) const { diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h index 722ea0b836..6f656d2029 100644 --- a/services/surfaceflinger/Scheduler/VsyncSchedule.h +++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h @@ -63,7 +63,8 @@ public: // IVsyncSource overrides: Period period() const override; - TimePoint vsyncDeadlineAfter(TimePoint) const override; + TimePoint vsyncDeadlineAfter(TimePoint, + ftl::Optional<TimePoint> lastVsyncOpt = {}) const override; Period minFramePeriod() const override; // Inform the schedule that the display mode changed the schedule needs to recalibrate diff --git a/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h b/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h index 0154060f36..f0f7a87105 100644 --- a/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h +++ b/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h @@ -16,13 +16,14 @@ #pragma once +#include <ftl/optional.h> #include <scheduler/Time.h> namespace android::scheduler { struct IVsyncSource { virtual Period period() const = 0; - virtual TimePoint vsyncDeadlineAfter(TimePoint) const = 0; + virtual TimePoint vsyncDeadlineAfter(TimePoint, ftl::Optional<TimePoint> = {}) const = 0; virtual Period minFramePeriod() const = 0; protected: diff --git a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp index a9abcaf14c..29711afdf9 100644 --- a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp +++ b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp @@ -38,7 +38,9 @@ struct VsyncSource final : IVsyncSource { const TimePoint vsyncDeadline; Period period() const override { return vsyncPeriod; } - TimePoint vsyncDeadlineAfter(TimePoint) const override { return vsyncDeadline; } + TimePoint vsyncDeadlineAfter(TimePoint, ftl::Optional<TimePoint> = {}) const override { + return vsyncDeadline; + } Period minFramePeriod() const override { return framePeriod; } }; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 178828a5f6..47e7474cfb 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -799,22 +799,26 @@ void SurfaceFlinger::bootFinished() { })); } -static std::optional<renderengine::RenderEngine::RenderEngineType> -chooseRenderEngineTypeViaSysProp() { +void chooseRenderEngineType(renderengine::RenderEngineCreationArgs::Builder& builder) { char prop[PROPERTY_VALUE_MAX]; - property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "skiaglthreaded"); + property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, ""); if (strcmp(prop, "skiagl") == 0) { - return renderengine::RenderEngine::RenderEngineType::SKIA_GL; + builder.setThreaded(renderengine::RenderEngine::Threaded::NO) + .setGraphicsApi(renderengine::RenderEngine::GraphicsApi::GL); } else if (strcmp(prop, "skiaglthreaded") == 0) { - return renderengine::RenderEngine::RenderEngineType::SKIA_GL_THREADED; + builder.setThreaded(renderengine::RenderEngine::Threaded::YES) + .setGraphicsApi(renderengine::RenderEngine::GraphicsApi::GL); } else if (strcmp(prop, "skiavk") == 0) { - return renderengine::RenderEngine::RenderEngineType::SKIA_VK; + builder.setThreaded(renderengine::RenderEngine::Threaded::NO) + .setGraphicsApi(renderengine::RenderEngine::GraphicsApi::VK); } else if (strcmp(prop, "skiavkthreaded") == 0) { - return renderengine::RenderEngine::RenderEngineType::SKIA_VK_THREADED; + builder.setThreaded(renderengine::RenderEngine::Threaded::YES) + .setGraphicsApi(renderengine::RenderEngine::GraphicsApi::VK); } else { - ALOGE("Unrecognized RenderEngineType %s; ignoring!", prop); - return {}; + builder.setGraphicsApi(FlagManager::getInstance().vulkan_renderengine() + ? renderengine::RenderEngine::GraphicsApi::VK + : renderengine::RenderEngine::GraphicsApi::GL); } } @@ -840,9 +844,7 @@ void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) { useContextPriority ? renderengine::RenderEngine::ContextPriority::REALTIME : renderengine::RenderEngine::ContextPriority::MEDIUM); - if (auto type = chooseRenderEngineTypeViaSysProp()) { - builder.setRenderEngineType(type.value()); - } + chooseRenderEngineType(builder); mRenderEngine = renderengine::RenderEngine::create(builder.build()); mCompositionEngine->setRenderEngine(mRenderEngine.get()); mMaxRenderTargetSize = @@ -1228,10 +1230,8 @@ status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>& displayToken, return NO_ERROR; } -void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& desiredMode) { - const auto mode = desiredMode.mode; - const auto displayId = mode.modePtr->getPhysicalDisplayId(); - +void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& request, bool force) { + const auto displayId = request.mode.modePtr->getPhysicalDisplayId(); ATRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str()); const auto display = getDisplayDeviceLocked(displayId); @@ -1240,9 +1240,10 @@ void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& desiredMode) { return; } - const bool emitEvent = desiredMode.emitEvent; + const auto mode = request.mode; + const bool emitEvent = request.emitEvent; - switch (display->setDesiredMode(std::move(desiredMode))) { + switch (display->setDesiredMode(std::move(request), force)) { case DisplayDevice::DesiredModeAction::InitiateDisplayModeSwitch: // DisplayDevice::setDesiredMode updated the render rate, so inform Scheduler. mScheduler->setRenderRate(displayId, @@ -1428,8 +1429,7 @@ void SurfaceFlinger::initiateDisplayModeChanges() { to_string(displayModePtrOpt->get()->getVsyncRate()).c_str(), to_string(display->getId()).c_str()); - if ((!FlagManager::getInstance().connected_display() || !desiredModeOpt->force) && - display->getActiveMode() == desiredModeOpt->mode) { + if (display->getActiveMode() == desiredModeOpt->mode) { applyActiveMode(display); continue; } @@ -1953,6 +1953,7 @@ status_t SurfaceFlinger::setDisplayBrightness(const sp<IBinder>& displayToken, const char* const whence = __func__; return ftl::Future(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) { + // TODO(b/241285876): Validate that the display is physical instead of failing later. if (const auto display = getDisplayDeviceLocked(displayToken)) { const bool supportsDisplayBrightnessCommand = getHwComposer().getComposer()->isSupported( @@ -2002,7 +2003,6 @@ status_t SurfaceFlinger::setDisplayBrightness(const sp<IBinder>& displayToken, Hwc2::Composer::DisplayBrightnessOptions{ .applyImmediately = true}); } - } else { ALOGE("%s: Invalid display token %p", whence, displayToken.get()); return ftl::yield<status_t>(NAME_NOT_FOUND); @@ -3098,7 +3098,7 @@ void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId, { Mutex::Autolock lock(mStateLock); if (mFpsReporter) { - mFpsReporter->dispatchLayerFps(); + mFpsReporter->dispatchLayerFps(mLayerHierarchyBuilder.getHierarchy()); } if (mTunnelModeEnabledReporter) { @@ -3295,88 +3295,14 @@ std::pair<DisplayModes, DisplayModePtr> SurfaceFlinger::loadDisplayModes( std::vector<HWComposer::HWCDisplayMode> hwcModes; std::optional<hal::HWDisplayId> activeModeHwcId; - const bool isExternalDisplay = FlagManager::getInstance().connected_display() && - getHwComposer().getDisplayConnectionType(displayId) == - ui::DisplayConnectionType::External; - int attempt = 0; constexpr int kMaxAttempts = 3; do { hwcModes = getHwComposer().getModes(displayId, scheduler::RefreshRateSelector::kMinSupportedFrameRate .getPeriodNsecs()); - activeModeHwcId = getHwComposer().getActiveMode(displayId); - if (isExternalDisplay) { - constexpr nsecs_t k59HzVsyncPeriod = 16949153; - constexpr nsecs_t k60HzVsyncPeriod = 16666667; - - // DM sets the initial mode for an external display to 1080p@60, but - // this comes after SF creates its own state (including the - // DisplayDevice). For now, pick the same mode in order to avoid - // inconsistent state and unnecessary mode switching. - // TODO (b/318534874): Let DM decide the initial mode. - // - // Try to find 1920x1080 @ 60 Hz - if (const auto iter = std::find_if(hwcModes.begin(), hwcModes.end(), - [](const auto& mode) { - return mode.width == 1920 && - mode.height == 1080 && - mode.vsyncPeriod == k60HzVsyncPeriod; - }); - iter != hwcModes.end()) { - activeModeHwcId = iter->hwcId; - break; - } - - // Try to find 1920x1080 @ 59-60 Hz - if (const auto iter = std::find_if(hwcModes.begin(), hwcModes.end(), - [](const auto& mode) { - return mode.width == 1920 && - mode.height == 1080 && - mode.vsyncPeriod >= k60HzVsyncPeriod && - mode.vsyncPeriod <= k59HzVsyncPeriod; - }); - iter != hwcModes.end()) { - activeModeHwcId = iter->hwcId; - break; - } - - // The display does not support 1080p@60, and this is the last attempt to pick a display - // mode. Prefer 60 Hz if available, with the closest resolution to 1080p. - if (attempt + 1 == kMaxAttempts) { - std::vector<HWComposer::HWCDisplayMode> hwcModeOpts; - - for (const auto& mode : hwcModes) { - if (mode.width <= 1920 && mode.height <= 1080 && - mode.vsyncPeriod >= k60HzVsyncPeriod && - mode.vsyncPeriod <= k59HzVsyncPeriod) { - hwcModeOpts.push_back(mode); - } - } - - if (const auto iter = std::max_element(hwcModeOpts.begin(), hwcModeOpts.end(), - [](const auto& a, const auto& b) { - const auto aSize = a.width * a.height; - const auto bSize = b.width * b.height; - if (aSize < bSize) - return true; - else if (aSize == bSize) - return a.vsyncPeriod > b.vsyncPeriod; - else - return false; - }); - iter != hwcModeOpts.end()) { - activeModeHwcId = iter->hwcId; - break; - } - - // hwcModeOpts was empty, use hwcModes[0] as the last resort - activeModeHwcId = hwcModes[0].hwcId; - } - } - const auto isActiveMode = [activeModeHwcId](const HWComposer::HWCDisplayMode& mode) { return mode.hwcId == activeModeHwcId; }; @@ -3436,10 +3362,6 @@ std::pair<DisplayModes, DisplayModePtr> SurfaceFlinger::loadDisplayModes( return pair.second->getHwcId() == activeModeHwcId; })->second; - if (isExternalDisplay) { - ALOGI("External display %s initial mode: {%s}", to_string(displayId).c_str(), - to_string(*activeMode).c_str()); - } return {modes, activeMode}; } @@ -3625,9 +3547,7 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( getPhysicalDisplayOrientation(compositionDisplay->getId(), creationArgs.isPrimary); ALOGV("Display Orientation: %s", toCString(creationArgs.physicalOrientation)); - // virtual displays are always considered enabled - creationArgs.initialPowerMode = - state.isVirtual() ? std::make_optional(hal::PowerMode::ON) : std::nullopt; + creationArgs.initialPowerMode = state.isVirtual() ? hal::PowerMode::ON : hal::PowerMode::OFF; creationArgs.requestedRefreshRate = state.requestedRefreshRate; @@ -3746,27 +3666,6 @@ void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken, } mDisplays.try_emplace(displayToken, std::move(display)); - - // For an external display, loadDisplayModes already selected the same mode - // as DM, but SF still needs to be updated to match. - // TODO (b/318534874): Let DM decide the initial mode. - if (const auto& physical = state.physical; - mScheduler && physical && FlagManager::getInstance().connected_display()) { - const bool isInternalDisplay = mPhysicalDisplays.get(physical->id) - .transform(&PhysicalDisplay::isInternal) - .value_or(false); - - if (!isInternalDisplay) { - auto activeModePtr = physical->activeMode; - const auto fps = activeModePtr->getPeakFps(); - - setDesiredMode( - {.mode = scheduler::FrameRateMode{fps, - ftl::as_non_null(std::move(activeModePtr))}, - .emitEvent = false, - .force = true}); - } - } } void SurfaceFlinger::processDisplayRemoved(const wp<IBinder>& displayToken) { @@ -4335,13 +4234,12 @@ void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { /* workDuration */ activeRefreshRate.getPeriod(), /* readyDuration */ configs.late.sfWorkDuration); - mScheduler->initVsync(mScheduler->getVsyncSchedule()->getDispatch(), - *mFrameTimeline->getTokenManager(), configs.late.sfWorkDuration); + mScheduler->initVsync(*mFrameTimeline->getTokenManager(), configs.late.sfWorkDuration); mRegionSamplingThread = sp<RegionSamplingThread>::make(*this, RegionSamplingThread::EnvironmentTimingTunables()); - mFpsReporter = sp<FpsReporter>::make(*mFrameTimeline, *this); + mFpsReporter = sp<FpsReporter>::make(*mFrameTimeline); } void SurfaceFlinger::doCommitTransactions() { @@ -5913,12 +5811,6 @@ void SurfaceFlinger::onHandleDestroyed(BBinder* handle, sp<Layer>& layer, uint32 } void SurfaceFlinger::initializeDisplays() { - const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()); - if (!display) return; - - const sp<IBinder> token = display->getDisplayToken().promote(); - LOG_ALWAYS_FATAL_IF(token == nullptr); - TransactionState state; state.inputWindowCommands = mInputWindowCommands; const nsecs_t now = systemTime(); @@ -5929,18 +5821,10 @@ void SurfaceFlinger::initializeDisplays() { const uint64_t transactionId = (static_cast<uint64_t>(mPid) << 32) | mUniqueTransactionId++; state.id = transactionId; - // reset screen orientation and use primary layer stack - DisplayState d; - d.what = DisplayState::eDisplayProjectionChanged | - DisplayState::eLayerStackChanged; - d.token = token; - d.layerStack = ui::DEFAULT_LAYER_STACK; - d.orientation = ui::ROTATION_0; - d.orientedDisplaySpaceRect.makeInvalid(); - d.layerStackSpaceRect.makeInvalid(); - d.width = 0; - d.height = 0; - state.displays.add(d); + auto layerStack = ui::DEFAULT_LAYER_STACK.id; + for (const auto& [id, display] : FTL_FAKE_GUARD(mStateLock, mPhysicalDisplays)) { + state.displays.push(DisplayState(display.token(), ui::LayerStack::fromValue(layerStack++))); + } std::vector<TransactionState> transactions; transactions.emplace_back(state); @@ -5953,12 +5837,25 @@ void SurfaceFlinger::initializeDisplays() { { ftl::FakeGuard guard(mStateLock); - setPowerModeInternal(display, hal::PowerMode::ON); + + // In case of a restart, ensure all displays are off. + for (const auto& [id, display] : mPhysicalDisplays) { + setPowerModeInternal(getDisplayDeviceLocked(id), hal::PowerMode::OFF); + } + + // Power on all displays. The primary display is first, so becomes the active display. Also, + // the DisplayCapability set of a display is populated on its first powering on. Do this now + // before responding to any Binder query from DisplayManager about display capabilities. + for (const auto& [id, display] : mPhysicalDisplays) { + setPowerModeInternal(getDisplayDeviceLocked(id), hal::PowerMode::ON); + } } } void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) { if (display->isVirtual()) { + // TODO(b/241285876): This code path should not be reachable, so enforce this at compile + // time. ALOGE("%s: Invalid operation on virtual display", __func__); return; } @@ -5966,8 +5863,8 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: const auto displayId = display->getPhysicalId(); ALOGD("Setting power mode %d on display %s", mode, to_string(displayId).c_str()); - const auto currentModeOpt = display->getPowerMode(); - if (currentModeOpt == mode) { + const auto currentMode = display->getPowerMode(); + if (currentMode == mode) { return; } @@ -5984,7 +5881,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: display->setPowerMode(mode); const auto activeMode = display->refreshRateSelector().getActiveMode().modePtr; - if (!currentModeOpt || *currentModeOpt == hal::PowerMode::OFF) { + if (currentMode == hal::PowerMode::OFF) { // Turn on the display // Activate the display (which involves a modeset to the active mode) when the inner or @@ -6029,7 +5926,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: mVisibleRegionsDirty = true; scheduleComposite(FrameHint::kActive); } else if (mode == hal::PowerMode::OFF) { - const bool currentModeNotDozeSuspend = (*currentModeOpt != hal::PowerMode::DOZE_SUSPEND); + const bool currentModeNotDozeSuspend = (currentMode != hal::PowerMode::DOZE_SUSPEND); // Turn off the display if (displayId == mActiveDisplayId) { if (const auto display = getActivatableDisplay()) { @@ -6070,7 +5967,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: } else if (mode == hal::PowerMode::DOZE || mode == hal::PowerMode::ON) { // Update display while dozing getHwComposer().setPowerMode(displayId, mode); - if (*currentModeOpt == hal::PowerMode::DOZE_SUSPEND && + if (currentMode == hal::PowerMode::DOZE_SUSPEND && (displayId == mActiveDisplayId || FlagManager::getInstance().multithreaded_present())) { if (displayId == mActiveDisplayId) { ALOGI("Force repainting for DOZE_SUSPEND -> DOZE or ON."); @@ -8258,13 +8155,8 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( // // TODO(b/196334700) Once we use RenderEngineThreaded everywhere we can always defer the call // to CompositionEngine::present. - const bool renderEngineIsThreaded = [&]() { - using Type = renderengine::RenderEngine::RenderEngineType; - const auto type = mRenderEngine->getRenderEngineType(); - return type == Type::SKIA_GL_THREADED; - }(); - auto presentFuture = renderEngineIsThreaded ? ftl::defer(std::move(present)).share() - : ftl::yield(present()).share(); + auto presentFuture = mRenderEngine->isThreaded() ? ftl::defer(std::move(present)).share() + : ftl::yield(present()).share(); for (auto& [layer, layerFE] : layers) { layer->onLayerDisplayed(ftl::Future(presentFuture) @@ -8441,7 +8333,7 @@ status_t SurfaceFlinger::applyRefreshRateSelectorPolicy( return INVALID_OPERATION; } - setDesiredMode({std::move(preferredMode), .emitEvent = true, .force = force}); + setDesiredMode({std::move(preferredMode), .emitEvent = true}, force); // Update the frameRateOverride list as the display render rate might have changed if (mScheduler->updateFrameRateOverrides(scheduler::GlobalSignals{}, preferredFps)) { diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 24a1a108ce..c8e2a4d70a 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -719,7 +719,7 @@ private: // Show hdr sdr ratio overlay bool mHdrSdrRatioOverlay = false; - void setDesiredMode(display::DisplayModeRequest&&) REQUIRES(mStateLock); + void setDesiredMode(display::DisplayModeRequest&&, bool force = false) REQUIRES(mStateLock); status_t setActiveModeFromBackdoor(const sp<display::DisplayToken>&, DisplayModeId, Fps minFps, Fps maxFps); @@ -904,7 +904,8 @@ private: * Display and layer stack management */ - // Called during boot, and restart after system_server death. + // Called during boot and restart after system_server death, setting the stage for bootanimation + // before DisplayManager takes over. void initializeDisplays() REQUIRES(kMainThreadContext); sp<const DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& displayToken) const diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp index a27e10033d..255b517e3e 100644 --- a/services/surfaceflinger/common/FlagManager.cpp +++ b/services/surfaceflinger/common/FlagManager.cpp @@ -128,6 +128,7 @@ void FlagManager::dump(std::string& result) const { DUMP_READ_ONLY_FLAG(fp16_client_target); DUMP_READ_ONLY_FLAG(game_default_frame_rate); DUMP_READ_ONLY_FLAG(enable_layer_command_batching); + DUMP_READ_ONLY_FLAG(vulkan_renderengine); #undef DUMP_READ_ONLY_FLAG #undef DUMP_SERVER_FLAG #undef DUMP_FLAG_INTERVAL @@ -202,6 +203,7 @@ FLAG_MANAGER_READ_ONLY_FLAG(display_protected, "") FLAG_MANAGER_READ_ONLY_FLAG(fp16_client_target, "debug.sf.fp16_client_target") FLAG_MANAGER_READ_ONLY_FLAG(game_default_frame_rate, "") FLAG_MANAGER_READ_ONLY_FLAG(enable_layer_command_batching, "") +FLAG_MANAGER_READ_ONLY_FLAG(vulkan_renderengine, "debug.renderengine.vulkan") /// Trunk stable server flags /// FLAG_MANAGER_SERVER_FLAG(late_boot_misc2, "") diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h index c01465b04e..15938c012c 100644 --- a/services/surfaceflinger/common/include/common/FlagManager.h +++ b/services/surfaceflinger/common/include/common/FlagManager.h @@ -68,6 +68,7 @@ public: bool fp16_client_target() const; bool game_default_frame_rate() const; bool enable_layer_command_batching() const; + bool vulkan_renderengine() const; protected: // overridden for unit tests diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp index 8a5500f167..a6b12d0a12 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp +++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp @@ -405,8 +405,8 @@ void SchedulerFuzzer::fuzzRefreshRateSelector() { Fps::fromValue(mFdp.ConsumeFloatingPoint<float>())); android::mock::TimeStats timeStats; - RefreshRateStats refreshRateStats(timeStats, Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()), - PowerMode::OFF); + RefreshRateStats refreshRateStats(timeStats, + Fps::fromValue(mFdp.ConsumeFloatingPoint<float>())); const auto fpsOpt = displayModes.get(modeId).transform( [](const DisplayModePtr& mode) { return mode->getVsyncRate(); }); @@ -441,7 +441,9 @@ void SchedulerFuzzer::fuzzFrameTargeter() { FuzzedDataProvider& fuzzer; Period period() const { return getFuzzedDuration(fuzzer); } - TimePoint vsyncDeadlineAfter(TimePoint) const { return getFuzzedTimePoint(fuzzer); } + TimePoint vsyncDeadlineAfter(TimePoint, ftl::Optional<TimePoint> = {}) const { + return getFuzzedTimePoint(fuzzer); + } Period minFramePeriod() const { return period(); } } vsyncSource{mFdp}; diff --git a/services/surfaceflinger/tests/unittests/ActiveDisplayRotationFlagsTest.cpp b/services/surfaceflinger/tests/unittests/ActiveDisplayRotationFlagsTest.cpp index f1bb231f26..b17b529793 100644 --- a/services/surfaceflinger/tests/unittests/ActiveDisplayRotationFlagsTest.cpp +++ b/services/surfaceflinger/tests/unittests/ActiveDisplayRotationFlagsTest.cpp @@ -17,7 +17,7 @@ #undef LOG_TAG #define LOG_TAG "LibSurfaceFlingerUnittests" -#include "DisplayTransactionTestHelpers.h" +#include "DualDisplayTransactionTest.h" #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -25,31 +25,10 @@ namespace android { namespace { -struct ActiveDisplayRotationFlagsTest : DisplayTransactionTest { - static constexpr bool kWithMockScheduler = false; - ActiveDisplayRotationFlagsTest() : DisplayTransactionTest(kWithMockScheduler) {} - +struct ActiveDisplayRotationFlagsTest + : DualDisplayTransactionTest<hal::PowerMode::ON, hal::PowerMode::OFF> { void SetUp() override { - injectMockScheduler(kInnerDisplayId); - - // Inject inner and outer displays with uninitialized power modes. - constexpr bool kInitPowerMode = false; - { - InnerDisplayVariant::injectHwcDisplay<kInitPowerMode>(this); - auto injector = InnerDisplayVariant::makeFakeExistingDisplayInjector(this); - injector.setPowerMode(std::nullopt); - injector.setRefreshRateSelector(mFlinger.scheduler()->refreshRateSelector()); - mInnerDisplay = injector.inject(); - } - { - OuterDisplayVariant::injectHwcDisplay<kInitPowerMode>(this); - auto injector = OuterDisplayVariant::makeFakeExistingDisplayInjector(this); - injector.setPowerMode(std::nullopt); - mOuterDisplay = injector.inject(); - } - - mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON); - mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON); + DualDisplayTransactionTest::SetUp(); // The flags are a static variable, so by modifying them in the test, we // are modifying the real ones used by SurfaceFlinger. Save the original @@ -64,10 +43,6 @@ struct ActiveDisplayRotationFlagsTest : DisplayTransactionTest { void TearDown() override { mFlinger.mutableActiveDisplayRotationFlags() = mOldRotationFlags; } - static inline PhysicalDisplayId kInnerDisplayId = InnerDisplayVariant::DISPLAY_ID::get(); - static inline PhysicalDisplayId kOuterDisplayId = OuterDisplayVariant::DISPLAY_ID::get(); - - sp<DisplayDevice> mInnerDisplay, mOuterDisplay; ui::Transform::RotationFlags mOldRotationFlags; }; diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h index 99fef7ea24..387d2f26e6 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h @@ -347,7 +347,6 @@ struct HwcDisplayVariant { // The HWC active configuration id static constexpr hal::HWConfigId HWC_ACTIVE_CONFIG_ID = 2001; - static constexpr PowerMode INIT_POWER_MODE = hal::PowerMode::ON; static void injectPendingHotplugEvent(DisplayTransactionTest* test, Connection connection) { test->mFlinger.mutablePendingHotplugEvents().emplace_back( @@ -355,7 +354,7 @@ struct HwcDisplayVariant { } // Called by tests to inject a HWC display setup - template <bool kInitPowerMode = true> + template <hal::PowerMode kPowerMode = hal::PowerMode::ON> static void injectHwcDisplayWithNoDefaultCapabilities(DisplayTransactionTest* test) { const auto displayId = DisplayVariant::DISPLAY_ID::get(); ASSERT_FALSE(GpuVirtualDisplayId::tryCast(displayId)); @@ -364,22 +363,37 @@ struct HwcDisplayVariant { .setHwcDisplayId(HWC_DISPLAY_ID) .setResolution(DisplayVariant::RESOLUTION) .setActiveConfig(HWC_ACTIVE_CONFIG_ID) - .setPowerMode(kInitPowerMode ? std::make_optional(INIT_POWER_MODE) : std::nullopt) + .setPowerMode(kPowerMode) .inject(&test->mFlinger, test->mComposer); } // Called by tests to inject a HWC display setup - template <bool kInitPowerMode = true> + // + // TODO(b/241285876): The `kExpectSetPowerModeOnce` argument is set to `false` by tests that + // power on/off displays several times. Replace those catch-all expectations with `InSequence` + // and `RetiresOnSaturation`. + // + template <hal::PowerMode kPowerMode = hal::PowerMode::ON, bool kExpectSetPowerModeOnce = true> static void injectHwcDisplay(DisplayTransactionTest* test) { - if constexpr (kInitPowerMode) { + if constexpr (kExpectSetPowerModeOnce) { + if constexpr (kPowerMode == hal::PowerMode::ON) { + EXPECT_CALL(*test->mComposer, getDisplayCapabilities(HWC_DISPLAY_ID, _)) + .WillOnce(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})), + Return(Error::NONE))); + } + + EXPECT_CALL(*test->mComposer, setPowerMode(HWC_DISPLAY_ID, kPowerMode)) + .WillOnce(Return(Error::NONE)); + } else { EXPECT_CALL(*test->mComposer, getDisplayCapabilities(HWC_DISPLAY_ID, _)) - .WillOnce(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})), - Return(Error::NONE))); + .WillRepeatedly(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})), + Return(Error::NONE))); - EXPECT_CALL(*test->mComposer, setPowerMode(HWC_DISPLAY_ID, INIT_POWER_MODE)) - .WillOnce(Return(Error::NONE)); + EXPECT_CALL(*test->mComposer, setPowerMode(HWC_DISPLAY_ID, _)) + .WillRepeatedly(Return(Error::NONE)); } - injectHwcDisplayWithNoDefaultCapabilities<kInitPowerMode>(test); + + injectHwcDisplayWithNoDefaultCapabilities<kPowerMode>(test); } static std::shared_ptr<compositionengine::Display> injectCompositionDisplay( @@ -447,11 +461,9 @@ struct HwcDisplayVariant { ? IComposerClient::DisplayConnectionType::INTERNAL : IComposerClient::DisplayConnectionType::EXTERNAL; - using ::testing::AtLeast; EXPECT_CALL(*test->mComposer, getDisplayConnectionType(HWC_DISPLAY_ID, _)) - .Times(AtLeast(1)) - .WillRepeatedly(DoAll(SetArgPointee<1>(CONNECTION_TYPE), - Return(hal::V2_4::Error::NONE))); + .WillOnce(DoAll(SetArgPointee<1>(CONNECTION_TYPE), + Return(hal::V2_4::Error::NONE))); } EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_)) diff --git a/services/surfaceflinger/tests/unittests/DualDisplayTransactionTest.h b/services/surfaceflinger/tests/unittests/DualDisplayTransactionTest.h new file mode 100644 index 0000000000..90e716ff1f --- /dev/null +++ b/services/surfaceflinger/tests/unittests/DualDisplayTransactionTest.h @@ -0,0 +1,57 @@ +/* + * Copyright 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. + */ + +#pragma once + +#include "DisplayTransactionTestHelpers.h" + +namespace android { + +template <hal::PowerMode kInnerDisplayPowerMode, hal::PowerMode kOuterDisplayPowerMode, + bool kExpectSetPowerModeOnce = true> +struct DualDisplayTransactionTest : DisplayTransactionTest { + static constexpr bool kWithMockScheduler = false; + DualDisplayTransactionTest() : DisplayTransactionTest(kWithMockScheduler) {} + + void SetUp() override { + injectMockScheduler(kInnerDisplayId); + + { + InnerDisplayVariant::injectHwcDisplay<kInnerDisplayPowerMode, kExpectSetPowerModeOnce>( + this); + + auto injector = InnerDisplayVariant::makeFakeExistingDisplayInjector(this); + injector.setRefreshRateSelector(mFlinger.scheduler()->refreshRateSelector()); + injector.setPowerMode(kInnerDisplayPowerMode); + mInnerDisplay = injector.inject(); + } + { + OuterDisplayVariant::injectHwcDisplay<kOuterDisplayPowerMode, kExpectSetPowerModeOnce>( + this); + + auto injector = OuterDisplayVariant::makeFakeExistingDisplayInjector(this); + injector.setPowerMode(kOuterDisplayPowerMode); + mOuterDisplay = injector.inject(); + } + } + + static inline PhysicalDisplayId kInnerDisplayId = InnerDisplayVariant::DISPLAY_ID::get(); + static inline PhysicalDisplayId kOuterDisplayId = OuterDisplayVariant::DISPLAY_ID::get(); + + sp<DisplayDevice> mInnerDisplay, mOuterDisplay; +}; + +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp index f695b096a7..9e8e306621 100644 --- a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp +++ b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp @@ -24,7 +24,11 @@ #include <gtest/gtest.h> #include <gui/LayerMetadata.h> +#include "Client.h" // temporarily needed for LayerCreationArgs #include "FpsReporter.h" +#include "FrontEnd/LayerCreationArgs.h" +#include "FrontEnd/LayerHierarchy.h" +#include "FrontEnd/LayerLifecycleManager.h" #include "Layer.h" #include "TestableSurfaceFlinger.h" #include "fake/FakeClock.h" @@ -76,7 +80,15 @@ protected: sp<Layer> createBufferStateLayer(LayerMetadata metadata); - TestableSurfaceFlinger mFlinger; + LayerCreationArgs createArgs(uint32_t id, bool canBeRoot, uint32_t parentId, + LayerMetadata metadata); + + void createRootLayer(uint32_t id, LayerMetadata metadata); + + void createLayer(uint32_t id, uint32_t parentId, LayerMetadata metadata); + + frontend::LayerLifecycleManager mLifecycleManager; + mock::FrameTimeline mFrameTimeline = mock::FrameTimeline(std::make_shared<impl::TimeStats>(), 0); @@ -89,8 +101,8 @@ protected: sp<TestableFpsListener> mFpsListener; fake::FakeClock* mClock = new fake::FakeClock(); - sp<FpsReporter> mFpsReporter = sp<FpsReporter>::make(mFrameTimeline, *(mFlinger.flinger()), - std::unique_ptr<Clock>(mClock)); + sp<FpsReporter> mFpsReporter = + sp<FpsReporter>::make(mFrameTimeline, std::unique_ptr<Clock>(mClock)); }; FpsReporterTest::FpsReporterTest() { @@ -98,9 +110,6 @@ FpsReporterTest::FpsReporterTest() { ::testing::UnitTest::GetInstance()->current_test_info(); ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); - mFlinger.setupMockScheduler(); - mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>()); - mFpsListener = sp<TestableFpsListener>::make(); } @@ -110,76 +119,94 @@ FpsReporterTest::~FpsReporterTest() { ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); } -sp<Layer> FpsReporterTest::createBufferStateLayer(LayerMetadata metadata = {}) { +LayerCreationArgs FpsReporterTest::createArgs(uint32_t id, bool canBeRoot, uint32_t parentId, + LayerMetadata metadata) { sp<Client> client; - LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", LAYER_FLAGS, metadata); - return sp<Layer>::make(args); + LayerCreationArgs args(std::make_optional(id)); + args.name = "testlayer"; + args.addToRoot = canBeRoot; + args.flags = LAYER_FLAGS; + args.metadata = metadata; + args.parentId = parentId; + return args; +} + +void FpsReporterTest::createRootLayer(uint32_t id, LayerMetadata metadata = LayerMetadata()) { + std::vector<std::unique_ptr<frontend::RequestedLayerState>> layers; + layers.emplace_back(std::make_unique<frontend::RequestedLayerState>( + createArgs(/*id=*/id, /*canBeRoot=*/true, /*parent=*/UNASSIGNED_LAYER_ID, + /*metadata=*/metadata))); + mLifecycleManager.addLayers(std::move(layers)); +} + +void FpsReporterTest::createLayer(uint32_t id, uint32_t parentId, + LayerMetadata metadata = LayerMetadata()) { + std::vector<std::unique_ptr<frontend::RequestedLayerState>> layers; + layers.emplace_back(std::make_unique<frontend::RequestedLayerState>( + createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/parentId, + /*mirror=*/metadata))); + mLifecycleManager.addLayers(std::move(layers)); } namespace { TEST_F(FpsReporterTest, callsListeners) { - mParent = createBufferStateLayer(); constexpr int32_t kTaskId = 12; LayerMetadata targetMetadata; targetMetadata.setInt32(gui::METADATA_TASK_ID, kTaskId); - mTarget = createBufferStateLayer(targetMetadata); - mChild = createBufferStateLayer(); - mGrandChild = createBufferStateLayer(); - mUnrelated = createBufferStateLayer(); - mParent->addChild(mTarget); - mTarget->addChild(mChild); - mChild->addChild(mGrandChild); - mParent->commitChildList(); - mFlinger.mutableCurrentState().layersSortedByZ.add(mParent); - mFlinger.mutableCurrentState().layersSortedByZ.add(mTarget); - mFlinger.mutableCurrentState().layersSortedByZ.add(mChild); - mFlinger.mutableCurrentState().layersSortedByZ.add(mGrandChild); + + createRootLayer(1, targetMetadata); + createLayer(11, 1); + createLayer(111, 11); + + frontend::LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); float expectedFps = 44.0; - EXPECT_CALL(mFrameTimeline, - computeFps(UnorderedElementsAre(mTarget->getSequence(), mChild->getSequence(), - mGrandChild->getSequence()))) + EXPECT_CALL(mFrameTimeline, computeFps(UnorderedElementsAre(1, 11, 111))) .WillOnce(Return(expectedFps)); mFpsReporter->addListener(mFpsListener, kTaskId); mClock->advanceTime(600ms); - mFpsReporter->dispatchLayerFps(); + mFpsReporter->dispatchLayerFps(hierarchyBuilder.getHierarchy()); EXPECT_EQ(expectedFps, mFpsListener->lastReportedFps); mFpsReporter->removeListener(mFpsListener); Mock::VerifyAndClearExpectations(&mFrameTimeline); EXPECT_CALL(mFrameTimeline, computeFps(_)).Times(0); - mFpsReporter->dispatchLayerFps(); + mFpsReporter->dispatchLayerFps(hierarchyBuilder.getHierarchy()); } TEST_F(FpsReporterTest, rateLimits) { const constexpr int32_t kTaskId = 12; LayerMetadata targetMetadata; targetMetadata.setInt32(gui::METADATA_TASK_ID, kTaskId); - mTarget = createBufferStateLayer(targetMetadata); - mFlinger.mutableCurrentState().layersSortedByZ.add(mTarget); + createRootLayer(1); + createLayer(11, 1, targetMetadata); + + frontend::LayerHierarchyBuilder hierarchyBuilder; + hierarchyBuilder.update(mLifecycleManager); float firstFps = 44.0; float secondFps = 53.0; - EXPECT_CALL(mFrameTimeline, computeFps(UnorderedElementsAre(mTarget->getSequence()))) + EXPECT_CALL(mFrameTimeline, computeFps(UnorderedElementsAre(11))) .WillOnce(Return(firstFps)) .WillOnce(Return(secondFps)); mFpsReporter->addListener(mFpsListener, kTaskId); mClock->advanceTime(600ms); - mFpsReporter->dispatchLayerFps(); + mFpsReporter->dispatchLayerFps(hierarchyBuilder.getHierarchy()); EXPECT_EQ(firstFps, mFpsListener->lastReportedFps); mClock->advanceTime(200ms); - mFpsReporter->dispatchLayerFps(); + mFpsReporter->dispatchLayerFps(hierarchyBuilder.getHierarchy()); EXPECT_EQ(firstFps, mFpsListener->lastReportedFps); mClock->advanceTime(200ms); - mFpsReporter->dispatchLayerFps(); + mFpsReporter->dispatchLayerFps(hierarchyBuilder.getHierarchy()); EXPECT_EQ(firstFps, mFpsListener->lastReportedFps); mClock->advanceTime(200ms); - mFpsReporter->dispatchLayerFps(); + mFpsReporter->dispatchLayerFps(hierarchyBuilder.getHierarchy()); EXPECT_EQ(secondFps, mFpsListener->lastReportedFps); } diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp index e9c4d801a9..249ed4017d 100644 --- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp +++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp @@ -72,7 +72,8 @@ struct MockTokenManager : frametimeline::TokenManager { struct MessageQueueTest : testing::Test { void SetUp() override { EXPECT_CALL(*mVSyncDispatch, registerCallback(_, "sf")).WillOnce(Return(mCallbackToken)); - EXPECT_NO_FATAL_FAILURE(mEventQueue.initVsync(mVSyncDispatch, mTokenManager, kDuration)); + EXPECT_NO_FATAL_FAILURE( + mEventQueue.initVsyncInternal(mVSyncDispatch, mTokenManager, kDuration)); EXPECT_CALL(*mVSyncDispatch, unregisterCallback(mCallbackToken)).Times(1); } diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp index e2e3d7b12b..fba77e997c 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp @@ -37,7 +37,7 @@ protected: ~RefreshRateStatsTest(); void resetStats(Fps fps) { - mRefreshRateStats = std::make_unique<RefreshRateStats>(mTimeStats, fps, PowerMode::OFF); + mRefreshRateStats = std::make_unique<RefreshRateStats>(mTimeStats, fps); } mock::TimeStats mTimeStats; diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp index 82b4ad0b4f..8b16a8a480 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp @@ -21,13 +21,9 @@ #include "mock/DisplayHardware/MockDisplayMode.h" #include "mock/MockDisplayModeSpecs.h" -#include <com_android_graphics_surfaceflinger_flags.h> -#include <common/test/FlagUtils.h> #include <ftl/fake_guard.h> #include <scheduler/Fps.h> -using namespace com::android::graphics::surfaceflinger; - namespace android { namespace { @@ -364,13 +360,6 @@ MATCHER_P(ModeSettledTo, modeId, "") { } TEST_F(DisplayModeSwitchingTest, innerXorOuterDisplay) { - SET_FLAG_FOR_TEST(flags::connected_display, true); - - // For the inner display, this is handled by setupHwcHotplugCallExpectations. - EXPECT_CALL(*mComposer, getDisplayConnectionType(kOuterDisplayHwcId, _)) - .WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayConnectionType::INTERNAL), - Return(hal::V2_4::Error::NONE))); - const auto [innerDisplay, outerDisplay] = injectOuterDisplay(); EXPECT_TRUE(innerDisplay->isPoweredOn()); @@ -440,12 +429,6 @@ TEST_F(DisplayModeSwitchingTest, innerXorOuterDisplay) { } TEST_F(DisplayModeSwitchingTest, innerAndOuterDisplay) { - SET_FLAG_FOR_TEST(flags::connected_display, true); - - // For the inner display, this is handled by setupHwcHotplugCallExpectations. - EXPECT_CALL(*mComposer, getDisplayConnectionType(kOuterDisplayHwcId, _)) - .WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayConnectionType::INTERNAL), - Return(hal::V2_4::Error::NONE))); const auto [innerDisplay, outerDisplay] = injectOuterDisplay(); EXPECT_TRUE(innerDisplay->isPoweredOn()); @@ -529,13 +512,6 @@ TEST_F(DisplayModeSwitchingTest, powerOffDuringModeSet) { } TEST_F(DisplayModeSwitchingTest, powerOffDuringConcurrentModeSet) { - SET_FLAG_FOR_TEST(flags::connected_display, true); - - // For the inner display, this is handled by setupHwcHotplugCallExpectations. - EXPECT_CALL(*mComposer, getDisplayConnectionType(kOuterDisplayHwcId, _)) - .WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayConnectionType::INTERNAL), - Return(hal::V2_4::Error::NONE))); - const auto [innerDisplay, outerDisplay] = injectOuterDisplay(); EXPECT_TRUE(innerDisplay->isPoweredOn()); diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp index 844b96c5cc..93c282963f 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp @@ -17,7 +17,7 @@ #undef LOG_TAG #define LOG_TAG "LibSurfaceFlingerUnittests" -#include "DisplayTransactionTestHelpers.h" +#include "DualDisplayTransactionTest.h" #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -25,35 +25,9 @@ namespace android { namespace { -struct FoldableTest : DisplayTransactionTest { - static constexpr bool kWithMockScheduler = false; - FoldableTest() : DisplayTransactionTest(kWithMockScheduler) {} - - void SetUp() override { - injectMockScheduler(kInnerDisplayId); - - // Inject inner and outer displays with uninitialized power modes. - constexpr bool kInitPowerMode = false; - { - InnerDisplayVariant::injectHwcDisplay<kInitPowerMode>(this); - auto injector = InnerDisplayVariant::makeFakeExistingDisplayInjector(this); - injector.setPowerMode(std::nullopt); - injector.setRefreshRateSelector(mFlinger.scheduler()->refreshRateSelector()); - mInnerDisplay = injector.inject(); - } - { - OuterDisplayVariant::injectHwcDisplay<kInitPowerMode>(this); - auto injector = OuterDisplayVariant::makeFakeExistingDisplayInjector(this); - injector.setPowerMode(std::nullopt); - mOuterDisplay = injector.inject(); - } - } - - static inline PhysicalDisplayId kInnerDisplayId = InnerDisplayVariant::DISPLAY_ID::get(); - static inline PhysicalDisplayId kOuterDisplayId = OuterDisplayVariant::DISPLAY_ID::get(); - - sp<DisplayDevice> mInnerDisplay, mOuterDisplay; -}; +constexpr bool kExpectSetPowerModeOnce = false; +struct FoldableTest : DualDisplayTransactionTest<hal::PowerMode::OFF, hal::PowerMode::OFF, + kExpectSetPowerModeOnce> {}; TEST_F(FoldableTest, promotesPacesetterOnBoot) { // When the device boots, the inner display should be the pacesetter. diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp index 1583f64c0a..eaf468432c 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp @@ -17,66 +17,49 @@ #undef LOG_TAG #define LOG_TAG "LibSurfaceFlingerUnittests" -#include "DisplayTransactionTestHelpers.h" +#include "DualDisplayTransactionTest.h" namespace android { namespace { -class InitializeDisplaysTest : public DisplayTransactionTest {}; +constexpr bool kExpectSetPowerModeOnce = false; +struct InitializeDisplaysTest : DualDisplayTransactionTest<hal::PowerMode::OFF, hal::PowerMode::OFF, + kExpectSetPowerModeOnce> {}; -TEST_F(InitializeDisplaysTest, commitsPrimaryDisplay) { - using Case = SimplePrimaryDisplayCase; - - // -------------------------------------------------------------------- - // Preconditions - - // A primary display is set up - Case::Display::injectHwcDisplay(this); - auto primaryDisplay = Case::Display::makeFakeExistingDisplayInjector(this); - primaryDisplay.inject(); - - // -------------------------------------------------------------------- - // Call Expectations - - // We expect a call to get the active display config. - Case::Display::setupHwcGetActiveConfigCallExpectations(this); - - // We expect a scheduled commit for the display transaction. - EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); +TEST_F(InitializeDisplaysTest, initializesDisplays) { + // Scheduled by the display transaction, and by powering on each display. + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(3); EXPECT_CALL(static_cast<mock::VSyncTracker&>( mFlinger.scheduler()->getVsyncSchedule()->getTracker()), nextAnticipatedVSyncTimeFrom(_, _)) .WillRepeatedly(Return(0)); - // -------------------------------------------------------------------- - // Invocation - FTL_FAKE_GUARD(kMainThreadContext, mFlinger.initializeDisplays()); - // -------------------------------------------------------------------- - // Postconditions + for (const auto& display : {mInnerDisplay, mOuterDisplay}) { + const auto token = display->getDisplayToken().promote(); + ASSERT_TRUE(token); + + ASSERT_TRUE(hasCurrentDisplayState(token)); + const auto& state = getCurrentDisplayState(token); - // The primary display should have a current state - ASSERT_TRUE(hasCurrentDisplayState(primaryDisplay.token())); - const auto& primaryDisplayState = getCurrentDisplayState(primaryDisplay.token()); + const ui::LayerStack expectedLayerStack = display == mInnerDisplay + ? ui::DEFAULT_LAYER_STACK + : ui::LayerStack::fromValue(ui::DEFAULT_LAYER_STACK.id + 1); - // The primary display state should be reset - EXPECT_EQ(ui::DEFAULT_LAYER_STACK, primaryDisplayState.layerStack); - EXPECT_EQ(ui::ROTATION_0, primaryDisplayState.orientation); - EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.orientedDisplaySpaceRect); - EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.layerStackSpaceRect); + EXPECT_EQ(expectedLayerStack, state.layerStack); + EXPECT_EQ(ui::ROTATION_0, state.orientation); + EXPECT_EQ(Rect::INVALID_RECT, state.orientedDisplaySpaceRect); + EXPECT_EQ(Rect::INVALID_RECT, state.layerStackSpaceRect); - // The width and height should both be zero - EXPECT_EQ(0u, primaryDisplayState.width); - EXPECT_EQ(0u, primaryDisplayState.height); + EXPECT_EQ(0u, state.width); + EXPECT_EQ(0u, state.height); - // The display should be set to PowerMode::ON - ASSERT_TRUE(hasDisplayDevice(primaryDisplay.token())); - auto displayDevice = primaryDisplay.mutableDisplayDevice(); - EXPECT_EQ(PowerMode::ON, displayDevice->getPowerMode()); + ASSERT_TRUE(hasDisplayDevice(token)); + EXPECT_EQ(PowerMode::ON, getDisplayDevice(token).getPowerMode()); + } - // The display transaction needed flag should be set. EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded)); } diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index f00eacce8b..9ef0749dc6 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -268,7 +268,7 @@ public: vsyncTrackerCallback); } - mScheduler->initVsync(mScheduler->getVsyncSchedule()->getDispatch(), *mTokenManager, 0ms); + mScheduler->initVsync(*mTokenManager, 0ms); mScheduler->mutableAppConnectionHandle() = mScheduler->createConnection(std::move(appEventThread)); @@ -750,7 +750,6 @@ public: static constexpr int32_t DEFAULT_CONFIG_GROUP = 7; static constexpr int32_t DEFAULT_DPI = 320; static constexpr hal::HWConfigId DEFAULT_ACTIVE_CONFIG = 0; - static constexpr hal::PowerMode DEFAULT_POWER_MODE = hal::PowerMode::ON; FakeHwcDisplayInjector(HalDisplayId displayId, hal::DisplayType hwcDisplayType, bool isPrimary) @@ -793,7 +792,7 @@ public: return *this; } - auto& setPowerMode(std::optional<hal::PowerMode> mode) { + auto& setPowerMode(hal::PowerMode mode) { mPowerMode = mode; return *this; } @@ -817,9 +816,7 @@ public: mHwcDisplayType); display->mutableIsConnected() = true; - if (mPowerMode) { - display->setPowerMode(*mPowerMode); - } + display->setPowerMode(mPowerMode); flinger->mutableHwcDisplayData()[mDisplayId].hwcDisplay = std::move(display); @@ -885,7 +882,7 @@ public: int32_t mDpiY = DEFAULT_DPI; int32_t mConfigGroup = DEFAULT_CONFIG_GROUP; hal::HWConfigId mActiveConfig = DEFAULT_ACTIVE_CONFIG; - std::optional<hal::PowerMode> mPowerMode = DEFAULT_POWER_MODE; + hal::PowerMode mPowerMode = hal::PowerMode::ON; const std::unordered_set<aidl::android::hardware::graphics::composer3::Capability>* mCapabilities = nullptr; }; @@ -962,7 +959,7 @@ public: return *this; } - auto& setPowerMode(std::optional<hal::PowerMode> mode) { + auto& setPowerMode(hal::PowerMode mode) { mCreationArgs.initialPowerMode = mode; return *this; } diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h index b17c8adf6c..ae41e7ea75 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h @@ -34,8 +34,6 @@ class IPower; namespace android::Hwc2::mock { -using aidl::android::hardware::power::Boost; -using aidl::android::hardware::power::Mode; using android::power::HalResult; class MockPowerHalController : public power::PowerHalController { @@ -43,12 +41,22 @@ public: MockPowerHalController(); ~MockPowerHalController() override; MOCK_METHOD(void, init, (), (override)); - MOCK_METHOD(HalResult<void>, setBoost, (Boost, int32_t), (override)); - MOCK_METHOD(HalResult<void>, setMode, (Mode, bool), (override)); + MOCK_METHOD(HalResult<void>, setBoost, (aidl::android::hardware::power::Boost, int32_t), + (override)); + MOCK_METHOD(HalResult<void>, setMode, (aidl::android::hardware::power::Mode, bool), (override)); MOCK_METHOD(HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>, createHintSession, (int32_t, int32_t, const std::vector<int32_t>&, int64_t), (override)); + MOCK_METHOD(HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>, + createHintSessionWithConfig, + (int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, + int64_t durationNanos, aidl::android::hardware::power::SessionTag tag, + aidl::android::hardware::power::SessionConfig* config), + (override)); MOCK_METHOD(HalResult<int64_t>, getHintSessionPreferredRate, (), (override)); + MOCK_METHOD(HalResult<aidl::android::hardware::power::ChannelConfig>, getSessionChannel, + (int tgid, int uid), (override)); + MOCK_METHOD(HalResult<void>, closeSessionChannel, (int tgid, int uid), (override)); }; } // namespace android::Hwc2::mock
\ No newline at end of file |