Merge "Add captureLayersSync function" into main
diff --git a/aidl/gui/android/view/Surface.aidl b/aidl/gui/android/view/Surface.aidl
index bb3faaf..6686717 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 ae56cb0..07908ba 100644
--- a/cmds/servicemanager/main.cpp
+++ b/cmds/servicemanager/main.cpp
@@ -40,15 +40,12 @@
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 @@
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 @@
}
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 @@
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 2ac7d4d..6420cd0 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 30200c7..08d339b 100644
--- a/include/android/performance_hint.h
+++ b/include/android/performance_hint.h
@@ -240,7 +240,7 @@
* 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 @@
*
* @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 @@
* 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 @@
* 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 9e426d3..c50bc4a 100644
--- a/include/powermanager/PowerHalController.h
+++ b/include/powermanager/PowerHalController.h
@@ -62,7 +62,15 @@
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 @@
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 4e4a1b0..e2da014 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
};
// 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 @@
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 @@
}; // 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 e1dc791..6b8e824 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -416,26 +416,36 @@
};
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 @@
// 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 576d9d5..0c850fe 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -97,3 +97,10 @@
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 aab7df0..22ad834 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 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 0000000..25fea80
--- /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 4525a42..5689f7d 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 3e1ac33..233134d 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -28,28 +28,28 @@
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 a7f1df9..101f519 100644
--- a/libs/renderengine/benchmark/RenderEngineBench.cpp
+++ b/libs/renderengine/benchmark/RenderEngineBench.cpp
@@ -30,46 +30,6 @@
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 @@
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 @@
.setPrecacheToneMapperShaderOnly(false)
.setSupportsBackgroundBlur(true)
.setContextPriority(RenderEngine::ContextPriority::REALTIME)
- .setRenderEngineType(type)
+ .setThreaded(threaded)
+ .setGraphicsApi(graphicsApi)
.build();
return RenderEngine::create(args);
}
@@ -214,8 +176,11 @@
// 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 @@
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 818d035..7047358 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 @@
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 @@
// 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 @@
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 @@
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 @@
bool precacheToneMapperShaderOnly;
bool supportsBackgroundBlur;
RenderEngine::ContextPriority contextPriority;
- RenderEngine::RenderEngineType renderEngineType;
+ RenderEngine::Threaded threaded;
+ RenderEngine::GraphicsApi graphicsApi;
struct Builder;
@@ -261,14 +264,16 @@
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 @@
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 @@
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 d688b51..fea4129 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -268,7 +268,7 @@
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 3729be6..fc84bbf 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -269,9 +269,9 @@
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::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 ac134af..e88d44c 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -59,7 +59,7 @@
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 ba20d1f..3af85c0 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
@@ -596,7 +596,7 @@
}
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 11d4fde..4c18704 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -106,92 +106,46 @@
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;
+ 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))
+ .setImageCacheSize(1)
+ .setEnableProtectedContext(false)
+ .setPrecacheToneMapperShaderOnly(false)
+ .setSupportsBackgroundBlur(true)
+ .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
+ .setThreaded(renderengine::RenderEngine::Threaded::NO)
+ .setGraphicsApi(graphicsApi())
+ .build();
+ return renderengine::RenderEngine::create(reCreationArgs);
+ }
};
class SkiaVkRenderEngineFactory : public RenderEngineFactory {
public:
std::string name() override { return "SkiaVkRenderEngineFactory"; }
- renderengine::RenderEngine::RenderEngineType type() {
- return renderengine::RenderEngine::RenderEngineType::SKIA_VK;
+ renderengine::RenderEngine::GraphicsApi graphicsApi() override {
+ return renderengine::RenderEngine::GraphicsApi::VK;
}
- std::unique_ptr<renderengine::RenderEngine> createRenderEngine() override {
- std::unique_ptr<renderengine::RenderEngine> re = createSkiaVkRenderEngine();
- return re;
- }
-
- std::unique_ptr<renderengine::skia::SkiaVkRenderEngine> createSkiaVkRenderEngine() {
- 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::SkiaVkRenderEngine::create(reCreationArgs);
- }
-
- bool typeSupported() override {
+ bool apiSupported() override {
return skia::SkiaVkRenderEngine::canSupportSkiaVkRenderEngine();
}
- void skip() { GTEST_SKIP(); }
};
class SkiaGLESRenderEngineFactory : public RenderEngineFactory {
public:
std::string name() override { return "SkiaGLRenderEngineFactory"; }
- renderengine::RenderEngine::RenderEngineType type() {
- return renderengine::RenderEngine::RenderEngineType::SKIA_GL;
+ renderengine::RenderEngine::GraphicsApi graphicsApi() {
+ return renderengine::RenderEngine::GraphicsApi::GL;
}
- 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 typeSupported() override { return true; }
-};
-
-class SkiaGLESCMRenderEngineFactory : public RenderEngineFactory {
-public:
- std::string name() override { return "SkiaGLCMRenderEngineFactory"; }
-
- renderengine::RenderEngine::RenderEngineType type() {
- return renderengine::RenderEngine::RenderEngineType::SKIA_GL;
- }
-
- 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 typeSupported() override { return true; }
+ bool apiSupported() override { return true; }
};
class RenderEngineTest : public ::testing::TestWithParam<std::shared_ptr<RenderEngineFactory>> {
@@ -1526,7 +1480,7 @@
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_fillRedBufferAndEmptyBuffer) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1561,7 +1515,7 @@
}
TEST_P(RenderEngineTest, drawLayers_withoutBuffers_withColorTransform) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1595,7 +1549,7 @@
}
TEST_P(RenderEngineTest, drawLayers_nullOutputBuffer) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1616,7 +1570,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_colorSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1624,7 +1578,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_colorSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1632,7 +1586,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_colorSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1640,7 +1594,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_colorSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1648,7 +1602,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_colorSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1656,7 +1610,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_colorSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1664,7 +1618,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_colorSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1672,7 +1626,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_colorSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1680,7 +1634,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_colorSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1688,7 +1642,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_colorSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1696,7 +1650,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_colorSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1706,7 +1660,7 @@
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_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_fillBufferRoundedCorners_colorSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1734,7 +1688,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_colorSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1742,7 +1696,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_colorSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1750,7 +1704,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_colorSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1758,7 +1712,7 @@
}
TEST_P(RenderEngineTest, drawLayers_overlayCorners_colorSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1766,7 +1720,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_opaqueBufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1774,7 +1728,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_opaqueBufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1782,7 +1736,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_opaqueBufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1790,7 +1744,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_opaqueBufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1798,7 +1752,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_opaqueBufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1806,7 +1760,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_opaqueBufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1814,7 +1768,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_opaqueBufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1822,7 +1776,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_opaqueBufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1830,7 +1784,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_opaqueBufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1838,7 +1792,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_opaqueBufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1846,7 +1800,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_opaqueBufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1856,7 +1810,7 @@
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_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_fillBufferRoundedCorners_opaqueBufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1884,7 +1838,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_opaqueBufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1892,7 +1846,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_opaqueBufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1900,7 +1854,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_opaqueBufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1908,7 +1862,7 @@
}
TEST_P(RenderEngineTest, drawLayers_overlayCorners_opaqueBufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1916,7 +1870,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_bufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1924,7 +1878,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_bufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1932,7 +1886,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_bufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1940,7 +1894,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_bufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1948,7 +1902,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_bufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1956,7 +1910,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_bufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1964,7 +1918,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_bufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1972,7 +1926,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_bufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1980,7 +1934,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_bufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1988,7 +1942,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_bufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -1996,7 +1950,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_bufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2006,7 +1960,7 @@
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_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_fillBufferRoundedCorners_bufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2034,7 +1988,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_bufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2042,7 +1996,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_bufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2050,7 +2004,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_bufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2058,7 +2012,7 @@
}
TEST_P(RenderEngineTest, drawLayers_overlayCorners_bufferSource) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2066,7 +2020,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferTextureTransform) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2074,7 +2028,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBuffer_premultipliesAlpha) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2082,7 +2036,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBuffer_withoutPremultiplyingAlpha) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2090,7 +2044,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillShadow_castsWithoutCasterLayer) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2108,7 +2062,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2131,7 +2085,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2155,7 +2109,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2180,7 +2134,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillShadow_casterWithRoundedCorner) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2206,7 +2160,7 @@
}
TEST_P(RenderEngineTest, drawLayers_fillShadow_translucentCasterWithAlpha) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2235,7 +2189,7 @@
}
TEST_P(RenderEngineTest, cleanupPostRender_cleansUpOnce) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2271,7 +2225,7 @@
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, testRoundedCornersCrop) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2332,7 +2286,7 @@
}
TEST_P(RenderEngineTest, testRoundedCornersParentCrop) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2380,7 +2334,7 @@
}
TEST_P(RenderEngineTest, testRoundedCornersParentCropSmallBounds) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2417,7 +2371,7 @@
}
TEST_P(RenderEngineTest, testRoundedCornersXY) {
- if (GetParam()->type() != renderengine::RenderEngine::RenderEngineType::SKIA_GL) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
@@ -2460,7 +2414,7 @@
}
TEST_P(RenderEngineTest, testClear) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2492,7 +2446,7 @@
}
TEST_P(RenderEngineTest, testDisableBlendingBuffer) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2543,7 +2497,7 @@
}
TEST_P(RenderEngineTest, testBorder) {
- if (GetParam()->type() != renderengine::RenderEngine::RenderEngineType::SKIA_GL) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
@@ -2588,7 +2542,7 @@
}
TEST_P(RenderEngineTest, testDimming) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2663,7 +2617,7 @@
}
TEST_P(RenderEngineTest, testDimming_inGammaSpace) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2741,7 +2695,7 @@
}
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_deviceHandles) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2868,7 +2822,7 @@
}
TEST_P(RenderEngineTest, testDimming_withoutTargetLuminance) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2922,7 +2876,7 @@
}
TEST_P(RenderEngineTest, test_isOpaque) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -2972,7 +2926,7 @@
}
TEST_P(RenderEngineTest, test_tonemapPQMatches) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
@@ -2989,7 +2943,7 @@
}
TEST_P(RenderEngineTest, test_tonemapHLGMatches) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
@@ -3006,7 +2960,7 @@
}
TEST_P(RenderEngineTest, r8_behaves_as_mask) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -3066,7 +3020,7 @@
}
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_when_device_handles) {
- if (!GetParam()->typeSupported()) {
+ if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
initializeRenderEngine();
@@ -3199,7 +3153,7 @@
}
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 1b9adba..d56dbb2 100644
--- a/libs/renderengine/tests/RenderEngineThreadedTest.cpp
+++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
@@ -35,8 +35,7 @@
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 f58f543..f4cebc0 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -33,13 +33,12 @@
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 3f1e67f..d440c96 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -37,10 +37,9 @@
*/
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 72c6f1a..1ada5e5 100644
--- a/services/inputflinger/InputFilter.cpp
+++ b/services/inputflinger/InputFilter.cpp
@@ -118,6 +118,15 @@
}
}
+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 153d29d..4ddc9f4 100644
--- a/services/inputflinger/InputFilter.h
+++ b/services/inputflinger/InputFilter.h
@@ -35,6 +35,7 @@
*/
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 @@
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 a8759b7..6c31442 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 @@
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 @@
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 31c160a..a74955b 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 @@
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 @@
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 4571ef4..3ac4285 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -109,7 +109,9 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 f46419e..6aab3aa 100644
--- a/services/inputflinger/PointerChoreographer.h
+++ b/services/inputflinger/PointerChoreographer.h
@@ -67,6 +67,11 @@
*/
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 @@
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 @@
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 @@
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 2921d30..994d1c4 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 @@
/** 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 0000000..2f6b8fc
--- /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 38b1612..9984a6a 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 @@
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 1085c94..c349a58 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 @@
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 @@
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 @@
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 @@
if (traceBackend) {
// TODO: Create input tracer instance.
}
+
+ mLastUserActivityTimes.fill(0);
}
InputDispatcher::~InputDispatcher() {
@@ -3140,6 +3164,21 @@
// 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 @@
}
}
- 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 @@
}
return;
}
- if (MotionEvent::isTouchEvent(motionEntry.source, motionEntry.action)) {
- eventType = USER_ACTIVITY_EVENT_TOUCH;
- }
break;
}
case EventEntry::Type::KEY: {
@@ -3190,7 +3225,6 @@
return;
}
- eventType = USER_ACTIVITY_EVENT_BUTTON;
break;
}
default: {
@@ -3200,6 +3234,7 @@
}
}
+ mLastUserActivityTimes[eventType] = eventEntry.eventTime;
auto command = [this, eventTime = eventEntry.eventTime, eventType, displayId]()
REQUIRES(mLock) {
scoped_unlock unlock(mLock);
@@ -5292,6 +5327,14 @@
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 1e11b27..e635852 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 @@
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 @@
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 001dc6c..c8f3d05 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -101,6 +101,9 @@
*/
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 40359a4..9abfef1 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -131,10 +131,10 @@
// 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 @@
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 4cebd64..d207ed1 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -159,15 +159,15 @@
out.push_back(NotifyDeviceResetArgs(getContext()->getNextId(), when, getDeviceId()));
}
- if (!changes.any() || changes.test(InputReaderConfiguration::Change::POINTER_SPEED) ||
- configurePointerCapture) {
- configureOnChangePointerSpeed(readerConfig);
- }
-
if (!changes.any() || changes.test(InputReaderConfiguration::Change::DISPLAY_INFO) ||
configurePointerCapture) {
configureOnChangeDisplayInfo(readerConfig);
}
+
+ if (!changes.any() || changes.test(InputReaderConfiguration::Change::POINTER_SPEED) ||
+ configurePointerCapture) {
+ configureOnChangePointerSpeed(readerConfig);
+ }
return out;
}
@@ -510,7 +510,8 @@
} 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 f79219f..9ec02a6 100644
--- a/services/inputflinger/reader/mapper/SlopController.cpp
+++ b/services/inputflinger/reader/mapper/SlopController.cpp
@@ -54,11 +54,13 @@
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 2803805..9e6dbe4 100644
--- a/services/inputflinger/rust/Android.bp
+++ b/services/inputflinger/rust/Android.bp
@@ -42,6 +42,7 @@
"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 894b881..2d5039a 100644
--- a/services/inputflinger/rust/bounce_keys_filter.rs
+++ b/services/inputflinger/rust/bounce_keys_filter.rs
@@ -118,6 +118,10 @@
}
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 e94a71f..a544fa3 100644
--- a/services/inputflinger/rust/input_filter.rs
+++ b/services/inputflinger/rust/input_filter.rs
@@ -22,11 +22,14 @@
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 @@
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 @@
// 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 @@
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 @@
}
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");
+ {
+ 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;
}
- 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;
Result::Ok(())
}
}
@@ -132,27 +153,51 @@
fn notify_devices_changed(&mut self, _device_infos: &[DeviceInfo]) {
// do nothing
}
+
+ fn destroy(&mut self) {
+ // do nothing
+ }
}
-pub struct ModifierStateListener {
- callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>,
-}
+/// 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 @@
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 @@
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 @@
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 @@
struct TestFilterInner {
is_device_changed_called: bool,
last_event: Option<KeyEvent>,
+ is_destroy_called: bool,
}
#[derive(Default, Clone)]
@@ -296,6 +369,10 @@
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 @@
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 @@
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 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 @@
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 0000000..2d503ae
--- /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 fa16898..da72134 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 0000000..01165b5
--- /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 da581b8..6c2277c 100644
--- a/services/inputflinger/rust/sticky_keys_filter.rs
+++ b/services/inputflinger/rust/sticky_keys_filter.rs
@@ -142,6 +142,10 @@
}
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 e104543..7b793d8 100644
--- a/services/inputflinger/tests/CursorInputMapper_test.cpp
+++ b/services/inputflinger/tests/CursorInputMapper_test.cpp
@@ -1324,6 +1324,42 @@
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 b6ae0b3..f1f4a61 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -156,6 +156,20 @@
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 @@
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 @@
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 @@
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 @@
/*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 1ac043c..e9e5061 100644
--- a/services/inputflinger/tests/PointerChoreographer_test.cpp
+++ b/services/inputflinger/tests/PointerChoreographer_test.cpp
@@ -1557,4 +1557,122 @@
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 9a23c84..bc178bc 100644
--- a/services/powermanager/PowerHalController.cpp
+++ b/services/powermanager/PowerHalController.cpp
@@ -80,7 +80,7 @@
// 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 @@
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 @@
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 76afbfc..1009100 100644
--- a/services/powermanager/PowerHalWrapper.cpp
+++ b/services/powermanager/PowerHalWrapper.cpp
@@ -42,37 +42,58 @@
// -------------------------------------------------------------------------------------------------
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 @@
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 @@
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 @@
// 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 @@
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 @@
// 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 @@
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 @@
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 @@
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 3d2cf29..a720296 100644
--- a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
+++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
@@ -256,6 +256,23 @@
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 @@
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 c71c517..39748b8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
@@ -60,7 +60,7 @@
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 c53b461..2dc9a1a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -73,7 +73,7 @@
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 d907bf5..6428d08 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -204,13 +204,12 @@
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 c0e77bb..d07cdf5 100644
--- a/services/surfaceflinger/Display/DisplayModeRequest.h
+++ b/services/surfaceflinger/Display/DisplayModeRequest.h
@@ -27,9 +27,6 @@
// 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 b96264b..4f81482 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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
}
}
-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 @@
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 46534de..4ab6321 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -173,8 +173,8 @@
/* ------------------------------------------------------------------------
* 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 @@
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 @@
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 @@
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 64a8ae7..17f6f31 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -330,7 +330,11 @@
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 db66f5b..704ece5 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 @@
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 c4ff9cc..12ab2c2 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -291,6 +291,7 @@
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 155cf4d..a47348f 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 @@
}
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 438b1aa..01f1e07 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 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 @@
};
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 cf8b3bf..a145e59 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -75,9 +75,9 @@
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 @@
}
// 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 a523147..edb424b 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -65,8 +65,9 @@
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 @@
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 67e1b9c..d51af9a 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateStats.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -49,10 +49,10 @@
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 27ca17f..c76d4bd 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -77,8 +77,7 @@
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 @@
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 @@
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 f62f1ba..9912622 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -131,7 +131,7 @@
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 db6a187..3491600 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
@@ -89,8 +89,12 @@
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 722ea0b..6f656d2 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.h
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h
@@ -63,7 +63,8 @@
// 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 0154060..f0f7a87 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 a9abcaf..29711af 100644
--- a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
+++ b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
@@ -38,7 +38,9 @@
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 178828a..47e7474 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -799,22 +799,26 @@
}));
}
-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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
{
Mutex::Autolock lock(mStateLock);
if (mFpsReporter) {
- mFpsReporter->dispatchLayerFps();
+ mFpsReporter->dispatchLayerFps(mLayerHierarchyBuilder.getHierarchy());
}
if (mTunnelModeEnabledReporter) {
@@ -3295,88 +3295,14 @@
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 @@
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 @@
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 @@
}
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 @@
/* 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::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 @@
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 @@
{
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 @@
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 @@
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 @@
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 @@
} 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 @@
//
// 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 @@
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 24a1a10..c8e2a4d 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -719,7 +719,7 @@
// 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 @@
* 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 a27e100..255b517 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -128,6 +128,7 @@
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(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 c01465b..15938c0 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -68,6 +68,7 @@
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 8a5500f..a6b12d0 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -405,8 +405,8 @@
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 @@
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 f1bb231..b17b529 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 @@
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 99fef7e..387d2f2 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -347,7 +347,6 @@
// 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 @@
}
// 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 @@
.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) {
- EXPECT_CALL(*test->mComposer, getDisplayCapabilities(HWC_DISPLAY_ID, _))
- .WillOnce(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})),
- Return(Error::NONE)));
+ 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, INIT_POWER_MODE))
+ EXPECT_CALL(*test->mComposer, setPowerMode(HWC_DISPLAY_ID, kPowerMode))
.WillOnce(Return(Error::NONE));
+ } else {
+ EXPECT_CALL(*test->mComposer, getDisplayCapabilities(HWC_DISPLAY_ID, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})),
+ 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 @@
? 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 0000000..90e716f
--- /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 f695b09..9e8e306 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 @@
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 @@
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 @@
::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 @@
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 e9c4d80..249ed40 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -72,7 +72,8 @@
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 e2e3d7b..fba77e9 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
@@ -37,7 +37,7 @@
~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 82b4ad0..8b16a8a 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 @@
}
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, 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, 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 844b96c..93c2829 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 1583f64..eaf4684 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);
- // The primary display should have a current state
- ASSERT_TRUE(hasCurrentDisplayState(primaryDisplay.token()));
- const auto& primaryDisplayState = getCurrentDisplayState(primaryDisplay.token());
+ ASSERT_TRUE(hasCurrentDisplayState(token));
+ const auto& state = getCurrentDisplayState(token);
- // 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);
+ const ui::LayerStack expectedLayerStack = display == mInnerDisplay
+ ? ui::DEFAULT_LAYER_STACK
+ : ui::LayerStack::fromValue(ui::DEFAULT_LAYER_STACK.id + 1);
- // The width and height should both be zero
- EXPECT_EQ(0u, primaryDisplayState.width);
- EXPECT_EQ(0u, primaryDisplayState.height);
+ 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 display should be set to PowerMode::ON
- ASSERT_TRUE(hasDisplayDevice(primaryDisplay.token()));
- auto displayDevice = primaryDisplay.mutableDisplayDevice();
- EXPECT_EQ(PowerMode::ON, displayDevice->getPowerMode());
+ EXPECT_EQ(0u, state.width);
+ EXPECT_EQ(0u, state.height);
- // The display transaction needed flag should be set.
+ ASSERT_TRUE(hasDisplayDevice(token));
+ EXPECT_EQ(PowerMode::ON, getDisplayDevice(token).getPowerMode());
+ }
+
EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
}
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index f00eacc..9ef0749 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -268,7 +268,7 @@
vsyncTrackerCallback);
}
- mScheduler->initVsync(mScheduler->getVsyncSchedule()->getDispatch(), *mTokenManager, 0ms);
+ mScheduler->initVsync(*mTokenManager, 0ms);
mScheduler->mutableAppConnectionHandle() =
mScheduler->createConnection(std::move(appEventThread));
@@ -750,7 +750,6 @@
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 @@
return *this;
}
- auto& setPowerMode(std::optional<hal::PowerMode> mode) {
+ auto& setPowerMode(hal::PowerMode mode) {
mPowerMode = mode;
return *this;
}
@@ -817,9 +816,7 @@
mHwcDisplayType);
display->mutableIsConnected() = true;
- if (mPowerMode) {
- display->setPowerMode(*mPowerMode);
- }
+ display->setPowerMode(mPowerMode);
flinger->mutableHwcDisplayData()[mDisplayId].hwcDisplay = std::move(display);
@@ -885,7 +882,7 @@
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 @@
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 b17c8ad..ae41e7e 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h
@@ -34,8 +34,6 @@
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 @@
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