Merge "InputTracer: Add tests for perfetto backend" into main
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index 341fabb..de0aafa 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -37,9 +37,6 @@
name: "libdumpstateutil",
defaults: ["dumpstate_cflag_defaults"],
vendor_available: true,
- vndk: {
- enabled: true,
- },
srcs: [
"DumpstateInternal.cpp",
"DumpstateUtil.cpp",
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index fb1419f..dc9383a 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -307,7 +307,7 @@
/**
* Parameter for ASurfaceTransaction_setVisibility().
*/
-enum {
+enum ASurfaceTransactionVisibility : int8_t {
ASURFACE_TRANSACTION_VISIBILITY_HIDE = 0,
ASURFACE_TRANSACTION_VISIBILITY_SHOW = 1,
};
@@ -319,7 +319,8 @@
* Available since API level 29.
*/
void ASurfaceTransaction_setVisibility(ASurfaceTransaction* transaction,
- ASurfaceControl* surface_control, int8_t visibility)
+ ASurfaceControl* surface_control,
+ enum ASurfaceTransactionVisibility visibility)
__INTRODUCED_IN(29);
/**
@@ -362,7 +363,7 @@
*/
void ASurfaceTransaction_setColor(ASurfaceTransaction* transaction,
ASurfaceControl* surface_control, float r, float g, float b,
- float alpha, ADataSpace dataspace)
+ float alpha, enum ADataSpace dataspace)
__INTRODUCED_IN(29);
/**
@@ -440,7 +441,7 @@
/**
* Parameter for ASurfaceTransaction_setBufferTransparency().
*/
-enum {
+enum ASurfaceTransactionTransparency : int8_t {
ASURFACE_TRANSACTION_TRANSPARENCY_TRANSPARENT = 0,
ASURFACE_TRANSACTION_TRANSPARENCY_TRANSLUCENT = 1,
ASURFACE_TRANSACTION_TRANSPARENCY_OPAQUE = 2,
@@ -454,7 +455,7 @@
*/
void ASurfaceTransaction_setBufferTransparency(ASurfaceTransaction* transaction,
ASurfaceControl* surface_control,
- int8_t transparency)
+ enum ASurfaceTransactionTransparency transparency)
__INTRODUCED_IN(29);
/**
@@ -501,7 +502,7 @@
* Available since API level 29.
*/
void ASurfaceTransaction_setBufferDataSpace(ASurfaceTransaction* transaction,
- ASurfaceControl* surface_control, ADataSpace data_space)
+ ASurfaceControl* surface_control, enum ADataSpace data_space)
__INTRODUCED_IN(29);
/**
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index eec12e4..9c24dc5 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -358,9 +358,6 @@
// for vndbinder
vendor_available: true,
- vndk: {
- enabled: true,
- },
recovery_available: true,
double_loadable: true,
// TODO(b/153609531): remove when no longer needed.
@@ -669,6 +666,7 @@
"//packages/modules/Virtualization:__subpackages__",
"//device/google/cuttlefish/shared/minidroid:__subpackages__",
"//system/software_defined_vehicle:__subpackages__",
+ "//visibility:any_system_partition",
],
}
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index 30dbddd..2a8a353 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -50,6 +50,7 @@
],
cflags: [
+ "-DBINDER_WITH_KERNEL_IPC",
"-Wall",
"-Wextra",
"-Werror",
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index e2ede3f..34a86f2 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -24,6 +24,7 @@
#include <private/android_filesystem_config.h>
#endif
+#include "../BuildFlags.h"
#include "ibinder_internal.h"
#include "parcel_internal.h"
#include "status_internal.h"
@@ -211,6 +212,12 @@
binder_status_t status = getClass()->onTransact(this, code, &in, &out);
return PruneStatusT(status);
} else if (code == SHELL_COMMAND_TRANSACTION && getClass()->handleShellCommand != nullptr) {
+ if constexpr (!android::kEnableKernelIpc) {
+ // Non-IPC builds do not have getCallingUid(),
+ // so we have no way of authenticating the caller
+ return STATUS_PERMISSION_DENIED;
+ }
+
int in = data.readFileDescriptor();
int out = data.readFileDescriptor();
int err = data.readFileDescriptor();
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 16049f2..0540ed3 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -101,6 +101,8 @@
mod parcel;
mod proxy;
#[cfg(not(target_os = "trusty"))]
+mod service;
+#[cfg(not(target_os = "trusty"))]
mod state;
use binder_ndk_sys as sys;
@@ -108,14 +110,13 @@
pub use crate::binder_async::{BinderAsyncPool, BoxFuture};
pub use binder::{BinderFeatures, FromIBinder, IBinder, Interface, Strong, Weak};
pub use error::{ExceptionCode, IntoBinderResult, Status, StatusCode};
-pub use native::{
- add_service, force_lazy_services_persist, is_handling_transaction, register_lazy_service,
- LazyServiceGuard,
-};
pub use parcel::{ParcelFileDescriptor, Parcelable, ParcelableHolder};
-pub use proxy::{
- get_declared_instances, get_interface, get_service, is_declared, wait_for_interface,
- wait_for_service, DeathRecipient, SpIBinder, WpIBinder,
+pub use proxy::{DeathRecipient, SpIBinder, WpIBinder};
+#[cfg(not(target_os = "trusty"))]
+pub use service::{
+ add_service, force_lazy_services_persist, get_declared_instances, get_interface, get_service,
+ is_declared, is_handling_transaction, register_lazy_service, wait_for_interface,
+ wait_for_service, LazyServiceGuard,
};
#[cfg(not(target_os = "trusty"))]
pub use state::{ProcessState, ThreadState};
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index 8ae010e..da9d7dc 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -23,12 +23,11 @@
use crate::sys;
use std::convert::TryFrom;
-use std::ffi::{c_void, CStr, CString};
+use std::ffi::{c_void, CStr};
use std::io::Write;
use std::mem::ManuallyDrop;
use std::ops::Deref;
use std::os::raw::c_char;
-use std::sync::Mutex;
/// Rust wrapper around Binder remotable objects.
///
@@ -462,110 +461,6 @@
}
}
-/// Register a new service with the default service manager.
-///
-/// Registers the given binder object with the given identifier. If successful,
-/// this service can then be retrieved using that identifier.
-///
-/// This function will panic if the identifier contains a 0 byte (NUL).
-pub fn add_service(identifier: &str, mut binder: SpIBinder) -> Result<()> {
- let instance = CString::new(identifier).unwrap();
- let status =
- // Safety: `AServiceManager_addService` expects valid `AIBinder` and C
- // string pointers. Caller retains ownership of both pointers.
- // `AServiceManager_addService` creates a new strong reference and copies
- // the string, so both pointers need only be valid until the call returns.
- unsafe { sys::AServiceManager_addService(binder.as_native_mut(), instance.as_ptr()) };
- status_result(status)
-}
-
-/// Register a dynamic service via the LazyServiceRegistrar.
-///
-/// Registers the given binder object with the given identifier. If successful,
-/// this service can then be retrieved using that identifier. The service process
-/// will be shut down once all registered services are no longer in use.
-///
-/// If any service in the process is registered as lazy, all should be, otherwise
-/// the process may be shut down while a service is in use.
-///
-/// This function will panic if the identifier contains a 0 byte (NUL).
-pub fn register_lazy_service(identifier: &str, mut binder: SpIBinder) -> Result<()> {
- let instance = CString::new(identifier).unwrap();
- // Safety: `AServiceManager_registerLazyService` expects valid `AIBinder` and C
- // string pointers. Caller retains ownership of both
- // pointers. `AServiceManager_registerLazyService` creates a new strong reference
- // and copies the string, so both pointers need only be valid until the
- // call returns.
- let status = unsafe {
- sys::AServiceManager_registerLazyService(binder.as_native_mut(), instance.as_ptr())
- };
- status_result(status)
-}
-
-/// Prevent a process which registers lazy services from being shut down even when none
-/// of the services is in use.
-///
-/// If persist is true then shut down will be blocked until this function is called again with
-/// persist false. If this is to be the initial state, call this function before calling
-/// register_lazy_service.
-///
-/// Consider using [`LazyServiceGuard`] rather than calling this directly.
-pub fn force_lazy_services_persist(persist: bool) {
- // Safety: No borrowing or transfer of ownership occurs here.
- unsafe { sys::AServiceManager_forceLazyServicesPersist(persist) }
-}
-
-/// An RAII object to ensure a process which registers lazy services is not killed. During the
-/// lifetime of any of these objects the service manager will not not kill the process even if none
-/// of its lazy services are in use.
-#[must_use]
-#[derive(Debug)]
-pub struct LazyServiceGuard {
- // Prevent construction outside this module.
- _private: (),
-}
-
-// Count of how many LazyServiceGuard objects are in existence.
-static GUARD_COUNT: Mutex<u64> = Mutex::new(0);
-
-impl LazyServiceGuard {
- /// Create a new LazyServiceGuard to prevent the service manager prematurely killing this
- /// process.
- pub fn new() -> Self {
- let mut count = GUARD_COUNT.lock().unwrap();
- *count += 1;
- if *count == 1 {
- // It's important that we make this call with the mutex held, to make sure
- // that multiple calls (e.g. if the count goes 1 -> 0 -> 1) are correctly
- // sequenced. (That also means we can't just use an AtomicU64.)
- force_lazy_services_persist(true);
- }
- Self { _private: () }
- }
-}
-
-impl Drop for LazyServiceGuard {
- fn drop(&mut self) {
- let mut count = GUARD_COUNT.lock().unwrap();
- *count -= 1;
- if *count == 0 {
- force_lazy_services_persist(false);
- }
- }
-}
-
-impl Clone for LazyServiceGuard {
- fn clone(&self) -> Self {
- Self::new()
- }
-}
-
-impl Default for LazyServiceGuard {
- fn default() -> Self {
- Self::new()
- }
-}
-
/// Tests often create a base BBinder instance; so allowing the unit
/// type to be remotable translates nicely to Binder::new(()).
impl Remotable for () {
@@ -590,10 +485,3 @@
}
impl Interface for () {}
-
-/// Determine whether the current thread is currently executing an incoming
-/// transaction.
-pub fn is_handling_transaction() -> bool {
- // Safety: This method is always safe to call.
- unsafe { sys::AIBinder_isHandlingTransaction() }
-}
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index 7434e9d..340014a 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -29,11 +29,10 @@
use std::cmp::Ordering;
use std::convert::TryInto;
-use std::ffi::{c_void, CStr, CString};
+use std::ffi::{c_void, CString};
use std::fmt;
use std::mem;
use std::os::fd::AsRawFd;
-use std::os::raw::c_char;
use std::ptr;
use std::sync::Arc;
@@ -129,14 +128,6 @@
}
}
-fn interface_cast<T: FromIBinder + ?Sized>(service: Option<SpIBinder>) -> Result<Strong<T>> {
- if let Some(service) = service {
- FromIBinder::try_from(service)
- } else {
- Err(StatusCode::NAME_NOT_FOUND)
- }
-}
-
pub mod unstable_api {
use super::{sys, SpIBinder};
@@ -739,93 +730,6 @@
}
}
-/// Retrieve an existing service, blocking for a few seconds if it doesn't yet
-/// exist.
-pub fn get_service(name: &str) -> Option<SpIBinder> {
- let name = CString::new(name).ok()?;
- // Safety: `AServiceManager_getService` returns either a null pointer or a
- // valid pointer to an owned `AIBinder`. Either of these values is safe to
- // pass to `SpIBinder::from_raw`.
- unsafe { SpIBinder::from_raw(sys::AServiceManager_getService(name.as_ptr())) }
-}
-
-/// Retrieve an existing service, or start it if it is configured as a dynamic
-/// service and isn't yet started.
-pub fn wait_for_service(name: &str) -> Option<SpIBinder> {
- let name = CString::new(name).ok()?;
- // Safety: `AServiceManager_waitforService` returns either a null pointer or
- // a valid pointer to an owned `AIBinder`. Either of these values is safe to
- // pass to `SpIBinder::from_raw`.
- unsafe { SpIBinder::from_raw(sys::AServiceManager_waitForService(name.as_ptr())) }
-}
-
-/// Retrieve an existing service for a particular interface, blocking for a few
-/// seconds if it doesn't yet exist.
-pub fn get_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Strong<T>> {
- interface_cast(get_service(name))
-}
-
-/// Retrieve an existing service for a particular interface, or start it if it
-/// is configured as a dynamic service and isn't yet started.
-pub fn wait_for_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Strong<T>> {
- interface_cast(wait_for_service(name))
-}
-
-/// Check if a service is declared (e.g. in a VINTF manifest)
-pub fn is_declared(interface: &str) -> Result<bool> {
- let interface = CString::new(interface).or(Err(StatusCode::UNEXPECTED_NULL))?;
-
- // Safety: `interface` is a valid null-terminated C-style string and is only
- // borrowed for the lifetime of the call. The `interface` local outlives
- // this call as it lives for the function scope.
- unsafe { Ok(sys::AServiceManager_isDeclared(interface.as_ptr())) }
-}
-
-/// Retrieve all declared instances for a particular interface
-///
-/// For instance, if 'android.foo.IFoo/foo' is declared, and 'android.foo.IFoo'
-/// is passed here, then ["foo"] would be returned.
-pub fn get_declared_instances(interface: &str) -> Result<Vec<String>> {
- unsafe extern "C" fn callback(instance: *const c_char, opaque: *mut c_void) {
- // Safety: opaque was a mutable pointer created below from a Vec of
- // CString, and outlives this callback. The null handling here is just
- // to avoid the possibility of unwinding across C code if this crate is
- // ever compiled with panic=unwind.
- if let Some(instances) = unsafe { opaque.cast::<Vec<CString>>().as_mut() } {
- // Safety: instance is a valid null-terminated C string with a
- // lifetime at least as long as this function, and we immediately
- // copy it into an owned CString.
- unsafe {
- instances.push(CStr::from_ptr(instance).to_owned());
- }
- } else {
- eprintln!("Opaque pointer was null in get_declared_instances callback!");
- }
- }
-
- let interface = CString::new(interface).or(Err(StatusCode::UNEXPECTED_NULL))?;
- let mut instances: Vec<CString> = vec![];
- // Safety: `interface` and `instances` are borrowed for the length of this
- // call and both outlive the call. `interface` is guaranteed to be a valid
- // null-terminated C-style string.
- unsafe {
- sys::AServiceManager_forEachDeclaredInstance(
- interface.as_ptr(),
- &mut instances as *mut _ as *mut c_void,
- Some(callback),
- );
- }
-
- instances
- .into_iter()
- .map(CString::into_string)
- .collect::<std::result::Result<Vec<String>, _>>()
- .map_err(|e| {
- eprintln!("An interface instance name was not a valid UTF-8 string: {}", e);
- StatusCode::BAD_VALUE
- })
-}
-
/// Safety: `SpIBinder` guarantees that `binder` always contains a valid pointer
/// to an `AIBinder`, so we can trivially extract this pointer here.
unsafe impl AsNative<sys::AIBinder> for SpIBinder {
diff --git a/libs/binder/rust/src/service.rs b/libs/binder/rust/src/service.rs
new file mode 100644
index 0000000..3ca3b54
--- /dev/null
+++ b/libs/binder/rust/src/service.rs
@@ -0,0 +1,230 @@
+/*
+ * 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.
+ */
+
+use crate::binder::{AsNative, FromIBinder, Strong};
+use crate::error::{status_result, Result, StatusCode};
+use crate::proxy::SpIBinder;
+use crate::sys;
+
+use std::ffi::{c_void, CStr, CString};
+use std::os::raw::c_char;
+use std::sync::Mutex;
+
+/// Register a new service with the default service manager.
+///
+/// Registers the given binder object with the given identifier. If successful,
+/// this service can then be retrieved using that identifier.
+///
+/// This function will panic if the identifier contains a 0 byte (NUL).
+pub fn add_service(identifier: &str, mut binder: SpIBinder) -> Result<()> {
+ let instance = CString::new(identifier).unwrap();
+ let status =
+ // Safety: `AServiceManager_addService` expects valid `AIBinder` and C
+ // string pointers. Caller retains ownership of both pointers.
+ // `AServiceManager_addService` creates a new strong reference and copies
+ // the string, so both pointers need only be valid until the call returns.
+ unsafe { sys::AServiceManager_addService(binder.as_native_mut(), instance.as_ptr()) };
+ status_result(status)
+}
+
+/// Register a dynamic service via the LazyServiceRegistrar.
+///
+/// Registers the given binder object with the given identifier. If successful,
+/// this service can then be retrieved using that identifier. The service process
+/// will be shut down once all registered services are no longer in use.
+///
+/// If any service in the process is registered as lazy, all should be, otherwise
+/// the process may be shut down while a service is in use.
+///
+/// This function will panic if the identifier contains a 0 byte (NUL).
+pub fn register_lazy_service(identifier: &str, mut binder: SpIBinder) -> Result<()> {
+ let instance = CString::new(identifier).unwrap();
+ // Safety: `AServiceManager_registerLazyService` expects valid `AIBinder` and C
+ // string pointers. Caller retains ownership of both
+ // pointers. `AServiceManager_registerLazyService` creates a new strong reference
+ // and copies the string, so both pointers need only be valid until the
+ // call returns.
+ let status = unsafe {
+ sys::AServiceManager_registerLazyService(binder.as_native_mut(), instance.as_ptr())
+ };
+ status_result(status)
+}
+
+/// Prevent a process which registers lazy services from being shut down even when none
+/// of the services is in use.
+///
+/// If persist is true then shut down will be blocked until this function is called again with
+/// persist false. If this is to be the initial state, call this function before calling
+/// register_lazy_service.
+///
+/// Consider using [`LazyServiceGuard`] rather than calling this directly.
+pub fn force_lazy_services_persist(persist: bool) {
+ // Safety: No borrowing or transfer of ownership occurs here.
+ unsafe { sys::AServiceManager_forceLazyServicesPersist(persist) }
+}
+
+/// An RAII object to ensure a process which registers lazy services is not killed. During the
+/// lifetime of any of these objects the service manager will not kill the process even if none
+/// of its lazy services are in use.
+#[must_use]
+#[derive(Debug)]
+pub struct LazyServiceGuard {
+ // Prevent construction outside this module.
+ _private: (),
+}
+
+// Count of how many LazyServiceGuard objects are in existence.
+static GUARD_COUNT: Mutex<u64> = Mutex::new(0);
+
+impl LazyServiceGuard {
+ /// Create a new LazyServiceGuard to prevent the service manager prematurely killing this
+ /// process.
+ pub fn new() -> Self {
+ let mut count = GUARD_COUNT.lock().unwrap();
+ *count += 1;
+ if *count == 1 {
+ // It's important that we make this call with the mutex held, to make sure
+ // that multiple calls (e.g. if the count goes 1 -> 0 -> 1) are correctly
+ // sequenced. (That also means we can't just use an AtomicU64.)
+ force_lazy_services_persist(true);
+ }
+ Self { _private: () }
+ }
+}
+
+impl Drop for LazyServiceGuard {
+ fn drop(&mut self) {
+ let mut count = GUARD_COUNT.lock().unwrap();
+ *count -= 1;
+ if *count == 0 {
+ force_lazy_services_persist(false);
+ }
+ }
+}
+
+impl Clone for LazyServiceGuard {
+ fn clone(&self) -> Self {
+ Self::new()
+ }
+}
+
+impl Default for LazyServiceGuard {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+/// Determine whether the current thread is currently executing an incoming
+/// transaction.
+pub fn is_handling_transaction() -> bool {
+ // Safety: This method is always safe to call.
+ unsafe { sys::AIBinder_isHandlingTransaction() }
+}
+
+fn interface_cast<T: FromIBinder + ?Sized>(service: Option<SpIBinder>) -> Result<Strong<T>> {
+ if let Some(service) = service {
+ FromIBinder::try_from(service)
+ } else {
+ Err(StatusCode::NAME_NOT_FOUND)
+ }
+}
+
+/// Retrieve an existing service, blocking for a few seconds if it doesn't yet
+/// exist.
+pub fn get_service(name: &str) -> Option<SpIBinder> {
+ let name = CString::new(name).ok()?;
+ // Safety: `AServiceManager_getService` returns either a null pointer or a
+ // valid pointer to an owned `AIBinder`. Either of these values is safe to
+ // pass to `SpIBinder::from_raw`.
+ unsafe { SpIBinder::from_raw(sys::AServiceManager_getService(name.as_ptr())) }
+}
+
+/// Retrieve an existing service, or start it if it is configured as a dynamic
+/// service and isn't yet started.
+pub fn wait_for_service(name: &str) -> Option<SpIBinder> {
+ let name = CString::new(name).ok()?;
+ // Safety: `AServiceManager_waitforService` returns either a null pointer or
+ // a valid pointer to an owned `AIBinder`. Either of these values is safe to
+ // pass to `SpIBinder::from_raw`.
+ unsafe { SpIBinder::from_raw(sys::AServiceManager_waitForService(name.as_ptr())) }
+}
+
+/// Retrieve an existing service for a particular interface, blocking for a few
+/// seconds if it doesn't yet exist.
+pub fn get_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Strong<T>> {
+ interface_cast(get_service(name))
+}
+
+/// Retrieve an existing service for a particular interface, or start it if it
+/// is configured as a dynamic service and isn't yet started.
+pub fn wait_for_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Strong<T>> {
+ interface_cast(wait_for_service(name))
+}
+
+/// Check if a service is declared (e.g. in a VINTF manifest)
+pub fn is_declared(interface: &str) -> Result<bool> {
+ let interface = CString::new(interface).or(Err(StatusCode::UNEXPECTED_NULL))?;
+
+ // Safety: `interface` is a valid null-terminated C-style string and is only
+ // borrowed for the lifetime of the call. The `interface` local outlives
+ // this call as it lives for the function scope.
+ unsafe { Ok(sys::AServiceManager_isDeclared(interface.as_ptr())) }
+}
+
+/// Retrieve all declared instances for a particular interface
+///
+/// For instance, if 'android.foo.IFoo/foo' is declared, and 'android.foo.IFoo'
+/// is passed here, then ["foo"] would be returned.
+pub fn get_declared_instances(interface: &str) -> Result<Vec<String>> {
+ unsafe extern "C" fn callback(instance: *const c_char, opaque: *mut c_void) {
+ // Safety: opaque was a mutable pointer created below from a Vec of
+ // CString, and outlives this callback. The null handling here is just
+ // to avoid the possibility of unwinding across C code if this crate is
+ // ever compiled with panic=unwind.
+ if let Some(instances) = unsafe { opaque.cast::<Vec<CString>>().as_mut() } {
+ // Safety: instance is a valid null-terminated C string with a
+ // lifetime at least as long as this function, and we immediately
+ // copy it into an owned CString.
+ unsafe {
+ instances.push(CStr::from_ptr(instance).to_owned());
+ }
+ } else {
+ eprintln!("Opaque pointer was null in get_declared_instances callback!");
+ }
+ }
+
+ let interface = CString::new(interface).or(Err(StatusCode::UNEXPECTED_NULL))?;
+ let mut instances: Vec<CString> = vec![];
+ // Safety: `interface` and `instances` are borrowed for the length of this
+ // call and both outlive the call. `interface` is guaranteed to be a valid
+ // null-terminated C-style string.
+ unsafe {
+ sys::AServiceManager_forEachDeclaredInstance(
+ interface.as_ptr(),
+ &mut instances as *mut _ as *mut c_void,
+ Some(callback),
+ );
+ }
+
+ instances
+ .into_iter()
+ .map(CString::into_string)
+ .collect::<std::result::Result<Vec<String>, _>>()
+ .map_err(|e| {
+ eprintln!("An interface instance name was not a valid UTF-8 string: {}", e);
+ StatusCode::BAD_VALUE
+ })
+}
diff --git a/libs/binder/trusty/build-config-usertests b/libs/binder/trusty/build-config-usertests
index d0a1fbc..72e5ff9 100644
--- a/libs/binder/trusty/build-config-usertests
+++ b/libs/binder/trusty/build-config-usertests
@@ -16,4 +16,5 @@
[
porttest("com.android.trusty.binderRpcTest"),
+ porttest("com.android.trusty.rust.binder_rpc_test.test"),
]
diff --git a/libs/binder/trusty/rust/binder_rpc_test/aidl/rules.mk b/libs/binder/trusty/rust/binder_rpc_test/aidl/rules.mk
new file mode 100644
index 0000000..1b0dca0
--- /dev/null
+++ b/libs/binder/trusty/rust/binder_rpc_test/aidl/rules.mk
@@ -0,0 +1,34 @@
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+LIBBINDER_TESTS_DIR := $(LOCAL_DIR)/../../../../tests
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_AIDL_LANGUAGE := rust
+
+MODULE_CRATE_NAME := binder_rpc_test_aidl
+
+MODULE_AIDLS := \
+ $(LIBBINDER_TESTS_DIR)/BinderRpcTestClientInfo.aidl \
+ $(LIBBINDER_TESTS_DIR)/BinderRpcTestServerConfig.aidl \
+ $(LIBBINDER_TESTS_DIR)/BinderRpcTestServerInfo.aidl \
+ $(LIBBINDER_TESTS_DIR)/IBinderRpcCallback.aidl \
+ $(LIBBINDER_TESTS_DIR)/IBinderRpcSession.aidl \
+ $(LIBBINDER_TESTS_DIR)/IBinderRpcTest.aidl \
+ $(LIBBINDER_TESTS_DIR)/ParcelableCertificateData.aidl \
+
+include make/aidl.mk
diff --git a/libs/binder/trusty/rust/binder_rpc_test/main.rs b/libs/binder/trusty/rust/binder_rpc_test/main.rs
new file mode 100644
index 0000000..a71ce2c
--- /dev/null
+++ b/libs/binder/trusty/rust/binder_rpc_test/main.rs
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#![cfg(test)]
+
+use binder::{IBinder, Strong};
+use binder_rpc_test_aidl::aidl::IBinderRpcTest::IBinderRpcTest;
+use rpcbinder::RpcSession;
+use trusty_std::ffi::{CString, FallibleCString};
+
+test::init!();
+
+const SERVICE_PORT: &str = "com.android.trusty.binderRpcTestService.V1";
+
+fn get_service() -> Strong<dyn IBinderRpcTest> {
+ let port = CString::try_new(SERVICE_PORT).expect("Failed to allocate port name");
+ RpcSession::new().setup_trusty_client(port.as_c_str()).expect("Failed to create session")
+}
+
+#[test]
+fn ping() {
+ let srv = get_service();
+ assert_eq!(srv.as_binder().ping_binder(), Ok(()));
+}
diff --git a/libs/binder/trusty/rust/binder_rpc_test/manifest.json b/libs/binder/trusty/rust/binder_rpc_test/manifest.json
new file mode 100644
index 0000000..c2ecaa4
--- /dev/null
+++ b/libs/binder/trusty/rust/binder_rpc_test/manifest.json
@@ -0,0 +1,9 @@
+{
+ "uuid": "91eed949-8a9e-4569-9c83-5935fb624025",
+ "app_name": "rust_binder_rpc_test",
+ "min_heap": 16384,
+ "min_stack": 16384,
+ "mgmt_flags": {
+ "non_critical_app": true
+ }
+}
diff --git a/libs/binder/trusty/rust/binder_rpc_test/rules.mk b/libs/binder/trusty/rust/binder_rpc_test/rules.mk
new file mode 100644
index 0000000..192a159
--- /dev/null
+++ b/libs/binder/trusty/rust/binder_rpc_test/rules.mk
@@ -0,0 +1,35 @@
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+LIBBINDER_DIR := $(LOCAL_DIR)/../../..
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS := $(LOCAL_DIR)/main.rs
+
+MODULE_CRATE_NAME := binder_rpc_test
+
+MODULE_LIBRARY_DEPS += \
+ $(LIBBINDER_DIR)/trusty/rust \
+ $(LIBBINDER_DIR)/trusty/rust/rpcbinder \
+ $(LOCAL_DIR)/aidl \
+ trusty/user/base/lib/trusty-std \
+
+MODULE_RUST_TESTS := true
+
+MANIFEST := $(LOCAL_DIR)/manifest.json
+
+include make/library.mk
diff --git a/libs/binder/trusty/usertests-inc.mk b/libs/binder/trusty/usertests-inc.mk
index 1300121..241e668 100644
--- a/libs/binder/trusty/usertests-inc.mk
+++ b/libs/binder/trusty/usertests-inc.mk
@@ -17,3 +17,6 @@
frameworks/native/libs/binder/trusty/binderRpcTest \
frameworks/native/libs/binder/trusty/binderRpcTest/service \
+TRUSTY_RUST_USER_TESTS += \
+ frameworks/native/libs/binder/trusty/rust/binder_rpc_test \
+
diff --git a/libs/binderthreadstate/test.cpp b/libs/binderthreadstate/test.cpp
index b5c4010..e888b0a 100644
--- a/libs/binderthreadstate/test.cpp
+++ b/libs/binderthreadstate/test.cpp
@@ -22,6 +22,7 @@
#include <binderthreadstateutilstest/1.0/IHidlStuff.h>
#include <gtest/gtest.h>
#include <hidl/HidlTransportSupport.h>
+#include <hidl/ServiceManagement.h>
#include <hwbinder/IPCThreadState.h>
#include <thread>
@@ -37,6 +38,7 @@
using android::sp;
using android::String16;
using android::binder::Status;
+using android::hardware::isHidlSupported;
using android::hardware::Return;
using binderthreadstateutilstest::V1_0::IHidlStuff;
@@ -67,6 +69,7 @@
// complicated calls are possible, but this should do here.
static void callHidl(size_t id, int32_t idx) {
+ CHECK_EQ(true, isHidlSupported()) << "We shouldn't be calling HIDL if it's not supported";
auto stuff = IHidlStuff::getService(id2name(id));
CHECK(stuff->call(idx).isOk());
}
@@ -174,6 +177,7 @@
}
TEST(BindThreadState, RemoteHidlCall) {
+ if (!isHidlSupported()) GTEST_SKIP() << "No HIDL support on device";
auto stuff = IHidlStuff::getService(id2name(kP1Id));
ASSERT_NE(nullptr, stuff);
ASSERT_TRUE(stuff->call(0).isOk());
@@ -186,11 +190,14 @@
}
TEST(BindThreadState, RemoteNestedStartHidlCall) {
+ if (!isHidlSupported()) GTEST_SKIP() << "No HIDL support on device";
auto stuff = IHidlStuff::getService(id2name(kP1Id));
ASSERT_NE(nullptr, stuff);
ASSERT_TRUE(stuff->call(100).isOk());
}
TEST(BindThreadState, RemoteNestedStartAidlCall) {
+ // this test case is trying ot nest a HIDL call which requires HIDL support
+ if (!isHidlSupported()) GTEST_SKIP() << "No HIDL support on device";
sp<IAidlStuff> stuff;
ASSERT_EQ(OK, android::getService<IAidlStuff>(String16(id2name(kP1Id).c_str()), &stuff));
ASSERT_NE(nullptr, stuff);
@@ -205,11 +212,15 @@
defaultServiceManager()->addService(String16(id2name(thisId).c_str()), aidlServer));
android::ProcessState::self()->startThreadPool();
- // HIDL
- android::hardware::configureRpcThreadpool(1, true /*callerWillJoin*/);
- sp<IHidlStuff> hidlServer = new HidlServer(thisId, otherId);
- CHECK_EQ(OK, hidlServer->registerAsService(id2name(thisId).c_str()));
- android::hardware::joinRpcThreadpool();
+ if (isHidlSupported()) {
+ // HIDL
+ android::hardware::configureRpcThreadpool(1, true /*callerWillJoin*/);
+ sp<IHidlStuff> hidlServer = new HidlServer(thisId, otherId);
+ CHECK_EQ(OK, hidlServer->registerAsService(id2name(thisId).c_str()));
+ android::hardware::joinRpcThreadpool();
+ } else {
+ android::IPCThreadState::self()->joinThreadPool(true);
+ }
return EXIT_FAILURE;
}
@@ -227,9 +238,15 @@
}
android::waitForService<IAidlStuff>(String16(id2name(kP1Id).c_str()));
- android::hardware::details::waitForHwService(IHidlStuff::descriptor, id2name(kP1Id).c_str());
+ if (isHidlSupported()) {
+ android::hardware::details::waitForHwService(IHidlStuff::descriptor,
+ id2name(kP1Id).c_str());
+ }
android::waitForService<IAidlStuff>(String16(id2name(kP2Id).c_str()));
- android::hardware::details::waitForHwService(IHidlStuff::descriptor, id2name(kP2Id).c_str());
+ if (isHidlSupported()) {
+ android::hardware::details::waitForHwService(IHidlStuff::descriptor,
+ id2name(kP2Id).c_str());
+ }
return RUN_ALL_TESTS();
}
diff --git a/libs/bufferqueueconverter/Android.bp b/libs/bufferqueueconverter/Android.bp
index 3c8c41f..196161b 100644
--- a/libs/bufferqueueconverter/Android.bp
+++ b/libs/bufferqueueconverter/Android.bp
@@ -17,9 +17,6 @@
cc_library {
name: "libbufferqueueconverter",
vendor_available: true,
- vndk: {
- enabled: true,
- },
double_loadable: true,
srcs: [
diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp
index f300da5..8dabc2c 100644
--- a/libs/gralloc/types/Android.bp
+++ b/libs/gralloc/types/Android.bp
@@ -38,10 +38,7 @@
},
vendor_available: true,
- vndk: {
- enabled: true,
- support_system_process: true,
- },
+ double_loadable: true,
apex_available: [
"//apex_available:platform",
"com.android.media.swcodec",
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index d1b2c5f..6c45746 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -305,10 +305,6 @@
cc_library_shared {
name: "libgui",
vendor_available: true,
- vndk: {
- enabled: true,
- private: true,
- },
double_loadable: true,
defaults: [
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 1d2ea3e..eb945cc 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -2443,7 +2443,6 @@
const sp<IBinder>& parentHandle,
LayerMetadata metadata,
uint32_t* outTransformHint) {
- sp<SurfaceControl> sur;
status_t err = mStatus;
if (mStatus == NO_ERROR) {
diff --git a/libs/nativewindow/rust/Android.bp b/libs/nativewindow/rust/Android.bp
index a3df482..97740db 100644
--- a/libs/nativewindow/rust/Android.bp
+++ b/libs/nativewindow/rust/Android.bp
@@ -54,6 +54,10 @@
},
min_sdk_version: "VanillaIceCream",
vendor_available: true,
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.virt",
+ ],
}
rust_library {
@@ -78,6 +82,10 @@
},
min_sdk_version: "VanillaIceCream",
vendor_available: true,
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.virt",
+ ],
}
rust_test {
@@ -116,6 +124,10 @@
},
min_sdk_version: "VanillaIceCream",
vendor_available: true,
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.virt",
+ ],
}
rust_test {
diff --git a/libs/nativewindow/rust/src/lib.rs b/libs/nativewindow/rust/src/lib.rs
index 22ad834..dc3f51f 100644
--- a/libs/nativewindow/rust/src/lib.rs
+++ b/libs/nativewindow/rust/src/lib.rs
@@ -16,7 +16,8 @@
extern crate nativewindow_bindgen as ffi;
-pub mod surface;
+mod surface;
+pub use surface::Surface;
pub use ffi::{AHardwareBuffer_Format, AHardwareBuffer_UsageFlags};
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 4bd0852..9f614bd 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -1265,7 +1265,12 @@
void RenderEngineTest::fillBufferWithPremultiplyAlpha() {
fillRedBufferWithPremultiplyAlpha();
- expectBufferColor(fullscreenRect(), 128, 0, 0, 128);
+ // Different backends and GPUs may round 255 * 0.5 = 127.5 differently, but
+ // either 127 or 128 are acceptable. Checking both 127 and 128 with a
+ // tolerance of 1 allows either 127 or 128 to pass, while preventing 126 or
+ // 129 from erroneously passing.
+ expectBufferColor(fullscreenRect(), 127, 0, 0, 127, 1);
+ expectBufferColor(fullscreenRect(), 128, 0, 0, 128, 1);
}
void RenderEngineTest::fillRedBufferWithoutPremultiplyAlpha() {
@@ -1687,11 +1692,6 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_outputDataspace) {
- // TODO: b/331445583 - Fix in Graphite and re-enable.
- if (GetParam()->skiaBackend() == renderengine::RenderEngine::SkiaBackend::GRAPHITE) {
- GTEST_SKIP();
- }
-
const auto& renderEngineFactory = GetParam();
// skip for non color management
if (!renderEngineFactory->apiSupported()) {
@@ -1842,11 +1842,6 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_opaqueBufferSource) {
- // TODO: b/331447131 - Fix in Graphite and re-enable.
- if (GetParam()->skiaBackend() == renderengine::RenderEngine::SkiaBackend::GRAPHITE) {
- GTEST_SKIP();
- }
-
const auto& renderEngineFactory = GetParam();
// skip for non color management
if (!renderEngineFactory->apiSupported()) {
@@ -1997,11 +1992,6 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_bufferSource) {
- // TODO: b/331446495 - Fix in Graphite and re-enable.
- if (GetParam()->skiaBackend() == renderengine::RenderEngine::SkiaBackend::GRAPHITE) {
- GTEST_SKIP();
- }
-
const auto& renderEngineFactory = GetParam();
// skip for non color management
if (!renderEngineFactory->apiSupported()) {
@@ -2061,11 +2051,6 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBuffer_premultipliesAlpha) {
- // TODO: b/331446496 - Fix in Graphite and re-enable.
- if (GetParam()->skiaBackend() == renderengine::RenderEngine::SkiaBackend::GRAPHITE) {
- GTEST_SKIP();
- }
-
if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 9cb298a..312a1e6 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -105,9 +105,6 @@
cc_library_shared {
name: "libui",
vendor_available: true,
- vndk: {
- enabled: true,
- },
double_loadable: true,
cflags: [
diff --git a/services/inputflinger/InputFilterCallbacks.cpp b/services/inputflinger/InputFilterCallbacks.cpp
index 6c31442..a9bdbec 100644
--- a/services/inputflinger/InputFilterCallbacks.cpp
+++ b/services/inputflinger/InputFilterCallbacks.cpp
@@ -19,9 +19,10 @@
#include "InputFilterCallbacks.h"
#include <aidl/com/android/server/inputflinger/BnInputThread.h>
#include <android/binder_auto_utils.h>
+#include <utils/Looper.h>
#include <utils/StrongPointer.h>
-#include <utils/Thread.h>
#include <functional>
+#include "InputThread.h"
namespace android {
@@ -38,36 +39,37 @@
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);
+ mLooper = sp<Looper>::make(/*allowNonCallbacks=*/false);
+ mThread = std::make_unique<InputThread>(
+ "InputFilter", [this]() { loopOnce(); }, [this]() { mLooper->wake(); });
}
ndk::ScopedAStatus finish() override {
- mThread->requestExit();
+ if (mThread && mThread->isCallingThread()) {
+ ALOGE("InputFilterThread cannot be stopped on itself!");
+ return ndk::ScopedAStatus::fromStatus(INVALID_OPERATION);
+ }
+ mThread.reset();
+ return ndk::ScopedAStatus::ok();
+ }
+
+ ndk::ScopedAStatus sleepUntil(nsecs_t when) override {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ mLooper->pollOnce(toMillisecondTimeoutDelay(now, when));
+ return ndk::ScopedAStatus::ok();
+ }
+
+ ndk::ScopedAStatus wake() override {
+ mLooper->wake();
return ndk::ScopedAStatus::ok();
}
private:
- sp<Thread> mThread;
+ sp<Looper> mLooper;
+ std::unique_ptr<InputThread> mThread;
std::shared_ptr<IInputThreadCallback> mCallback;
void loopOnce() { LOG_ALWAYS_FATAL_IF(!mCallback->loopOnce().isOk()); }
diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/IInputThread.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/IInputThread.aidl
index 2f6b8fc..cc0592e 100644
--- a/services/inputflinger/aidl/com/android/server/inputflinger/IInputThread.aidl
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/IInputThread.aidl
@@ -21,6 +21,13 @@
* infrastructure.
*
* <p>
+ * Earlier, we used rust thread park()/unpark() to put the thread to sleep and wake up from sleep.
+ * But that caused some breakages after migrating the rust system crates to 2021 edition. Since,
+ * the threads are created in C++, it was more reliable to rely on C++ side of the implementation
+ * to implement the sleep and wake functions.
+ * </p>
+ *
+ * <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
@@ -33,6 +40,16 @@
/** Finish input thread (if not running, this call does nothing) */
void finish();
+ /** Wakes up the thread (if sleeping) */
+ void wake();
+
+ /**
+ * Puts the thread to sleep until a future time provided.
+ *
+ * NOTE: The thread can be awaken before the provided time using {@link wake()} function.
+ */
+ void sleepUntil(long whenNanos);
+
/** Callbacks from C++ to call into inputflinger rust components */
interface IInputThreadCallback {
/**
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index c2d2b7b..02bc368 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -338,9 +338,8 @@
// it's unlikely that those two streams would be consistent with each other. Therefore,
// cancel the previous gesture if the display id changes.
if (motionEntry.displayId != lastMemento.displayId) {
- LOG(INFO) << "Canceling stream: last displayId was "
- << inputEventSourceToString(lastMemento.displayId) << " and new event is "
- << motionEntry;
+ LOG(INFO) << "Canceling stream: last displayId was " << lastMemento.displayId
+ << " and new event is " << motionEntry;
return true;
}
diff --git a/services/inputflinger/dispatcher/trace/InputTracer.cpp b/services/inputflinger/dispatcher/trace/InputTracer.cpp
index 415b696..4931a5f 100644
--- a/services/inputflinger/dispatcher/trace/InputTracer.cpp
+++ b/services/inputflinger/dispatcher/trace/InputTracer.cpp
@@ -89,13 +89,6 @@
const auto& info = *target.windowHandle->getInfo();
const bool isSensitiveTarget =
info.inputConfig.test(gui::WindowInfo::InputConfig::SENSITIVE_FOR_TRACING);
-
- // All FLAG_SECURE targets must be marked as sensitive for tracing.
- if (info.layoutParamsFlags.test(gui::WindowInfo::Flag::SECURE) && !isSensitiveTarget) {
- LOG(FATAL)
- << "Input target with FLAG_SECURE does not set InputConfig::SENSITIVE_FOR_TRACING: "
- << info;
- }
return {target.windowHandle->getInfo()->ownerUid, isSensitiveTarget};
}
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 7d27d4a..8d91599 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -20,8 +20,24 @@
#include "TouchInputMapper.h"
+#include <algorithm>
+#include <cinttypes>
+#include <cmath>
+#include <cstddef>
+#include <tuple>
+
+#include <math.h>
+
+#include <android-base/stringprintf.h>
+#include <android/input.h>
#include <ftl/enum.h>
#include <input/PrintTools.h>
+#include <input/PropertyMap.h>
+#include <input/VirtualKeyMap.h>
+#include <linux/input-event-codes.h>
+#include <log/log_main.h>
+#include <math/vec2.h>
+#include <ui/FloatRect.h>
#include "CursorButtonAccumulator.h"
#include "CursorScrollAccumulator.h"
@@ -147,20 +163,6 @@
info.addMotionRange(mOrientedRanges.y);
info.addMotionRange(mOrientedRanges.pressure);
- if (mDeviceMode == DeviceMode::UNSCALED && mSource == AINPUT_SOURCE_TOUCHPAD) {
- // Populate RELATIVE_X and RELATIVE_Y motion ranges for touchpad capture mode.
- //
- // RELATIVE_X and RELATIVE_Y motion ranges should be the largest possible relative
- // motion, i.e. the hardware dimensions, as the finger could move completely across the
- // touchpad in one sample cycle.
- const InputDeviceInfo::MotionRange& x = mOrientedRanges.x;
- const InputDeviceInfo::MotionRange& y = mOrientedRanges.y;
- info.addMotionRange(AMOTION_EVENT_AXIS_RELATIVE_X, mSource, -x.max, x.max, x.flat, x.fuzz,
- x.resolution);
- info.addMotionRange(AMOTION_EVENT_AXIS_RELATIVE_Y, mSource, -y.max, y.max, y.flat, y.fuzz,
- y.resolution);
- }
-
if (mOrientedRanges.size) {
info.addMotionRange(*mOrientedRanges.size);
}
@@ -531,7 +533,7 @@
* 4. Otherwise, use a non-display viewport.
*/
std::optional<DisplayViewport> TouchInputMapper::findViewport() {
- if (mParameters.hasAssociatedDisplay && mDeviceMode != DeviceMode::UNSCALED) {
+ if (mParameters.hasAssociatedDisplay) {
if (getDeviceContext().getAssociatedViewport()) {
return getDeviceContext().getAssociatedViewport();
}
@@ -939,8 +941,10 @@
mSource = AINPUT_SOURCE_TOUCH_NAVIGATION;
mDeviceMode = DeviceMode::NAVIGATION;
} else {
- mSource = AINPUT_SOURCE_TOUCHPAD;
- mDeviceMode = DeviceMode::UNSCALED;
+ ALOGW("Touch device '%s' has invalid parameters or configuration. The device will be "
+ "inoperable.",
+ getDeviceName().c_str());
+ mDeviceMode = DeviceMode::DISABLED;
}
const std::optional<DisplayViewport> newViewportOpt = findViewport();
@@ -1884,8 +1888,7 @@
}
if (!mCurrentRawState.rawPointerData.hoveringIdBits.isEmpty() &&
- mCurrentRawState.rawPointerData.touchingIdBits.isEmpty() &&
- mDeviceMode != DeviceMode::UNSCALED) {
+ mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) {
// We have hovering pointers, and there are no touching pointers.
bool hoveringPointersInFrame = false;
auto hoveringIds = mCurrentRawState.rawPointerData.hoveringIdBits;
@@ -1912,7 +1915,7 @@
// Skip checking whether the pointer is inside the physical frame if the device is in
// unscaled or pointer mode.
if (!isPointInsidePhysicalFrame(pointer.x, pointer.y) &&
- mDeviceMode != DeviceMode::UNSCALED && mDeviceMode != DeviceMode::POINTER) {
+ mDeviceMode != DeviceMode::POINTER) {
// If exactly one pointer went down, check for virtual key hit.
// Otherwise, we will drop the entire stroke.
if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) {
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 4b39e40..8451675 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -16,17 +16,38 @@
#pragma once
+#include <array>
+#include <climits>
+#include <limits>
+#include <list>
+#include <memory>
#include <optional>
#include <string>
+#include <utility>
+#include <vector>
#include <stdint.h>
+#include <gui/constants.h>
+#include <input/DisplayViewport.h>
+#include <input/Input.h>
+#include <input/InputDevice.h>
+#include <input/VelocityControl.h>
+#include <input/VelocityTracker.h>
+#include <ui/Rect.h>
#include <ui/Rotation.h>
+#include <ui/Size.h>
+#include <ui/Transform.h>
+#include <utils/BitSet.h>
+#include <utils/Timers.h>
#include "CursorButtonAccumulator.h"
#include "CursorScrollAccumulator.h"
#include "EventHub.h"
#include "InputMapper.h"
#include "InputReaderBase.h"
+#include "NotifyArgs.h"
+#include "PointerControllerInterface.h"
+#include "StylusState.h"
#include "TouchButtonAccumulator.h"
namespace android {
@@ -195,7 +216,6 @@
enum class DeviceMode {
DISABLED, // input is disabled
DIRECT, // direct mapping (touchscreen)
- UNSCALED, // unscaled mapping (e.g. captured touchpad)
NAVIGATION, // unscaled mapping with assist gesture (touch navigation)
POINTER, // pointer mapping (e.g. uncaptured touchpad, drawing tablet)
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index 721cdfd..f558ba1 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -185,6 +185,7 @@
static AStatsManager_PullAtomCallbackReturn pullAtomCallback(int32_t atomTag,
AStatsEventList* outEventList,
void* cookie) {
+ ALOGI("Received pull request for touchpad usage atom");
LOG_ALWAYS_FATAL_IF(atomTag != android::util::TOUCHPAD_USAGE);
MetricsAccumulator& accumulator = MetricsAccumulator::getInstance();
accumulator.produceAtomsAndReset(*outEventList);
@@ -192,6 +193,7 @@
}
void produceAtomsAndReset(AStatsEventList& outEventList) {
+ ALOGI("Acquiring lock for touchpad usage metrics...");
std::scoped_lock lock(mLock);
produceAtomsLocked(outEventList);
resetCountersLocked();
diff --git a/services/inputflinger/rust/input_filter.rs b/services/inputflinger/rust/input_filter.rs
index a544fa3..6df339e 100644
--- a/services/inputflinger/rust/input_filter.rs
+++ b/services/inputflinger/rust/input_filter.rs
@@ -396,14 +396,16 @@
IInputThread::{BnInputThread, IInputThread, IInputThreadCallback::IInputThreadCallback},
KeyEvent::KeyEvent,
};
- use std::sync::{Arc, RwLock, RwLockWriteGuard};
+ use nix::{sys::time::TimeValLike, time::clock_gettime, time::ClockId};
+ use std::sync::{atomic::AtomicBool, atomic::Ordering, Arc, RwLock, RwLockWriteGuard};
+ use std::time::Duration;
#[derive(Default)]
struct TestCallbacksInner {
last_modifier_state: u32,
last_locked_modifier_state: u32,
last_event: Option<KeyEvent>,
- test_thread: Option<TestThread>,
+ test_thread: Option<FakeCppThread>,
}
#[derive(Default, Clone)]
@@ -438,13 +440,9 @@
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 {
+ pub fn is_thread_running(&self) -> bool {
if let Some(test_thread) = &self.0.read().unwrap().test_thread {
- return test_thread.is_finish_called();
+ return test_thread.is_running();
}
false
}
@@ -468,41 +466,101 @@
fn createInputFilterThread(
&self,
- _callback: &Strong<dyn IInputThreadCallback>,
+ callback: &Strong<dyn IInputThreadCallback>,
) -> std::result::Result<Strong<dyn IInputThread>, binder::Status> {
- let test_thread = TestThread::new();
+ let test_thread = FakeCppThread::new(callback.clone());
+ test_thread.start_looper();
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,
+ struct FakeCppThreadInner {
+ join_handle: Option<std::thread::JoinHandle<()>>,
}
- #[derive(Default, Clone)]
- struct TestThread(Arc<RwLock<TestThreadInner>>);
+ #[derive(Clone)]
+ struct FakeCppThread {
+ callback: Arc<RwLock<Strong<dyn IInputThreadCallback>>>,
+ inner: Arc<RwLock<FakeCppThreadInner>>,
+ exit_flag: Arc<AtomicBool>,
+ }
- impl Interface for TestThread {}
+ impl Interface for FakeCppThread {}
- impl TestThread {
- pub fn new() -> Self {
- Default::default()
+ impl FakeCppThread {
+ pub fn new(callback: Strong<dyn IInputThreadCallback>) -> Self {
+ let thread = Self {
+ callback: Arc::new(RwLock::new(callback)),
+ inner: Arc::new(RwLock::new(FakeCppThreadInner { join_handle: None })),
+ exit_flag: Arc::new(AtomicBool::new(true)),
+ };
+ thread.create_looper();
+ thread
}
- fn inner(&self) -> RwLockWriteGuard<'_, TestThreadInner> {
- self.0.write().unwrap()
+ fn inner(&self) -> RwLockWriteGuard<'_, FakeCppThreadInner> {
+ self.inner.write().unwrap()
}
- pub fn is_finish_called(&self) -> bool {
- self.0.read().unwrap().is_finish_called
+ fn create_looper(&self) {
+ let clone = self.clone();
+ let join_handle = std::thread::Builder::new()
+ .name("fake_cpp_thread".to_string())
+ .spawn(move || loop {
+ if !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 start_looper(&self) {
+ self.exit_flag.store(false, Ordering::Relaxed);
+ }
+
+ pub fn stop_looper(&self) {
+ self.exit_flag.store(true, Ordering::Relaxed);
+ if let Some(join_handle) = &self.inner.read().unwrap().join_handle {
+ join_handle.thread().unpark();
+ }
+ }
+
+ pub fn is_running(&self) -> bool {
+ !self.exit_flag.load(Ordering::Relaxed)
+ }
+
+ fn loop_once(&self) {
+ let _ = self.callback.read().unwrap().loopOnce();
}
}
- impl IInputThread for TestThread {
+ impl IInputThread for FakeCppThread {
fn finish(&self) -> binder::Result<()> {
- self.inner().is_finish_called = true;
+ self.stop_looper();
+ Result::Ok(())
+ }
+
+ fn wake(&self) -> binder::Result<()> {
+ if let Some(join_handle) = &self.inner.read().unwrap().join_handle {
+ join_handle.thread().unpark();
+ }
+ Result::Ok(())
+ }
+
+ fn sleepUntil(&self, wake_up_time: i64) -> binder::Result<()> {
+ let now = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds();
+ if wake_up_time == i64::MAX {
+ std::thread::park();
+ } else {
+ let duration_now = Duration::from_nanos(now as u64);
+ let duration_wake_up = Duration::from_nanos(wake_up_time as u64);
+ std::thread::park_timeout(duration_wake_up - duration_now);
+ }
Result::Ok(())
}
}
diff --git a/services/inputflinger/rust/input_filter_thread.rs b/services/inputflinger/rust/input_filter_thread.rs
index 2d503ae..96e5681 100644
--- a/services/inputflinger/rust/input_filter_thread.rs
+++ b/services/inputflinger/rust/input_filter_thread.rs
@@ -33,8 +33,6 @@
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 {
@@ -54,15 +52,18 @@
thread_creator: InputFilterThreadCreator,
thread_callback_handler: ThreadCallbackHandler,
inner: Arc<RwLock<InputFilterThreadInner>>,
+ looper: Arc<RwLock<Looper>>,
}
struct InputFilterThreadInner {
- cpp_thread: Option<Strong<dyn IInputThread>>,
- looper: Option<Thread>,
next_timeout: i64,
is_finishing: bool,
}
+struct Looper {
+ cpp_thread: Option<Strong<dyn IInputThread>>,
+}
+
impl InputFilterThread {
/// Create a new InputFilterThread instance.
/// NOTE: This will create a new thread. Clone the existing instance to reuse the same thread.
@@ -71,11 +72,10 @@
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,
})),
+ looper: Arc::new(RwLock::new(Looper { cpp_thread: None })),
}
}
@@ -83,12 +83,17 @@
/// 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();
+ let mut need_wake = false;
+ {
+ // acquire filter lock
+ let filter_thread = &mut self.filter_thread();
+ if when_nanos < filter_thread.next_timeout {
+ filter_thread.next_timeout = when_nanos;
+ need_wake = true;
}
+ } // release filter lock
+ if need_wake {
+ self.wake();
}
}
@@ -120,29 +125,36 @@
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;
- }
+ {
+ // acquire looper lock
+ let looper = &mut self.looper();
+ if looper.cpp_thread.is_none() {
+ looper.cpp_thread = Some(self.thread_creator.create(
+ &BnInputThreadCallback::new_binder(self.clone(), BinderFeatures::default()),
+ ));
+ }
+ } // release looper lock
+ self.set_finishing(false);
}
fn stop(&self) {
debug!("InputFilterThread: stop thread");
+ self.set_finishing(true);
+ self.wake();
+ {
+ // acquire looper lock
+ let looper = &mut self.looper();
+ if let Some(cpp_thread) = &looper.cpp_thread {
+ let _ = cpp_thread.finish();
+ }
+ // Clear all references
+ looper.cpp_thread = None;
+ } // release looper lock
+ }
+
+ fn set_finishing(&self, is_finishing: bool) {
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;
+ filter_thread.is_finishing = is_finishing;
}
fn loop_once(&self, now: i64) {
@@ -163,25 +175,34 @@
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);
- }
+ self.sleep_until(wake_up_time);
}
fn filter_thread(&self) -> RwLockWriteGuard<'_, InputFilterThreadInner> {
self.inner.write().unwrap()
}
+
+ fn sleep_until(&self, when_nanos: i64) {
+ let looper = self.looper.read().unwrap();
+ if let Some(cpp_thread) = &looper.cpp_thread {
+ let _ = cpp_thread.sleepUntil(when_nanos);
+ }
+ }
+
+ fn wake(&self) {
+ let looper = self.looper.read().unwrap();
+ if let Some(cpp_thread) = &looper.cpp_thread {
+ let _ = cpp_thread.wake();
+ }
+ }
+
+ fn looper(&self) -> RwLockWriteGuard<'_, Looper> {
+ self.looper.write().unwrap()
+ }
}
impl Interface for InputFilterThread {}
@@ -252,165 +273,64 @@
#[cfg(test)]
mod tests {
- use crate::input_filter::test_callbacks::TestCallbacks;
- use crate::input_filter_thread::{
- test_thread::TestThread, test_thread_callback::TestThreadCallback,
- };
+ use crate::input_filter::{test_callbacks::TestCallbacks, InputFilterThreadCreator};
+ use crate::input_filter_thread::{test_thread_callback::TestThreadCallback, InputFilterThread};
+ use binder::Strong;
+ use nix::{sys::time::TimeValLike, time::clock_gettime, time::ClockId};
+ use std::sync::{Arc, RwLock};
+ use std::time::Duration;
#[test]
fn test_register_callback_creates_cpp_thread() {
let test_callbacks = TestCallbacks::new();
- let test_thread = TestThread::new(test_callbacks.clone());
+ let test_thread = get_thread(test_callbacks.clone());
let test_thread_callback = TestThreadCallback::new();
- test_thread.register_thread_callback(test_thread_callback);
- assert!(test_callbacks.is_thread_created());
+ test_thread.register_thread_callback(Box::new(test_thread_callback));
+ assert!(test_callbacks.is_thread_running());
}
#[test]
fn test_unregister_callback_finishes_cpp_thread() {
let test_callbacks = TestCallbacks::new();
- let test_thread = TestThread::new(test_callbacks.clone());
+ let test_thread = get_thread(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_thread.register_thread_callback(Box::new(test_thread_callback.clone()));
+ test_thread.unregister_thread_callback(Box::new(test_thread_callback));
+ assert!(!test_callbacks.is_thread_running());
}
#[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 = get_thread(test_callbacks.clone());
let test_thread_callback = TestThreadCallback::new();
- test_thread.register_thread_callback(test_thread_callback.clone());
- test_thread.start_looper();
+ test_thread.register_thread_callback(Box::new(test_thread_callback.clone()));
- test_thread.request_timeout_at_time(500);
- test_thread.dispatch_next();
+ let now = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_milliseconds();
+ test_thread.request_timeout_at_time((now + 10) * 1000000);
- test_thread.move_time_forward(500);
-
- test_thread.stop_looper();
+ std::thread::sleep(Duration::from_millis(20));
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 = get_thread(test_callbacks.clone());
let test_thread_callback = TestThreadCallback::new();
- test_thread.register_thread_callback(test_thread_callback.clone());
- test_thread.start_looper();
+ test_thread.register_thread_callback(Box::new(test_thread_callback.clone()));
- test_thread.request_timeout_at_time(500);
- test_thread.dispatch_next();
+ let now = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_milliseconds();
+ test_thread.request_timeout_at_time((now + 100) * 1000000);
- test_thread.move_time_forward(100);
-
- test_thread.stop_looper();
+ std::thread::sleep(Duration::from_millis(10));
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);
- }
+ fn get_thread(callbacks: TestCallbacks) -> InputFilterThread {
+ InputFilterThread::new(InputFilterThreadCreator::new(Arc::new(RwLock::new(Strong::new(
+ Box::new(callbacks),
+ )))))
}
}
diff --git a/services/inputflinger/rust/slow_keys_filter.rs b/services/inputflinger/rust/slow_keys_filter.rs
index 09fbf40..0f18a2f 100644
--- a/services/inputflinger/rust/slow_keys_filter.rs
+++ b/services/inputflinger/rust/slow_keys_filter.rs
@@ -207,13 +207,19 @@
#[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::input_filter::{
+ test_callbacks::TestCallbacks, test_filter::TestFilter, Filter, InputFilterThreadCreator,
+ };
+ use crate::input_filter_thread::InputFilterThread;
use crate::slow_keys_filter::{SlowKeysFilter, POLICY_FLAG_DISABLE_KEY_REPEAT};
use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source;
+ use binder::Strong;
use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction,
};
+ use nix::{sys::time::TimeValLike, time::clock_gettime, time::ClockId};
+ use std::sync::{Arc, RwLock};
+ use std::time::Duration;
static BASE_KEY_EVENT: KeyEvent = KeyEvent {
id: 1,
@@ -231,18 +237,19 @@
metaState: 0,
};
+ static SLOW_KEYS_THRESHOLD_NS: i64 = 100 * 1000000; // 100 ms
+
#[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 test_thread = get_thread(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 */
+ 1, /* device_id */
+ SLOW_KEYS_THRESHOLD_NS,
);
- test_thread.start_looper();
let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
filter.notify_key(&event);
@@ -252,15 +259,14 @@
#[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 test_thread = get_thread(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 */
+ 1, /* device_id */
+ SLOW_KEYS_THRESHOLD_NS,
);
- test_thread.start_looper();
let event =
KeyEvent { action: KeyEventAction::DOWN, source: Source::STYLUS, ..BASE_KEY_EVENT };
@@ -271,89 +277,115 @@
#[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 test_thread = get_thread(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 */
+ 1, /* device_id */
+ SLOW_KEYS_THRESHOLD_NS,
);
- test_thread.start_looper();
-
- filter.notify_key(&KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT });
+ let down_time = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds();
+ filter.notify_key(&KeyEvent {
+ action: KeyEventAction::DOWN,
+ downTime: down_time,
+ eventTime: down_time,
+ ..BASE_KEY_EVENT
+ });
assert!(next.last_event().is_none());
- test_thread.dispatch_next();
- test_thread.move_time_forward(100);
-
- test_thread.stop_looper();
+ std::thread::sleep(Duration::from_nanos(2 * SLOW_KEYS_THRESHOLD_NS as u64));
assert_eq!(
next.last_event().unwrap(),
KeyEvent {
action: KeyEventAction::DOWN,
- downTime: 100,
- eventTime: 100,
+ downTime: down_time + SLOW_KEYS_THRESHOLD_NS,
+ eventTime: down_time + SLOW_KEYS_THRESHOLD_NS,
policyFlags: POLICY_FLAG_DISABLE_KEY_REPEAT,
..BASE_KEY_EVENT
}
);
+
+ let up_time = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds();
+ filter.notify_key(&KeyEvent {
+ action: KeyEventAction::UP,
+ downTime: down_time,
+ eventTime: up_time,
+ ..BASE_KEY_EVENT
+ });
+
+ assert_eq!(
+ next.last_event().unwrap(),
+ KeyEvent {
+ action: KeyEventAction::UP,
+ downTime: down_time + SLOW_KEYS_THRESHOLD_NS,
+ eventTime: up_time,
+ ..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 test_thread = get_thread(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 */
+ 1, /* device_id */
+ SLOW_KEYS_THRESHOLD_NS,
);
- test_thread.start_looper();
+ let mut now = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds();
+ filter.notify_key(&KeyEvent {
+ action: KeyEventAction::DOWN,
+ downTime: now,
+ eventTime: now,
+ ..BASE_KEY_EVENT
+ });
- filter.notify_key(&KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT });
- test_thread.dispatch_next();
+ std::thread::sleep(Duration::from_nanos(SLOW_KEYS_THRESHOLD_NS as u64 / 2));
- test_thread.move_time_forward(10);
+ now = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds();
+ filter.notify_key(&KeyEvent {
+ action: KeyEventAction::UP,
+ downTime: now,
+ eventTime: now,
+ ..BASE_KEY_EVENT
+ });
- 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 test_thread = get_thread(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 */
+ 1, /* device_id */
+ SLOW_KEYS_THRESHOLD_NS,
);
- test_thread.start_looper();
- filter.notify_key(&KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT });
- assert!(next.last_event().is_none());
- test_thread.dispatch_next();
+ let now = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds();
+ filter.notify_key(&KeyEvent {
+ action: KeyEventAction::DOWN,
+ downTime: now,
+ eventTime: now,
+ ..BASE_KEY_EVENT
+ });
filter.notify_devices_changed(&[]);
- test_thread.dispatch_next();
+ std::thread::sleep(Duration::from_nanos(2 * SLOW_KEYS_THRESHOLD_NS as u64));
- 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,
+ test_thread: InputFilterThread,
device_id: i32,
threshold: i64,
) -> SlowKeysFilter {
@@ -367,7 +399,7 @@
fn setup_filter_with_internal_device(
next: Box<dyn Filter + Send + Sync>,
- test_thread: TestThread,
+ test_thread: InputFilterThread,
device_id: i32,
threshold: i64,
) -> SlowKeysFilter {
@@ -381,12 +413,18 @@
fn setup_filter_with_devices(
next: Box<dyn Filter + Send + Sync>,
- test_thread: TestThread,
+ test_thread: InputFilterThread,
devices: &[DeviceInfo],
threshold: i64,
) -> SlowKeysFilter {
- let mut filter = SlowKeysFilter::new(next, threshold, test_thread.get_input_thread());
+ let mut filter = SlowKeysFilter::new(next, threshold, test_thread);
filter.notify_devices_changed(devices);
filter
}
+
+ fn get_thread(callbacks: TestCallbacks) -> InputFilterThread {
+ InputFilterThread::new(InputFilterThreadCreator::new(Arc::new(RwLock::new(Strong::new(
+ Box::new(callbacks),
+ )))))
+ }
}
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 1d46c9a..367bc70 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -9790,163 +9790,13 @@
ASSERT_EQ(SECONDARY_DISPLAY_ID, motionArgs.displayId);
}
-TEST_F(MultiTouchInputMapperTest, Process_TouchpadCapture) {
- // we need a pointer controller for mouse mode of touchpad (start pointer at 0,0)
+TEST_F(MultiTouchInputMapperTest, Process_TouchpadPointer) {
std::shared_ptr<FakePointerController> fakePointerController =
std::make_shared<FakePointerController>();
fakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
fakePointerController->setPosition(0, 0);
- // prepare device and capture
- prepareDisplay(ui::ROTATION_0);
- prepareAxes(POSITION | ID | SLOT);
- mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
- mFakePolicy->setPointerCapture(/*window=*/sp<BBinder>::make());
- mFakePolicy->setPointerController(fakePointerController);
- MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
-
- // captured touchpad should be a touchpad source
- NotifyDeviceResetArgs resetArgs;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
- ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
-
- InputDeviceInfo deviceInfo = mDevice->getDeviceInfo();
-
- const InputDeviceInfo::MotionRange* relRangeX =
- deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_RELATIVE_X, AINPUT_SOURCE_TOUCHPAD);
- ASSERT_NE(relRangeX, nullptr);
- ASSERT_EQ(relRangeX->min, -(RAW_X_MAX - RAW_X_MIN));
- ASSERT_EQ(relRangeX->max, RAW_X_MAX - RAW_X_MIN);
- const InputDeviceInfo::MotionRange* relRangeY =
- deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_RELATIVE_Y, AINPUT_SOURCE_TOUCHPAD);
- ASSERT_NE(relRangeY, nullptr);
- ASSERT_EQ(relRangeY->min, -(RAW_Y_MAX - RAW_Y_MIN));
- ASSERT_EQ(relRangeY->max, RAW_Y_MAX - RAW_Y_MIN);
-
- // run captured pointer tests - note that this is unscaled, so input listener events should be
- // identical to what the hardware sends (accounting for any
- // calibration).
- // FINGER 0 DOWN
- processSlot(mapper, 0);
- processId(mapper, 1);
- processPosition(mapper, 100 + RAW_X_MIN, 100 + RAW_Y_MIN);
- processKey(mapper, BTN_TOUCH, 1);
- processSync(mapper);
-
- // expect coord[0] to contain initial location of touch 0
- NotifyMotionArgs args;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
- ASSERT_EQ(1U, args.getPointerCount());
- ASSERT_EQ(0, args.pointerProperties[0].id);
- ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, args.source);
- ASSERT_NO_FATAL_FAILURE(
- assertPointerCoords(args.pointerCoords[0], 100, 100, 1, 0, 0, 0, 0, 0, 0, 0));
-
- // FINGER 1 DOWN
- processSlot(mapper, 1);
- processId(mapper, 2);
- processPosition(mapper, 560 + RAW_X_MIN, 154 + RAW_Y_MIN);
- processSync(mapper);
-
- // expect coord[0] to contain previous location, coord[1] to contain new touch 1 location
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(ACTION_POINTER_1_DOWN, args.action);
- ASSERT_EQ(2U, args.getPointerCount());
- ASSERT_EQ(0, args.pointerProperties[0].id);
- ASSERT_EQ(1, args.pointerProperties[1].id);
- ASSERT_NO_FATAL_FAILURE(
- assertPointerCoords(args.pointerCoords[0], 100, 100, 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NO_FATAL_FAILURE(
- assertPointerCoords(args.pointerCoords[1], 560, 154, 1, 0, 0, 0, 0, 0, 0, 0));
-
- // FINGER 1 MOVE
- processPosition(mapper, 540 + RAW_X_MIN, 690 + RAW_Y_MIN);
- processSync(mapper);
-
- // expect coord[0] to contain previous location, coord[1] to contain new touch 1 location
- // from move
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
- ASSERT_NO_FATAL_FAILURE(
- assertPointerCoords(args.pointerCoords[0], 100, 100, 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NO_FATAL_FAILURE(
- assertPointerCoords(args.pointerCoords[1], 540, 690, 1, 0, 0, 0, 0, 0, 0, 0));
-
- // FINGER 0 MOVE
- processSlot(mapper, 0);
- processPosition(mapper, 50 + RAW_X_MIN, 800 + RAW_Y_MIN);
- processSync(mapper);
-
- // expect coord[0] to contain new touch 0 location, coord[1] to contain previous location
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
- ASSERT_NO_FATAL_FAILURE(
- assertPointerCoords(args.pointerCoords[0], 50, 800, 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NO_FATAL_FAILURE(
- assertPointerCoords(args.pointerCoords[1], 540, 690, 1, 0, 0, 0, 0, 0, 0, 0));
-
- // BUTTON DOWN
- processKey(mapper, BTN_LEFT, 1);
- processSync(mapper);
-
- // touchinputmapper design sends a move before button press
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, args.action);
-
- // BUTTON UP
- processKey(mapper, BTN_LEFT, 0);
- processSync(mapper);
-
- // touchinputmapper design sends a move after button release
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
-
- // FINGER 0 UP
- processId(mapper, -1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | 0x0000, args.action);
-
- // FINGER 1 MOVE
- processSlot(mapper, 1);
- processPosition(mapper, 320 + RAW_X_MIN, 900 + RAW_Y_MIN);
- processSync(mapper);
-
- // expect coord[0] to contain new location of touch 1, and properties[0].id to contain 1
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
- ASSERT_EQ(1U, args.getPointerCount());
- ASSERT_EQ(1, args.pointerProperties[0].id);
- ASSERT_NO_FATAL_FAILURE(
- assertPointerCoords(args.pointerCoords[0], 320, 900, 1, 0, 0, 0, 0, 0, 0, 0));
-
- // FINGER 1 UP
- processId(mapper, -1);
- processKey(mapper, BTN_TOUCH, 0);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
-
- // non captured touchpad should be a mouse source
- mFakePolicy->setPointerCapture(/*window=*/nullptr);
- configureDevice(InputReaderConfiguration::Change::POINTER_CAPTURE);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
- ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
-}
-
-TEST_F(MultiTouchInputMapperTest, Process_UnCapturedTouchpadPointer) {
- std::shared_ptr<FakePointerController> fakePointerController =
- std::make_shared<FakePointerController>();
- fakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
- fakePointerController->setPosition(0, 0);
-
- // prepare device and capture
+ // prepare device
prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT);
mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0);
@@ -10004,7 +9854,7 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
}
-TEST_F(MultiTouchInputMapperTest, WhenCapturedAndNotCaptured_GetSources) {
+TEST_F(MultiTouchInputMapperTest, Touchpad_GetSources) {
std::shared_ptr<FakePointerController> fakePointerController =
std::make_shared<FakePointerController>();
@@ -10017,11 +9867,6 @@
// uncaptured touchpad should be a pointer device
ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
-
- // captured touchpad should be a touchpad device
- mFakePolicy->setPointerCapture(/*window=*/sp<BBinder>::make());
- configureDevice(InputReaderConfiguration::Change::POINTER_CAPTURE);
- ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
}
// --- BluetoothMultiTouchInputMapperTest ---
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
index 0e3fdbb..10dc927 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <common/FlagManager.h>
#include <compositionengine/impl/planner/LayerState.h>
namespace {
@@ -70,6 +71,10 @@
if (field->getField() == LayerStateField::Buffer) {
continue;
}
+ if (FlagManager::getInstance().cache_when_source_crop_layer_only_moved() &&
+ field->getField() == LayerStateField::SourceCrop) {
+ continue;
+ }
android::hashCombineSingleHashed(hash, field->getHash());
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
index 39fce2b..03758b3 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
@@ -18,6 +18,7 @@
#define LOG_TAG "LayerStateTest"
#include <aidl/android/hardware/graphics/common/BufferUsage.h>
+#include <common/include/common/test/FlagUtils.h>
#include <compositionengine/impl/OutputLayer.h>
#include <compositionengine/impl/planner/LayerState.h>
#include <compositionengine/mock/LayerFE.h>
@@ -26,6 +27,7 @@
#include <log/log.h>
#include "android/hardware_buffer.h"
+#include "com_android_graphics_surfaceflinger_flags.h"
#include "compositionengine/LayerFECompositionState.h"
#include <aidl/android/hardware/graphics/composer3/Composition.h>
@@ -464,6 +466,9 @@
}
TEST_F(LayerStateTest, compareSourceCrop) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::
+ cache_when_source_crop_layer_only_moved,
+ false);
OutputLayerCompositionState outputLayerCompositionState;
outputLayerCompositionState.sourceCrop = sFloatRectOne;
LayerFECompositionState layerFECompositionState;
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp
index 35d0ffb..a1210b4 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp
@@ -18,6 +18,9 @@
#undef LOG_TAG
#define LOG_TAG "PredictorTest"
+#include <common/include/common/test/FlagUtils.h>
+#include "com_android_graphics_surfaceflinger_flags.h"
+
#include <compositionengine/impl/planner/Predictor.h>
#include <compositionengine/mock/LayerFE.h>
#include <compositionengine/mock/OutputLayer.h>
@@ -127,6 +130,9 @@
}
TEST_F(LayerStackTest, getApproximateMatch_matchesSingleDifferenceInSingleLayer) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::
+ cache_when_source_crop_layer_only_moved,
+ false);
mock::OutputLayer outputLayerOne;
sp<mock::LayerFE> layerFEOne = sp<mock::LayerFE>::make();
OutputLayerCompositionState outputLayerCompositionStateOne{
@@ -158,6 +164,9 @@
}
TEST_F(LayerStackTest, getApproximateMatch_matchesSingleDifferenceInMultiLayerStack) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::
+ cache_when_source_crop_layer_only_moved,
+ false);
mock::OutputLayer outputLayerOne;
sp<mock::LayerFE> layerFEOne = sp<mock::LayerFE>::make();
OutputLayerCompositionState outputLayerCompositionStateOne{
@@ -304,6 +313,9 @@
}
TEST_F(LayerStackTest, getApproximateMatch_doesNotMatchMultipleApproximations) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::
+ cache_when_source_crop_layer_only_moved,
+ false);
mock::OutputLayer outputLayerOne;
sp<mock::LayerFE> layerFEOne = sp<mock::LayerFE>::make();
OutputLayerCompositionState outputLayerCompositionStateOne{
@@ -347,6 +359,9 @@
};
TEST_F(LayerStackTest, reorderingChangesNonBufferHash) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::
+ cache_when_source_crop_layer_only_moved,
+ false);
mock::OutputLayer outputLayerOne;
sp<mock::LayerFE> layerFEOne = sp<mock::LayerFE>::make();
OutputLayerCompositionState outputLayerCompositionStateOne{
@@ -467,6 +482,9 @@
}
TEST_F(PredictorTest, getPredictedPlan_recordCandidateAndRetrieveApproximateMatch) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::
+ cache_when_source_crop_layer_only_moved,
+ false);
mock::OutputLayer outputLayerOne;
sp<mock::LayerFE> layerFEOne = sp<mock::LayerFE>::make();
OutputLayerCompositionState outputLayerCompositionStateOne{
@@ -504,6 +522,9 @@
}
TEST_F(PredictorTest, recordMissedPlan_skipsApproximateMatch) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::
+ cache_when_source_crop_layer_only_moved,
+ false);
mock::OutputLayer outputLayerOne;
sp<mock::LayerFE> layerFEOne = sp<mock::LayerFE>::make();
OutputLayerCompositionState outputLayerCompositionStateOne{
diff --git a/services/surfaceflinger/OWNERS b/services/surfaceflinger/OWNERS
index 0aee7d4..ffc1dd7 100644
--- a/services/surfaceflinger/OWNERS
+++ b/services/surfaceflinger/OWNERS
@@ -2,7 +2,6 @@
adyabr@google.com
alecmouri@google.com
-chaviw@google.com
domlaskowski@google.com
jreck@google.com
lpy@google.com
@@ -10,5 +9,6 @@
racarr@google.com
ramindani@google.com
rnlee@google.com
+sallyqi@google.com
scroggo@google.com
vishnun@google.com
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 96eccf2..6b65449 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -235,7 +235,8 @@
ParcelableVsyncEventData* outVsyncEventData) {
ATRACE_CALL();
outVsyncEventData->vsync =
- mEventThread->getLatestVsyncEventData(sp<EventThreadConnection>::fromExisting(this));
+ mEventThread->getLatestVsyncEventData(sp<EventThreadConnection>::fromExisting(this),
+ systemTime());
return binder::Status::ok();
}
@@ -387,8 +388,8 @@
}
}
-VsyncEventData EventThread::getLatestVsyncEventData(
- const sp<EventThreadConnection>& connection) const {
+VsyncEventData EventThread::getLatestVsyncEventData(const sp<EventThreadConnection>& connection,
+ nsecs_t now) const {
// Resync so that the vsync is accurate with hardware. getLatestVsyncEventData is an alternate
// way to get vsync data (instead of posting callbacks to Choreographer).
mCallback.resync();
@@ -399,11 +400,10 @@
const auto [presentTime, deadline] = [&]() -> std::pair<nsecs_t, nsecs_t> {
std::lock_guard<std::mutex> lock(mMutex);
const auto vsyncTime = mVsyncSchedule->getTracker().nextAnticipatedVSyncTimeFrom(
- systemTime() + mWorkDuration.get().count() + mReadyDuration.count());
+ now + mWorkDuration.get().count() + mReadyDuration.count());
return {vsyncTime, vsyncTime - mReadyDuration.count()};
}();
- generateFrameTimeline(vsyncEventData, frameInterval.ns(), systemTime(SYSTEM_TIME_MONOTONIC),
- presentTime, deadline);
+ generateFrameTimeline(vsyncEventData, frameInterval.ns(), now, presentTime, deadline);
if (FlagManager::getInstance().vrr_config()) {
mCallback.onExpectedPresentTimePosted(TimePoint::fromNs(presentTime));
}
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 90e61a9..f772126 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -127,8 +127,8 @@
virtual void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) = 0;
// Requests the next vsync. If resetIdleTimer is set to true, it resets the idle timer.
virtual void requestNextVsync(const sp<EventThreadConnection>& connection) = 0;
- virtual VsyncEventData getLatestVsyncEventData(
- const sp<EventThreadConnection>& connection) const = 0;
+ virtual VsyncEventData getLatestVsyncEventData(const sp<EventThreadConnection>& connection,
+ nsecs_t now) const = 0;
virtual void onNewVsyncSchedule(std::shared_ptr<scheduler::VsyncSchedule>) = 0;
@@ -160,8 +160,8 @@
status_t registerDisplayEventConnection(const sp<EventThreadConnection>& connection) override;
void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) override;
void requestNextVsync(const sp<EventThreadConnection>& connection) override;
- VsyncEventData getLatestVsyncEventData(
- const sp<EventThreadConnection>& connection) const override;
+ VsyncEventData getLatestVsyncEventData(const sp<EventThreadConnection>& connection,
+ nsecs_t now) const override;
void enableSyntheticVsync(bool) override;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 81697da..21f1cb3 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -72,6 +72,7 @@
#include <gui/TraceUtils.h>
#include <hidl/ServiceManagement.h>
#include <layerproto/LayerProtoParser.h>
+#include <linux/sched/types.h>
#include <log/log.h>
#include <private/android_filesystem_config.h>
#include <private/gui/SyncFeatures.h>
@@ -2073,10 +2074,17 @@
sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
gui::ISurfaceComposer::VsyncSource vsyncSource, EventRegistrationFlags eventRegistration,
const sp<IBinder>& layerHandle) {
- const auto cycle = vsyncSource == gui::ISurfaceComposer::VsyncSource::eVsyncSourceSurfaceFlinger
- ? scheduler::Cycle::LastComposite
- : scheduler::Cycle::Render;
+ const auto cycle = [&] {
+ if (FlagManager::getInstance().deprecate_vsync_sf()) {
+ ALOGW_IF(vsyncSource == gui::ISurfaceComposer::VsyncSource::eVsyncSourceSurfaceFlinger,
+ "requested unsupported config eVsyncSourceSurfaceFlinger");
+ return scheduler::Cycle::Render;
+ }
+ return vsyncSource == gui::ISurfaceComposer::VsyncSource::eVsyncSourceSurfaceFlinger
+ ? scheduler::Cycle::LastComposite
+ : scheduler::Cycle::Render;
+ }();
return mScheduler->createDisplayEventConnection(cycle, eventRegistration, layerHandle);
}
@@ -3171,7 +3179,8 @@
if (mLayerLifecycleManagerEnabled) {
mLayerSnapshotBuilder.forEachVisibleSnapshot(
[&, compositionDisplay = compositionDisplay](
- std::unique_ptr<frontend::LayerSnapshot>& snapshot) {
+ std::unique_ptr<frontend::LayerSnapshot>&
+ snapshot) FTL_FAKE_GUARD(kMainThreadContext) {
auto it = mLegacyLayers.find(snapshot->sequence);
LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mLegacyLayers.end(),
"Couldnt find layer object for %s",
@@ -3230,7 +3239,7 @@
if (mNumTrustedPresentationListeners > 0) {
// We avoid any reverse traversal upwards so this shouldn't be too expensive
- traverseLegacyLayers([&](Layer* layer) {
+ traverseLegacyLayers([&](Layer* layer) FTL_FAKE_GUARD(kMainThreadContext) {
if (!layer->hasTrustedPresentationListener()) {
return;
}
@@ -4143,7 +4152,7 @@
outWindowInfos.push_back(snapshot.inputInfo);
});
} else {
- mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
+ mDrawingState.traverseInReverseZOrder([&](Layer* layer) FTL_FAKE_GUARD(kMainThreadContext) {
if (!layer->needsInputInfo()) return;
const auto opt =
mFrontEndDisplayInfos.get(layer->getLayerStack())
@@ -4848,6 +4857,8 @@
if (listener &&
(flushState.queueProcessTime - transaction.postTime) >
std::chrono::nanoseconds(4s).count()) {
+ // Used to add a stalled transaction which uses an internal lock.
+ ftl::FakeGuard guard(kMainThreadContext);
mTransactionHandler
.onTransactionQueueStalled(transaction.id,
{.pid = layer->getOwnerPid(),
@@ -4870,97 +4881,107 @@
const TransactionHandler::TransactionFlushState& flushState) {
using TransactionReadiness = TransactionHandler::TransactionReadiness;
auto ready = TransactionReadiness::Ready;
- flushState.transaction->traverseStatesWithBuffersWhileTrue([&](const ResolvedComposerState&
- resolvedState) -> bool {
- const frontend::RequestedLayerState* layer =
- mLayerLifecycleManager.getLayerFromId(resolvedState.layerId);
- const auto& transaction = *flushState.transaction;
- const auto& s = resolvedState.state;
- // check for barrier frames
- if (s.bufferData->hasBarrier) {
- // The current producerId is already a newer producer than the buffer that has a
- // barrier. This means the incoming buffer is older and we can release it here. We
- // don't wait on the barrier since we know that's stale information.
- if (layer->barrierProducerId > s.bufferData->producerId) {
- if (s.bufferData->releaseBufferListener) {
- uint32_t currentMaxAcquiredBufferCount =
- getMaxAcquiredBufferCountForCurrentRefreshRate(layer->ownerUid.val());
- ATRACE_FORMAT_INSTANT("callReleaseBufferCallback %s - %" PRIu64,
- layer->name.c_str(), s.bufferData->frameNumber);
- s.bufferData->releaseBufferListener
- ->onReleaseBuffer({resolvedState.externalTexture->getBuffer()->getId(),
- s.bufferData->frameNumber},
- s.bufferData->acquireFence
- ? s.bufferData->acquireFence
- : Fence::NO_FENCE,
- currentMaxAcquiredBufferCount);
+ flushState.transaction->traverseStatesWithBuffersWhileTrue(
+ [&](const ResolvedComposerState& resolvedState) FTL_FAKE_GUARD(
+ kMainThreadContext) -> bool {
+ const frontend::RequestedLayerState* layer =
+ mLayerLifecycleManager.getLayerFromId(resolvedState.layerId);
+ const auto& transaction = *flushState.transaction;
+ const auto& s = resolvedState.state;
+ // check for barrier frames
+ if (s.bufferData->hasBarrier) {
+ // The current producerId is already a newer producer than the buffer that has a
+ // barrier. This means the incoming buffer is older and we can release it here.
+ // We don't wait on the barrier since we know that's stale information.
+ if (layer->barrierProducerId > s.bufferData->producerId) {
+ if (s.bufferData->releaseBufferListener) {
+ uint32_t currentMaxAcquiredBufferCount =
+ getMaxAcquiredBufferCountForCurrentRefreshRate(
+ layer->ownerUid.val());
+ ATRACE_FORMAT_INSTANT("callReleaseBufferCallback %s - %" PRIu64,
+ layer->name.c_str(), s.bufferData->frameNumber);
+ s.bufferData->releaseBufferListener
+ ->onReleaseBuffer({resolvedState.externalTexture->getBuffer()
+ ->getId(),
+ s.bufferData->frameNumber},
+ s.bufferData->acquireFence
+ ? s.bufferData->acquireFence
+ : Fence::NO_FENCE,
+ currentMaxAcquiredBufferCount);
+ }
+
+ // Delete the entire state at this point and not just release the buffer
+ // because everything associated with the Layer in this Transaction is now
+ // out of date.
+ ATRACE_FORMAT("DeleteStaleBuffer %s barrierProducerId:%d > %d",
+ layer->name.c_str(), layer->barrierProducerId,
+ s.bufferData->producerId);
+ return TraverseBuffersReturnValues::DELETE_AND_CONTINUE_TRAVERSAL;
+ }
+
+ if (layer->barrierFrameNumber < s.bufferData->barrierFrameNumber) {
+ const bool willApplyBarrierFrame =
+ flushState.bufferLayersReadyToPresent.contains(s.surface.get()) &&
+ ((flushState.bufferLayersReadyToPresent.get(s.surface.get()) >=
+ s.bufferData->barrierFrameNumber));
+ if (!willApplyBarrierFrame) {
+ ATRACE_FORMAT("NotReadyBarrier %s barrierFrameNumber:%" PRId64
+ " > %" PRId64,
+ layer->name.c_str(), layer->barrierFrameNumber,
+ s.bufferData->barrierFrameNumber);
+ ready = TransactionReadiness::NotReadyBarrier;
+ return TraverseBuffersReturnValues::STOP_TRAVERSAL;
+ }
+ }
}
- // Delete the entire state at this point and not just release the buffer because
- // everything associated with the Layer in this Transaction is now out of date.
- ATRACE_FORMAT("DeleteStaleBuffer %s barrierProducerId:%d > %d", layer->name.c_str(),
- layer->barrierProducerId, s.bufferData->producerId);
- return TraverseBuffersReturnValues::DELETE_AND_CONTINUE_TRAVERSAL;
- }
-
- if (layer->barrierFrameNumber < s.bufferData->barrierFrameNumber) {
- const bool willApplyBarrierFrame =
- flushState.bufferLayersReadyToPresent.contains(s.surface.get()) &&
- ((flushState.bufferLayersReadyToPresent.get(s.surface.get()) >=
- s.bufferData->barrierFrameNumber));
- if (!willApplyBarrierFrame) {
- ATRACE_FORMAT("NotReadyBarrier %s barrierFrameNumber:%" PRId64 " > %" PRId64,
- layer->name.c_str(), layer->barrierFrameNumber,
- s.bufferData->barrierFrameNumber);
- ready = TransactionReadiness::NotReadyBarrier;
+ // If backpressure is enabled and we already have a buffer to commit, keep
+ // the transaction in the queue.
+ const bool hasPendingBuffer =
+ flushState.bufferLayersReadyToPresent.contains(s.surface.get());
+ if (layer->backpressureEnabled() && hasPendingBuffer &&
+ transaction.isAutoTimestamp) {
+ ATRACE_FORMAT("hasPendingBuffer %s", layer->name.c_str());
+ ready = TransactionReadiness::NotReady;
return TraverseBuffersReturnValues::STOP_TRAVERSAL;
}
- }
- }
- // If backpressure is enabled and we already have a buffer to commit, keep
- // the transaction in the queue.
- const bool hasPendingBuffer =
- flushState.bufferLayersReadyToPresent.contains(s.surface.get());
- if (layer->backpressureEnabled() && hasPendingBuffer && transaction.isAutoTimestamp) {
- ATRACE_FORMAT("hasPendingBuffer %s", layer->name.c_str());
- ready = TransactionReadiness::NotReady;
- return TraverseBuffersReturnValues::STOP_TRAVERSAL;
- }
-
- const bool acquireFenceAvailable = s.bufferData &&
- s.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged) &&
- s.bufferData->acquireFence;
- const bool fenceSignaled = !acquireFenceAvailable ||
- s.bufferData->acquireFence->getStatus() != Fence::Status::Unsignaled;
- if (!fenceSignaled) {
- // check fence status
- const bool allowLatchUnsignaled = shouldLatchUnsignaled(s, transaction.states.size(),
- flushState.firstTransaction) &&
- layer->isSimpleBufferUpdate(s);
- if (allowLatchUnsignaled) {
- ATRACE_FORMAT("fence unsignaled try allowLatchUnsignaled %s", layer->name.c_str());
- ready = TransactionReadiness::NotReadyUnsignaled;
- } else {
- ready = TransactionReadiness::NotReady;
- auto& listener = s.bufferData->releaseBufferListener;
- if (listener &&
- (flushState.queueProcessTime - transaction.postTime) >
- std::chrono::nanoseconds(4s).count()) {
- mTransactionHandler
- .onTransactionQueueStalled(transaction.id,
- {.pid = layer->ownerPid.val(),
- .layerId = layer->id,
- .layerName = layer->name,
- .bufferId = s.bufferData->getId(),
- .frameNumber = s.bufferData->frameNumber});
+ const bool acquireFenceAvailable = s.bufferData &&
+ s.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged) &&
+ s.bufferData->acquireFence;
+ const bool fenceSignaled = !acquireFenceAvailable ||
+ s.bufferData->acquireFence->getStatus() != Fence::Status::Unsignaled;
+ if (!fenceSignaled) {
+ // check fence status
+ const bool allowLatchUnsignaled =
+ shouldLatchUnsignaled(s, transaction.states.size(),
+ flushState.firstTransaction) &&
+ layer->isSimpleBufferUpdate(s);
+ if (allowLatchUnsignaled) {
+ ATRACE_FORMAT("fence unsignaled try allowLatchUnsignaled %s",
+ layer->name.c_str());
+ ready = TransactionReadiness::NotReadyUnsignaled;
+ } else {
+ ready = TransactionReadiness::NotReady;
+ auto& listener = s.bufferData->releaseBufferListener;
+ if (listener &&
+ (flushState.queueProcessTime - transaction.postTime) >
+ std::chrono::nanoseconds(4s).count()) {
+ mTransactionHandler
+ .onTransactionQueueStalled(transaction.id,
+ {.pid = layer->ownerPid.val(),
+ .layerId = layer->id,
+ .layerName = layer->name,
+ .bufferId = s.bufferData->getId(),
+ .frameNumber =
+ s.bufferData->frameNumber});
+ }
+ ATRACE_FORMAT("fence unsignaled %s", layer->name.c_str());
+ return TraverseBuffersReturnValues::STOP_TRAVERSAL;
+ }
}
- ATRACE_FORMAT("fence unsignaled %s", layer->name.c_str());
- return TraverseBuffersReturnValues::STOP_TRAVERSAL;
- }
- }
- return TraverseBuffersReturnValues::CONTINUE_TRAVERSAL;
- });
+ return TraverseBuffersReturnValues::CONTINUE_TRAVERSAL;
+ });
return ready;
}
@@ -5181,7 +5202,13 @@
}(state.flags);
const auto frameHint = state.isFrameActive() ? FrameHint::kActive : FrameHint::kNone;
- mTransactionHandler.queueTransaction(std::move(state));
+ {
+ // Transactions are added via a lockless queue and does not need to be added from the main
+ // thread.
+ ftl::FakeGuard guard(kMainThreadContext);
+ mTransactionHandler.queueTransaction(std::move(state));
+ }
+
for (const auto& [displayId, data] : mNotifyExpectedPresentMap) {
if (data.hintStatus.load() == NotifyExpectedPresentHintStatus::ScheduleOnTx) {
scheduleNotifyExpectedPresentHint(displayId, VsyncId{frameTimelineInfo.vsyncId});
@@ -5226,17 +5253,19 @@
desiredPresentTime, isAutoTimestamp,
postTime, transactionId);
}
- if ((flags & eAnimation) && resolvedState.state.surface) {
- if (const auto layer = LayerHandle::getLayer(resolvedState.state.surface)) {
- const auto layerProps = scheduler::LayerProps{
- .visible = layer->isVisible(),
- .bounds = layer->getBounds(),
- .transform = layer->getTransform(),
- .setFrameRateVote = layer->getFrameRateForLayerTree(),
- .frameRateSelectionPriority = layer->getFrameRateSelectionPriority(),
- .isFrontBuffered = layer->isFrontBuffered(),
- };
- layer->recordLayerHistoryAnimationTx(layerProps, now);
+ if (!mLayerLifecycleManagerEnabled) {
+ if ((flags & eAnimation) && resolvedState.state.surface) {
+ if (const auto layer = LayerHandle::getLayer(resolvedState.state.surface)) {
+ const auto layerProps = scheduler::LayerProps{
+ .visible = layer->isVisible(),
+ .bounds = layer->getBounds(),
+ .transform = layer->getTransform(),
+ .setFrameRateVote = layer->getFrameRateForLayerTree(),
+ .frameRateSelectionPriority = layer->getFrameRateSelectionPriority(),
+ .isFrontBuffered = layer->isFrontBuffered(),
+ };
+ layer->recordLayerHistoryAnimationTx(layerProps, now);
+ }
}
}
}
@@ -6023,7 +6052,11 @@
mDestroyedHandles.emplace_back(layerId, layer->getDebugName());
}
- mTransactionHandler.onLayerDestroyed(layerId);
+ {
+ // Used to remove stalled transactions which uses an internal lock.
+ ftl::FakeGuard guard(kMainThreadContext);
+ mTransactionHandler.onLayerDestroyed(layerId);
+ }
Mutex::Autolock lock(mStateLock);
markLayerPendingRemovalLocked(layer);
@@ -6675,7 +6708,11 @@
}
perfetto::protos::LayersProto SurfaceFlinger::dumpProtoFromMainThread(uint32_t traceFlags) {
- return mScheduler->schedule([=, this] { return dumpDrawingStateProto(traceFlags); }).get();
+ return mScheduler
+ ->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) {
+ return dumpDrawingStateProto(traceFlags);
+ })
+ .get();
}
void SurfaceFlinger::dumpOffscreenLayers(std::string& result) {
@@ -6724,17 +6761,18 @@
Layer::miniDumpHeader(result);
const DisplayDevice& ref = *display;
- mLayerSnapshotBuilder.forEachVisibleSnapshot([&](const frontend::LayerSnapshot& snapshot) {
- if (!snapshot.hasSomethingToDraw() ||
- ref.getLayerStack() != snapshot.outputFilter.layerStack) {
- return;
- }
- auto it = mLegacyLayers.find(snapshot.sequence);
- LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mLegacyLayers.end(),
- "Couldnt find layer object for %s",
- snapshot.getDebugString().c_str());
- it->second->miniDump(result, snapshot, ref);
- });
+ mLayerSnapshotBuilder.forEachVisibleSnapshot(
+ [&](const frontend::LayerSnapshot& snapshot) FTL_FAKE_GUARD(kMainThreadContext) {
+ if (!snapshot.hasSomethingToDraw() ||
+ ref.getLayerStack() != snapshot.outputFilter.layerStack) {
+ return;
+ }
+ auto it = mLegacyLayers.find(snapshot.sequence);
+ LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mLegacyLayers.end(),
+ "Couldnt find layer object for %s",
+ snapshot.getDebugString().c_str());
+ it->second->miniDump(result, snapshot, ref);
+ });
result.append("\n");
}
}
@@ -7770,20 +7808,6 @@
return NO_ERROR;
}
- // Currently, there is no wrapper in bionic: b/183240349.
- struct sched_attr {
- uint32_t size;
- uint32_t sched_policy;
- uint64_t sched_flags;
- int32_t sched_nice;
- uint32_t sched_priority;
- uint64_t sched_runtime;
- uint64_t sched_deadline;
- uint64_t sched_period;
- uint32_t sched_util_min;
- uint32_t sched_util_max;
- };
-
sched_attr attr = {};
attr.size = sizeof(attr);
@@ -8050,7 +8074,8 @@
}
bool childrenOnly = args.childrenOnly;
- RenderAreaFuture renderAreaFuture = ftl::defer([=, this]() -> std::unique_ptr<RenderArea> {
+ RenderAreaFuture renderAreaFuture = ftl::defer([=, this]() FTL_FAKE_GUARD(kMainThreadContext)
+ -> std::unique_ptr<RenderArea> {
ui::Transform layerTransform;
Rect layerBufferSize;
if (mLayerLifecycleManagerEnabled) {
@@ -9022,6 +9047,8 @@
status_t SurfaceFlinger::getStalledTransactionInfo(
int pid, std::optional<TransactionHandler::StalledTransactionInfo>& result) {
+ // Used to add a stalled transaction which uses an internal lock.
+ ftl::FakeGuard guard(kMainThreadContext);
result = mTransactionHandler.getStalledTransactionInfo(pid);
return NO_ERROR;
}
@@ -9222,7 +9249,8 @@
if (mLayerLifecycleManagerEnabled) {
nsecs_t currentTime = systemTime();
mLayerSnapshotBuilder.forEachVisibleSnapshot(
- [&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) {
+ [&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) FTL_FAKE_GUARD(
+ kMainThreadContext) {
if (cursorOnly &&
snapshot->compositionType !=
aidl::android::hardware::graphics::composer3::Composition::CURSOR) {
@@ -9283,11 +9311,12 @@
std::optional<ui::LayerStack> layerStack, uint32_t uid,
std::function<bool(const frontend::LayerSnapshot&, bool& outStopTraversal)>
snapshotFilterFn) {
- return [&, layerStack, uid]() {
+ return [&, layerStack, uid]() FTL_FAKE_GUARD(kMainThreadContext) {
std::vector<std::pair<Layer*, sp<LayerFE>>> layers;
bool stopTraversal = false;
mLayerSnapshotBuilder.forEachVisibleSnapshot(
- [&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) {
+ [&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) FTL_FAKE_GUARD(
+ kMainThreadContext) {
if (stopTraversal) {
return;
}
@@ -9322,7 +9351,8 @@
SurfaceFlinger::getLayerSnapshotsForScreenshots(std::optional<ui::LayerStack> layerStack,
uint32_t uid,
std::unordered_set<uint32_t> excludeLayerIds) {
- return [&, layerStack, uid, excludeLayerIds = std::move(excludeLayerIds)]() {
+ return [&, layerStack, uid,
+ excludeLayerIds = std::move(excludeLayerIds)]() FTL_FAKE_GUARD(kMainThreadContext) {
if (excludeLayerIds.empty()) {
auto getLayerSnapshotsFn =
getLayerSnapshotsForScreenshots(layerStack, uid, /*snapshotFilterFn=*/nullptr);
@@ -9364,7 +9394,7 @@
bool childrenOnly,
const std::optional<FloatRect>& parentCrop) {
return [&, rootLayerId, uid, excludeLayerIds = std::move(excludeLayerIds), childrenOnly,
- parentCrop]() {
+ parentCrop]() FTL_FAKE_GUARD(kMainThreadContext) {
auto root = mLayerHierarchyBuilder.getPartialHierarchy(rootLayerId, childrenOnly);
frontend::LayerSnapshotBuilder::Args
args{.root = root,
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 4a44be6..44fa806 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -770,24 +770,27 @@
void updateLayerGeometry();
void updateLayerMetadataSnapshot();
std::vector<std::pair<Layer*, LayerFE*>> moveSnapshotsToCompositionArgs(
- compositionengine::CompositionRefreshArgs& refreshArgs, bool cursorOnly);
+ compositionengine::CompositionRefreshArgs& refreshArgs, bool cursorOnly)
+ REQUIRES(kMainThreadContext);
void moveSnapshotsFromCompositionArgs(compositionengine::CompositionRefreshArgs& refreshArgs,
- const std::vector<std::pair<Layer*, LayerFE*>>& layers);
+ const std::vector<std::pair<Layer*, LayerFE*>>& layers)
+ REQUIRES(kMainThreadContext);
// Return true if we must composite this frame
bool updateLayerSnapshotsLegacy(VsyncId vsyncId, nsecs_t frameTimeNs, bool transactionsFlushed,
bool& out) REQUIRES(kMainThreadContext);
// Return true if we must composite this frame
bool updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, bool transactionsFlushed,
bool& out) REQUIRES(kMainThreadContext);
- void updateLayerHistory(nsecs_t now);
+ void updateLayerHistory(nsecs_t now) REQUIRES(kMainThreadContext);
frontend::Update flushLifecycleUpdates() REQUIRES(kMainThreadContext);
- void updateInputFlinger(VsyncId vsyncId, TimePoint frameTime);
+ void updateInputFlinger(VsyncId vsyncId, TimePoint frameTime) REQUIRES(kMainThreadContext);
void persistDisplayBrightness(bool needsComposite) REQUIRES(kMainThreadContext);
void buildWindowInfos(std::vector<gui::WindowInfo>& outWindowInfos,
- std::vector<gui::DisplayInfo>& outDisplayInfos);
+ std::vector<gui::DisplayInfo>& outDisplayInfos)
+ REQUIRES(kMainThreadContext);
void commitInputWindowCommands() REQUIRES(mStateLock);
- void updateCursorAsync();
+ void updateCursorAsync() REQUIRES(kMainThreadContext);
void initScheduler(const sp<const DisplayDevice>&) REQUIRES(kMainThreadContext, mStateLock);
@@ -803,7 +806,7 @@
const int64_t postTime, bool hasListenerCallbacks,
const std::vector<ListenerCallbacks>& listenerCallbacks,
int originPid, int originUid, uint64_t transactionId)
- REQUIRES(mStateLock);
+ REQUIRES(mStateLock, kMainThreadContext);
// Flush pending transactions that were presented after desiredPresentTime.
// For test only
bool flushTransactionQueues(VsyncId) REQUIRES(kMainThreadContext);
@@ -813,8 +816,8 @@
REQUIRES(kMainThreadContext, mStateLock);
// Returns true if there is at least one transaction that needs to be flushed
- bool transactionFlushNeeded();
- void addTransactionReadyFilters();
+ bool transactionFlushNeeded() REQUIRES(kMainThreadContext);
+ void addTransactionReadyFilters() REQUIRES(kMainThreadContext);
TransactionHandler::TransactionReadiness transactionReadyTimelineCheck(
const TransactionHandler::TransactionFlushState& flushState)
REQUIRES(kMainThreadContext);
@@ -831,7 +834,7 @@
uint32_t updateLayerCallbacksAndStats(const FrameTimelineInfo&, ResolvedComposerState&,
int64_t desiredPresentTime, bool isAutoTimestamp,
int64_t postTime, uint64_t transactionId)
- REQUIRES(mStateLock);
+ REQUIRES(mStateLock, kMainThreadContext);
uint32_t getTransactionFlags() const;
// Sets the masked bits, and schedules a commit if needed.
@@ -847,7 +850,7 @@
static LatchUnsignaledConfig getLatchUnsignaledConfig();
bool shouldLatchUnsignaled(const layer_state_t&, size_t numStates, bool firstTransaction) const;
bool applyTransactionsLocked(std::vector<TransactionState>& transactions, VsyncId)
- REQUIRES(mStateLock);
+ REQUIRES(mStateLock, kMainThreadContext);
uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands)
REQUIRES(mStateLock);
@@ -1136,9 +1139,10 @@
void dumpHwcLayersMinidumpLockedLegacy(std::string& result) const REQUIRES(mStateLock);
void appendSfConfigString(std::string& result) const;
- void listLayers(std::string& result) const;
- void dumpStats(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock);
- void clearStats(const DumpArgs& args, std::string& result);
+ void listLayers(std::string& result) const REQUIRES(kMainThreadContext);
+ void dumpStats(const DumpArgs& args, std::string& result) const
+ REQUIRES(mStateLock, kMainThreadContext);
+ void clearStats(const DumpArgs& args, std::string& result) REQUIRES(kMainThreadContext);
void dumpTimeStats(const DumpArgs& args, bool asProto, std::string& result) const;
void dumpFrameTimeline(const DumpArgs& args, std::string& result) const;
void logFrameStats(TimePoint now) REQUIRES(kMainThreadContext);
@@ -1156,7 +1160,8 @@
void dumpFrontEnd(std::string& result) REQUIRES(kMainThreadContext);
void dumpVisibleFrontEnd(std::string& result) REQUIRES(mStateLock, kMainThreadContext);
- perfetto::protos::LayersProto dumpDrawingStateProto(uint32_t traceFlags) const;
+ perfetto::protos::LayersProto dumpDrawingStateProto(uint32_t traceFlags) const
+ REQUIRES(kMainThreadContext);
void dumpOffscreenLayersProto(perfetto::protos::LayersProto& layersProto,
uint32_t traceFlags = LayerTracing::TRACE_ALL) const;
google::protobuf::RepeatedPtrField<perfetto::protos::DisplayProto> dumpDisplayProto() const;
@@ -1204,7 +1209,8 @@
ui::Rotation getPhysicalDisplayOrientation(DisplayId, bool isPrimary) const
REQUIRES(mStateLock);
- void traverseLegacyLayers(const LayerVector::Visitor& visitor) const;
+ void traverseLegacyLayers(const LayerVector::Visitor& visitor) const
+ REQUIRES(kMainThreadContext);
void initBootProperties();
void initTransactionTraceWriter();
@@ -1482,23 +1488,25 @@
bool mLayerLifecycleManagerEnabled = false;
bool mLegacyFrontEndEnabled = true;
- frontend::LayerLifecycleManager mLayerLifecycleManager;
- frontend::LayerHierarchyBuilder mLayerHierarchyBuilder;
- frontend::LayerSnapshotBuilder mLayerSnapshotBuilder;
+ frontend::LayerLifecycleManager mLayerLifecycleManager GUARDED_BY(kMainThreadContext);
+ frontend::LayerHierarchyBuilder mLayerHierarchyBuilder GUARDED_BY(kMainThreadContext);
+ frontend::LayerSnapshotBuilder mLayerSnapshotBuilder GUARDED_BY(kMainThreadContext);
- std::vector<std::pair<uint32_t, std::string>> mDestroyedHandles;
- std::vector<std::unique_ptr<frontend::RequestedLayerState>> mNewLayers;
- std::vector<LayerCreationArgs> mNewLayerArgs;
+ std::vector<std::pair<uint32_t, std::string>> mDestroyedHandles GUARDED_BY(mCreatedLayersLock);
+ std::vector<std::unique_ptr<frontend::RequestedLayerState>> mNewLayers
+ GUARDED_BY(mCreatedLayersLock);
+ std::vector<LayerCreationArgs> mNewLayerArgs GUARDED_BY(mCreatedLayersLock);
// These classes do not store any client state but help with managing transaction callbacks
// and stats.
- std::unordered_map<uint32_t, sp<Layer>> mLegacyLayers;
+ std::unordered_map<uint32_t, sp<Layer>> mLegacyLayers GUARDED_BY(kMainThreadContext);
- TransactionHandler mTransactionHandler;
- ui::DisplayMap<ui::LayerStack, frontend::DisplayInfo> mFrontEndDisplayInfos;
- bool mFrontEndDisplayInfosChanged = false;
+ TransactionHandler mTransactionHandler GUARDED_BY(kMainThreadContext);
+ ui::DisplayMap<ui::LayerStack, frontend::DisplayInfo> mFrontEndDisplayInfos
+ GUARDED_BY(kMainThreadContext);
+ bool mFrontEndDisplayInfosChanged GUARDED_BY(kMainThreadContext) = false;
// WindowInfo ids visible during the last commit.
- std::unordered_set<int32_t> mVisibleWindowIds;
+ std::unordered_set<int32_t> mVisibleWindowIds GUARDED_BY(kMainThreadContext);
// Mirroring
// Map of displayid to mirrorRoot
diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/TransactionState.h
index 31cd2d7..89a8f92 100644
--- a/services/surfaceflinger/TransactionState.h
+++ b/services/surfaceflinger/TransactionState.h
@@ -86,7 +86,7 @@
}
template <typename Visitor>
- void traverseStatesWithBuffersWhileTrue(Visitor&& visitor) {
+ void traverseStatesWithBuffersWhileTrue(Visitor&& visitor) NO_THREAD_SAFETY_ANALYSIS {
for (auto state = states.begin(); state != states.end();) {
if (state->state.hasBufferChanges() && state->externalTexture && state->state.surface) {
int result = visitor(*state);
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index 12043d4..5a54f7d 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -141,6 +141,7 @@
DUMP_READ_ONLY_FLAG(idle_screen_refresh_rate_timeout);
DUMP_READ_ONLY_FLAG(graphite_renderengine);
DUMP_READ_ONLY_FLAG(latch_unsignaled_with_auto_refresh_changed);
+ DUMP_READ_ONLY_FLAG(deprecate_vsync_sf);
#undef DUMP_READ_ONLY_FLAG
#undef DUMP_SERVER_FLAG
@@ -232,6 +233,7 @@
FLAG_MANAGER_READ_ONLY_FLAG(ce_fence_promise, "");
FLAG_MANAGER_READ_ONLY_FLAG(graphite_renderengine, "debug.renderengine.graphite")
FLAG_MANAGER_READ_ONLY_FLAG(latch_unsignaled_with_auto_refresh_changed, "");
+FLAG_MANAGER_READ_ONLY_FLAG(deprecate_vsync_sf, "");
/// Trunk stable server flags ///
FLAG_MANAGER_SERVER_FLAG(refresh_rate_overlay_on_external_display, "")
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index 0239eb0..0c1f9fb 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -79,6 +79,7 @@
bool idle_screen_refresh_rate_timeout() const;
bool graphite_renderengine() const;
bool latch_unsignaled_with_auto_refresh_changed() const;
+ bool deprecate_vsync_sf() const;
protected:
// overridden for unit tests
diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
index 0b9fd58..4a60987 100644
--- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
@@ -21,6 +21,17 @@
}
} # ce_fence_promise
+ flag {
+ name: "deprecate_vsync_sf"
+ namespace: "core_graphics"
+ description: "Depracate eVsyncSourceSurfaceFlinger and use vsync_app everywhere"
+ bug: "162235855"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+} # deprecate_vsync_sf
+
flag {
name: "frame_rate_category_mrr"
namespace: "core_graphics"
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 3eabe1f..625d2e6 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -493,7 +493,7 @@
EXPECT_CALL(mockTracker, nextAnticipatedVSyncTimeFrom(_, _))
.WillOnce(Return(preferredExpectedPresentationTime));
- VsyncEventData vsyncEventData = mThread->getLatestVsyncEventData(mConnection);
+ VsyncEventData vsyncEventData = mThread->getLatestVsyncEventData(mConnection, now);
// Check EventThread immediately requested a resync.
EXPECT_TRUE(mResyncCallRecorder.waitForCall().has_value());
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 82023b0..83e3245 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -492,9 +492,11 @@
auto& getTransactionQueue() { return mFlinger->mTransactionHandler.mLocklessTransactionQueue; }
auto& getPendingTransactionQueue() {
+ ftl::FakeGuard guard(kMainThreadContext);
return mFlinger->mTransactionHandler.mPendingTransactionQueues;
}
size_t getPendingTransactionCount() {
+ ftl::FakeGuard guard(kMainThreadContext);
return mFlinger->mTransactionHandler.mPendingTransactionCount.load();
}
@@ -513,7 +515,9 @@
}
auto setTransactionStateInternal(TransactionState& transaction) {
- return mFlinger->mTransactionHandler.queueTransaction(std::move(transaction));
+ return FTL_FAKE_GUARD(kMainThreadContext,
+ mFlinger->mTransactionHandler.queueTransaction(
+ std::move(transaction)));
}
auto flushTransactionQueues() {
@@ -598,15 +602,20 @@
}
void injectLegacyLayer(sp<Layer> layer) {
- mFlinger->mLegacyLayers[static_cast<uint32_t>(layer->sequence)] = layer;
+ FTL_FAKE_GUARD(kMainThreadContext,
+ mFlinger->mLegacyLayers[static_cast<uint32_t>(layer->sequence)] = layer);
};
- void releaseLegacyLayer(uint32_t sequence) { mFlinger->mLegacyLayers.erase(sequence); };
+ void releaseLegacyLayer(uint32_t sequence) {
+ FTL_FAKE_GUARD(kMainThreadContext, mFlinger->mLegacyLayers.erase(sequence));
+ };
auto setLayerHistoryDisplayArea(uint32_t displayArea) {
return mFlinger->mScheduler->onActiveDisplayAreaChanged(displayArea);
};
- auto updateLayerHistory(nsecs_t now) { return mFlinger->updateLayerHistory(now); };
+ auto updateLayerHistory(nsecs_t now) {
+ return FTL_FAKE_GUARD(kMainThreadContext, mFlinger->updateLayerHistory(now));
+ };
auto setDaltonizerType(ColorBlindnessType type) {
mFlinger->mDaltonizer.setType(type);
return mFlinger->updateColorMatrixLocked();
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index e2b0ed1..8dd1a34 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -55,7 +55,7 @@
(override));
MOCK_METHOD(void, requestNextVsync, (const sp<android::EventThreadConnection>&), (override));
MOCK_METHOD(VsyncEventData, getLatestVsyncEventData,
- (const sp<android::EventThreadConnection>&), (const, override));
+ (const sp<android::EventThreadConnection>&, nsecs_t), (const, override));
MOCK_METHOD(void, requestLatestConfig, (const sp<android::EventThreadConnection>&));
MOCK_METHOD(void, pauseVsyncCallback, (bool));
MOCK_METHOD(void, onNewVsyncSchedule, (std::shared_ptr<scheduler::VsyncSchedule>), (override));
diff --git a/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
index e11a809..42aec7d 100644
--- a/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
+++ b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
@@ -17,7 +17,9 @@
#define LOG_TAG "VibratorHalControllerBenchmarks"
#include <benchmark/benchmark.h>
+#include <binder/ProcessState.h>
#include <vibratorservice/VibratorHalController.h>
+#include <future>
using ::android::enum_range;
using ::android::hardware::vibrator::CompositeEffect;
@@ -30,14 +32,90 @@
using ::benchmark::State;
using ::benchmark::internal::Benchmark;
+using std::chrono::milliseconds;
+
using namespace android;
using namespace std::chrono_literals;
+// Fixed number of iterations for benchmarks that trigger a vibration on the loop.
+// They require slow cleanup to ensure a stable state on each run and less noisy metrics.
+static constexpr auto VIBRATION_ITERATIONS = 500;
+
+// Timeout to wait for vibration callback completion.
+static constexpr auto VIBRATION_CALLBACK_TIMEOUT = 100ms;
+
+// Max duration the vibrator can be turned on, in milliseconds.
+static constexpr auto MAX_ON_DURATION_MS = milliseconds(UINT16_MAX);
+
+// Helper to wait for the vibrator to become idle between vibrate bench iterations.
+class HalCallback {
+public:
+ HalCallback(std::function<void()>&& waitFn, std::function<void()>&& completeFn)
+ : mWaitFn(std::move(waitFn)), mCompleteFn(std::move(completeFn)) {}
+ ~HalCallback() = default;
+
+ std::function<void()> completeFn() const { return mCompleteFn; }
+
+ void waitForComplete() const { mWaitFn(); }
+
+private:
+ std::function<void()> mWaitFn;
+ std::function<void()> mCompleteFn;
+};
+
+// Helper for vibration callbacks, kept by the Fixture until all pending callbacks are done.
+class HalCallbacks {
+public:
+ HalCallback next() {
+ auto id = mCurrentId++;
+ mPendingPromises[id] = std::promise<void>();
+ mPendingFutures[id] = mPendingPromises[id].get_future(); // Can only be called once.
+ return HalCallback([&, id]() { waitForComplete(id); }, [&, id]() { onComplete(id); });
+ }
+
+ void onComplete(int32_t id) {
+ mPendingPromises[id].set_value();
+ mPendingPromises.erase(id);
+ }
+
+ void waitForComplete(int32_t id) {
+ // Wait until the HAL has finished processing previous vibration before starting a new one,
+ // so the HAL state is consistent on each run and metrics are less noisy. Some of the newest
+ // HAL implementations are waiting on previous vibration cleanup and might be significantly
+ // slower, so make sure we measure vibrations on a clean slate.
+ if (mPendingFutures[id].wait_for(VIBRATION_CALLBACK_TIMEOUT) == std::future_status::ready) {
+ mPendingFutures.erase(id);
+ }
+ }
+
+ void waitForPending() {
+ // Wait for pending callbacks from the test, possibly skipped with error.
+ for (auto& [id, future] : mPendingFutures) {
+ future.wait_for(VIBRATION_CALLBACK_TIMEOUT);
+ }
+ mPendingFutures.clear();
+ mPendingPromises.clear();
+ }
+
+private:
+ std::map<int32_t, std::promise<void>> mPendingPromises;
+ std::map<int32_t, std::future<void>> mPendingFutures;
+ int32_t mCurrentId;
+};
+
class VibratorBench : public Fixture {
public:
- void SetUp(State& /*state*/) override { mController.init(); }
+ void SetUp(State& /*state*/) override {
+ android::ProcessState::self()->setThreadPoolMaxThreadCount(1);
+ android::ProcessState::self()->startThreadPool();
+ mController.init();
+ }
- void TearDown(State& state) override { turnVibratorOff(state); }
+ void TearDown(State& /*state*/) override {
+ turnVibratorOff();
+ disableExternalControl();
+ mCallbacks.waitForPending();
+ }
static void DefaultConfig(Benchmark* b) { b->Unit(kMicrosecond); }
@@ -47,38 +125,59 @@
protected:
vibrator::HalController mController;
+ HalCallbacks mCallbacks;
+
+ static void SlowBenchConfig(Benchmark* b) { b->Iterations(VIBRATION_ITERATIONS); }
auto getOtherArg(const State& state, std::size_t index) const { return state.range(index + 0); }
- bool hasCapabilities(vibrator::Capabilities&& query, State& state) {
+ vibrator::HalResult<void> turnVibratorOff() {
+ return mController.doWithRetry<void>([](auto hal) { return hal->off(); }, "off");
+ }
+
+ vibrator::HalResult<void> disableExternalControl() {
+ auto disableExternalControlFn = [](auto hal) { return hal->setExternalControl(false); };
+ return mController.doWithRetry<void>(disableExternalControlFn, "setExternalControl false");
+ }
+
+ bool shouldSkipWithMissingCapabilityMessage(vibrator::Capabilities query, State& state) {
auto result = mController.getInfo().capabilities;
if (result.isFailed()) {
state.SkipWithError(result.errorMessage());
- return false;
+ return true;
}
if (!result.isOk()) {
- return false;
+ state.SkipWithMessage("capability result is unsupported");
+ return true;
}
- return (result.value() & query) == query;
- }
-
- void turnVibratorOff(State& state) {
- checkHalResult(halCall<void>(mController, [](auto hal) { return hal->off(); }), state);
+ if ((result.value() & query) != query) {
+ state.SkipWithMessage("missing capability");
+ return true;
+ }
+ return false;
}
template <class R>
- bool checkHalResult(const vibrator::HalResult<R>& result, State& state) {
+ bool shouldSkipWithError(const vibrator::HalFunction<vibrator::HalResult<R>>& halFn,
+ const char* label, State& state) {
+ return shouldSkipWithError(mController.doWithRetry<R>(halFn, label), state);
+ }
+
+ template <class R>
+ bool shouldSkipWithError(const vibrator::HalResult<R>& result, State& state) {
if (result.isFailed()) {
state.SkipWithError(result.errorMessage());
- return false;
+ return true;
}
- return true;
+ return false;
}
+};
- template <class R>
- vibrator::HalResult<R> halCall(vibrator::HalController& controller,
- const vibrator::HalFunction<vibrator::HalResult<R>>& halFn) {
- return controller.doWithRetry<R>(halFn, "benchmark");
+class SlowVibratorBench : public VibratorBench {
+public:
+ static void DefaultConfig(Benchmark* b) {
+ VibratorBench::DefaultConfig(b);
+ SlowBenchConfig(b);
}
};
@@ -91,25 +190,32 @@
BENCHMARK_WRAPPER(VibratorBench, init, {
for (auto _ : state) {
+ // Setup
state.PauseTiming();
vibrator::HalController controller;
state.ResumeTiming();
+
+ // Test
controller.init();
}
});
BENCHMARK_WRAPPER(VibratorBench, initCached, {
+ // First call to cache values.
+ mController.init();
+
for (auto _ : state) {
mController.init();
}
});
BENCHMARK_WRAPPER(VibratorBench, ping, {
+ auto pingFn = [](auto hal) { return hal->ping(); };
+
for (auto _ : state) {
- state.ResumeTiming();
- auto ret = halCall<void>(mController, [](auto hal) { return hal->ping(); });
- state.PauseTiming();
- checkHalResult(ret, state);
+ if (shouldSkipWithError<void>(pingFn, "ping", state)) {
+ return;
+ }
}
});
@@ -119,164 +225,131 @@
}
});
-BENCHMARK_WRAPPER(VibratorBench, on, {
- auto duration = 60s;
- auto callback = []() {};
+BENCHMARK_WRAPPER(SlowVibratorBench, on, {
+ auto duration = MAX_ON_DURATION_MS;
for (auto _ : state) {
- state.ResumeTiming();
- auto ret =
- halCall<void>(mController, [&](auto hal) { return hal->on(duration, callback); });
+ // Setup
state.PauseTiming();
- if (checkHalResult(ret, state)) {
- turnVibratorOff(state);
+ auto cb = mCallbacks.next();
+ auto onFn = [&](auto hal) { return hal->on(duration, cb.completeFn()); };
+ state.ResumeTiming();
+
+ // Test
+ if (shouldSkipWithError<void>(onFn, "on", state)) {
+ return;
}
+
+ // Cleanup
+ state.PauseTiming();
+ if (shouldSkipWithError(turnVibratorOff(), state)) {
+ return;
+ }
+ cb.waitForComplete();
+ state.ResumeTiming();
}
});
-BENCHMARK_WRAPPER(VibratorBench, off, {
- auto duration = 60s;
- auto callback = []() {};
+BENCHMARK_WRAPPER(SlowVibratorBench, off, {
+ auto duration = MAX_ON_DURATION_MS;
for (auto _ : state) {
+ // Setup
state.PauseTiming();
- auto ret =
- halCall<void>(mController, [&](auto hal) { return hal->on(duration, callback); });
- if (!checkHalResult(ret, state)) {
- continue;
+ auto cb = mCallbacks.next();
+ auto onFn = [&](auto hal) { return hal->on(duration, cb.completeFn()); };
+ if (shouldSkipWithError<void>(onFn, "on", state)) {
+ return;
}
+ auto offFn = [&](auto hal) { return hal->off(); };
state.ResumeTiming();
- turnVibratorOff(state);
+
+ // Test
+ if (shouldSkipWithError<void>(offFn, "off", state)) {
+ return;
+ }
+
+ // Cleanup
+ state.PauseTiming();
+ cb.waitForComplete();
+ state.ResumeTiming();
}
});
BENCHMARK_WRAPPER(VibratorBench, setAmplitude, {
- if (!hasCapabilities(vibrator::Capabilities::AMPLITUDE_CONTROL, state)) {
- state.SkipWithMessage("missing capability");
+ if (shouldSkipWithMissingCapabilityMessage(vibrator::Capabilities::AMPLITUDE_CONTROL, state)) {
return;
}
- auto duration = 60s;
- auto callback = []() {};
+ auto duration = MAX_ON_DURATION_MS;
auto amplitude = 1.0f;
+ auto setAmplitudeFn = [&](auto hal) { return hal->setAmplitude(amplitude); };
- for (auto _ : state) {
- state.PauseTiming();
- vibrator::HalController controller;
- controller.init();
- auto result =
- halCall<void>(controller, [&](auto hal) { return hal->on(duration, callback); });
- if (!checkHalResult(result, state)) {
- continue;
- }
- state.ResumeTiming();
- auto ret =
- halCall<void>(controller, [&](auto hal) { return hal->setAmplitude(amplitude); });
- state.PauseTiming();
- if (checkHalResult(ret, state)) {
- turnVibratorOff(state);
- }
- }
-});
-
-BENCHMARK_WRAPPER(VibratorBench, setAmplitudeCached, {
- if (!hasCapabilities(vibrator::Capabilities::AMPLITUDE_CONTROL, state)) {
- state.SkipWithMessage("missing capability");
+ auto onFn = [&](auto hal) { return hal->on(duration, [&]() {}); };
+ if (shouldSkipWithError<void>(onFn, "on", state)) {
return;
}
- auto duration = 60s;
- auto callback = []() {};
- auto amplitude = 1.0f;
-
- auto onResult =
- halCall<void>(mController, [&](auto hal) { return hal->on(duration, callback); });
- checkHalResult(onResult, state);
-
for (auto _ : state) {
- auto ret =
- halCall<void>(mController, [&](auto hal) { return hal->setAmplitude(amplitude); });
- checkHalResult(ret, state);
+ if (shouldSkipWithError<void>(setAmplitudeFn, "setAmplitude", state)) {
+ return;
+ }
}
});
BENCHMARK_WRAPPER(VibratorBench, setExternalControl, {
- if (!hasCapabilities(vibrator::Capabilities::EXTERNAL_CONTROL, state)) {
- state.SkipWithMessage("missing capability");
+ if (shouldSkipWithMissingCapabilityMessage(vibrator::Capabilities::EXTERNAL_CONTROL, state)) {
return;
}
+ auto enableExternalControlFn = [](auto hal) { return hal->setExternalControl(true); };
+
for (auto _ : state) {
- state.PauseTiming();
- vibrator::HalController controller;
- controller.init();
- state.ResumeTiming();
- auto ret =
- halCall<void>(controller, [](auto hal) { return hal->setExternalControl(true); });
- state.PauseTiming();
- if (checkHalResult(ret, state)) {
- auto result = halCall<void>(controller,
- [](auto hal) { return hal->setExternalControl(false); });
- checkHalResult(result, state);
+ // Test
+ if (shouldSkipWithError<void>(enableExternalControlFn, "setExternalControl true", state)) {
+ return;
}
+
+ // Cleanup
+ state.PauseTiming();
+ if (shouldSkipWithError(disableExternalControl(), state)) {
+ return;
+ }
+ state.ResumeTiming();
}
});
-BENCHMARK_WRAPPER(VibratorBench, setExternalControlCached, {
- if (!hasCapabilities(vibrator::Capabilities::EXTERNAL_CONTROL, state)) {
- state.SkipWithMessage("missing capability");
- return;
- }
-
- for (auto _ : state) {
- state.ResumeTiming();
- auto result =
- halCall<void>(mController, [](auto hal) { return hal->setExternalControl(true); });
- state.PauseTiming();
- if (checkHalResult(result, state)) {
- auto ret = halCall<void>(mController,
- [](auto hal) { return hal->setExternalControl(false); });
- checkHalResult(ret, state);
- }
- }
-});
-
-BENCHMARK_WRAPPER(VibratorBench, setExternalAmplitudeCached, {
- if (!hasCapabilities(vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL, state)) {
- state.SkipWithMessage("missing capability");
+BENCHMARK_WRAPPER(VibratorBench, setExternalAmplitude, {
+ auto externalAmplitudeControl = vibrator::Capabilities::EXTERNAL_CONTROL &
+ vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL;
+ if (shouldSkipWithMissingCapabilityMessage(externalAmplitudeControl, state)) {
return;
}
auto amplitude = 1.0f;
+ auto setAmplitudeFn = [&](auto hal) { return hal->setAmplitude(amplitude); };
+ auto enableExternalControlFn = [](auto hal) { return hal->setExternalControl(true); };
- auto onResult =
- halCall<void>(mController, [](auto hal) { return hal->setExternalControl(true); });
- checkHalResult(onResult, state);
-
- for (auto _ : state) {
- auto ret =
- halCall<void>(mController, [&](auto hal) { return hal->setAmplitude(amplitude); });
- checkHalResult(ret, state);
+ if (shouldSkipWithError<void>(enableExternalControlFn, "setExternalControl true", state)) {
+ return;
}
- auto offResult =
- halCall<void>(mController, [](auto hal) { return hal->setExternalControl(false); });
- checkHalResult(offResult, state);
+ for (auto _ : state) {
+ if (shouldSkipWithError<void>(setAmplitudeFn, "setExternalAmplitude", state)) {
+ return;
+ }
+ }
});
BENCHMARK_WRAPPER(VibratorBench, getInfo, {
for (auto _ : state) {
+ // Setup
state.PauseTiming();
vibrator::HalController controller;
controller.init();
state.ResumeTiming();
- auto result = controller.getInfo();
- checkHalResult(result.capabilities, state);
- checkHalResult(result.supportedEffects, state);
- checkHalResult(result.supportedPrimitives, state);
- checkHalResult(result.primitiveDurations, state);
- checkHalResult(result.resonantFrequency, state);
- checkHalResult(result.qFactor, state);
+
+ controller.getInfo();
}
});
@@ -285,13 +358,7 @@
mController.getInfo();
for (auto _ : state) {
- auto result = mController.getInfo();
- checkHalResult(result.capabilities, state);
- checkHalResult(result.supportedEffects, state);
- checkHalResult(result.supportedPrimitives, state);
- checkHalResult(result.primitiveDurations, state);
- checkHalResult(result.resonantFrequency, state);
- checkHalResult(result.qFactor, state);
+ mController.getInfo();
}
});
@@ -334,9 +401,16 @@
}
};
+class SlowVibratorEffectsBench : public VibratorEffectsBench {
+public:
+ static void DefaultConfig(Benchmark* b) {
+ VibratorBench::DefaultConfig(b);
+ SlowBenchConfig(b);
+ }
+};
+
BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnEnable, {
- if (!hasCapabilities(vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) {
- state.SkipWithMessage("missing capability");
+ if (shouldSkipWithMissingCapabilityMessage(vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) {
return;
}
if (!hasArgs(state)) {
@@ -347,24 +421,26 @@
int32_t id = 1;
auto effect = getEffect(state);
auto strength = getStrength(state);
+ auto enableFn = [&](auto hal) { return hal->alwaysOnEnable(id, effect, strength); };
+ auto disableFn = [&](auto hal) { return hal->alwaysOnDisable(id); };
for (auto _ : state) {
- state.ResumeTiming();
- auto ret = halCall<void>(mController, [&](auto hal) {
- return hal->alwaysOnEnable(id, effect, strength);
- });
- state.PauseTiming();
- if (checkHalResult(ret, state)) {
- auto disableResult =
- halCall<void>(mController, [&](auto hal) { return hal->alwaysOnDisable(id); });
- checkHalResult(disableResult, state);
+ // Test
+ if (shouldSkipWithError<void>(enableFn, "alwaysOnEnable", state)) {
+ return;
}
+
+ // Cleanup
+ state.PauseTiming();
+ if (shouldSkipWithError<void>(disableFn, "alwaysOnDisable", state)) {
+ return;
+ }
+ state.ResumeTiming();
}
});
BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnDisable, {
- if (!hasCapabilities(vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) {
- state.SkipWithMessage("missing capability");
+ if (shouldSkipWithMissingCapabilityMessage(vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) {
return;
}
if (!hasArgs(state)) {
@@ -375,23 +451,25 @@
int32_t id = 1;
auto effect = getEffect(state);
auto strength = getStrength(state);
+ auto enableFn = [&](auto hal) { return hal->alwaysOnEnable(id, effect, strength); };
+ auto disableFn = [&](auto hal) { return hal->alwaysOnDisable(id); };
for (auto _ : state) {
+ // Setup
state.PauseTiming();
- auto enableResult = halCall<void>(mController, [&](auto hal) {
- return hal->alwaysOnEnable(id, effect, strength);
- });
- if (!checkHalResult(enableResult, state)) {
- continue;
+ if (shouldSkipWithError<void>(enableFn, "alwaysOnEnable", state)) {
+ return;
}
state.ResumeTiming();
- auto disableResult =
- halCall<void>(mController, [&](auto hal) { return hal->alwaysOnDisable(id); });
- checkHalResult(disableResult, state);
+
+ // Test
+ if (shouldSkipWithError<void>(disableFn, "alwaysOnDisable", state)) {
+ return;
+ }
}
});
-BENCHMARK_WRAPPER(VibratorEffectsBench, performEffect, {
+BENCHMARK_WRAPPER(SlowVibratorEffectsBench, performEffect, {
if (!hasArgs(state)) {
state.SkipWithMessage("missing args");
return;
@@ -399,22 +477,38 @@
auto effect = getEffect(state);
auto strength = getStrength(state);
- auto callback = []() {};
for (auto _ : state) {
- state.ResumeTiming();
- auto ret = halCall<std::chrono::milliseconds>(mController, [&](auto hal) {
- return hal->performEffect(effect, strength, callback);
- });
+ // Setup
state.PauseTiming();
- if (checkHalResult(ret, state)) {
- turnVibratorOff(state);
+ auto cb = mCallbacks.next();
+ auto performFn = [&](auto hal) {
+ return hal->performEffect(effect, strength, cb.completeFn());
+ };
+ state.ResumeTiming();
+
+ // Test
+ if (shouldSkipWithError<milliseconds>(performFn, "performEffect", state)) {
+ return;
}
+
+ // Cleanup
+ state.PauseTiming();
+ if (shouldSkipWithError(turnVibratorOff(), state)) {
+ return;
+ }
+ cb.waitForComplete();
+ state.ResumeTiming();
}
});
-class VibratorPrimitivesBench : public VibratorBench {
+class SlowVibratorPrimitivesBench : public VibratorBench {
public:
+ static void DefaultConfig(Benchmark* b) {
+ VibratorBench::DefaultConfig(b);
+ SlowBenchConfig(b);
+ }
+
static void DefaultArgs(Benchmark* b) {
vibrator::HalController controller;
auto primitivesResult = controller.getInfo().supportedPrimitives;
@@ -449,9 +543,8 @@
}
};
-BENCHMARK_WRAPPER(VibratorPrimitivesBench, performComposedEffect, {
- if (!hasCapabilities(vibrator::Capabilities::COMPOSE_EFFECTS, state)) {
- state.SkipWithMessage("missing capability");
+BENCHMARK_WRAPPER(SlowVibratorPrimitivesBench, performComposedEffect, {
+ if (shouldSkipWithMissingCapabilityMessage(vibrator::Capabilities::COMPOSE_EFFECTS, state)) {
return;
}
if (!hasArgs(state)) {
@@ -464,19 +557,29 @@
effect.scale = 1.0f;
effect.delayMs = static_cast<int32_t>(0);
- std::vector<CompositeEffect> effects;
- effects.push_back(effect);
- auto callback = []() {};
+ std::vector<CompositeEffect> effects = {effect};
for (auto _ : state) {
- state.ResumeTiming();
- auto ret = halCall<std::chrono::milliseconds>(mController, [&](auto hal) {
- return hal->performComposedEffect(effects, callback);
- });
+ // Setup
state.PauseTiming();
- if (checkHalResult(ret, state)) {
- turnVibratorOff(state);
+ auto cb = mCallbacks.next();
+ auto performFn = [&](auto hal) {
+ return hal->performComposedEffect(effects, cb.completeFn());
+ };
+ state.ResumeTiming();
+
+ // Test
+ if (shouldSkipWithError<milliseconds>(performFn, "performComposedEffect", state)) {
+ return;
}
+
+ // Cleanup
+ state.PauseTiming();
+ if (shouldSkipWithError(turnVibratorOff(), state)) {
+ return;
+ }
+ cb.waitForComplete();
+ state.ResumeTiming();
}
});
diff --git a/services/vibratorservice/test/VibratorHalControllerTest.cpp b/services/vibratorservice/test/VibratorHalControllerTest.cpp
index 9b95d74..15fde91 100644
--- a/services/vibratorservice/test/VibratorHalControllerTest.cpp
+++ b/services/vibratorservice/test/VibratorHalControllerTest.cpp
@@ -255,16 +255,17 @@
.WillRepeatedly(Return(vibrator::HalResult<void>::transactionFailed("message")));
}
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ auto counter = vibrator::TestCounter(0);
- auto onFn = [&](vibrator::HalWrapper* hal) { return hal->on(10ms, callback); };
+ auto onFn = [&](vibrator::HalWrapper* hal) {
+ return hal->on(10ms, [&counter] { counter.increment(); });
+ };
ASSERT_TRUE(mController->doWithRetry<void>(onFn, "on").isOk());
ASSERT_TRUE(mController->doWithRetry<void>(PING_FN, "ping").isFailed());
mMockHal.reset();
- ASSERT_EQ(0, *callbackCounter.get());
+ ASSERT_EQ(0, counter.get());
// Callback triggered even after HalWrapper was reconnected.
- std::this_thread::sleep_for(15ms);
- ASSERT_EQ(1, *callbackCounter.get());
+ counter.tryWaitUntilCountIsAtLeast(1, 500ms);
+ ASSERT_EQ(1, counter.get());
}
diff --git a/services/vibratorservice/test/test_utils.h b/services/vibratorservice/test/test_utils.h
index 1933a11..b584f64 100644
--- a/services/vibratorservice/test/test_utils.h
+++ b/services/vibratorservice/test/test_utils.h
@@ -85,10 +85,36 @@
~TestFactory() = delete;
};
+class TestCounter {
+public:
+ TestCounter(int32_t init) : mCount(init), mMutex(), mCondVar() {}
+
+ int32_t get() {
+ std::unique_lock<std::mutex> lock(mMutex);
+ return mCount;
+ }
+
+ void increment() {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mCount += 1;
+ mCondVar.notify_all();
+ }
+
+ void tryWaitUntilCountIsAtLeast(int32_t count, std::chrono::milliseconds timeout) {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mCondVar.wait_for(lock, timeout, [&] { return mCount >= count; });
+ }
+
+private:
+ int32_t mCount;
+ std::mutex mMutex;
+ std::condition_variable mCondVar;
+};
+
// -------------------------------------------------------------------------------------------------
} // namespace vibrator
} // namespace android
-#endif // VIBRATORSERVICE_UNITTEST_UTIL_H_
\ No newline at end of file
+#endif // VIBRATORSERVICE_UNITTEST_UTIL_H_