diff options
48 files changed, 1968 insertions, 737 deletions
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp index 341fabb32a..de0aafaa1c 100644 --- a/cmds/dumpstate/Android.bp +++ b/cmds/dumpstate/Android.bp @@ -37,9 +37,6 @@ cc_library_shared { name: "libdumpstateutil", defaults: ["dumpstate_cflag_defaults"], vendor_available: true, - vndk: { - enabled: true, - }, srcs: [ "DumpstateInternal.cpp", "DumpstateUtil.cpp", diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index eec12e4cd5..9c24dc590c 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -358,9 +358,6 @@ cc_library { // 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 @@ cc_library { "//packages/modules/Virtualization:__subpackages__", "//device/google/cuttlefish/shared/minidroid:__subpackages__", "//system/software_defined_vehicle:__subpackages__", + "//visibility:any_system_partition", ], } diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs index 16049f28c5..0540ed379b 100644 --- a/libs/binder/rust/src/lib.rs +++ b/libs/binder/rust/src/lib.rs @@ -101,6 +101,8 @@ mod native; 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 @@ use binder_ndk_sys as sys; 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 8ae010ea88..da9d7dc948 100644 --- a/libs/binder/rust/src/native.rs +++ b/libs/binder/rust/src/native.rs @@ -23,12 +23,11 @@ use crate::proxy::SpIBinder; 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 @@ unsafe impl<B: Remotable> AsNative<sys::AIBinder> for Binder<B> { } } -/// 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 Remotable for () { } 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 7434e9ddbd..340014aeaa 100644 --- a/libs/binder/rust/src/proxy.rs +++ b/libs/binder/rust/src/proxy.rs @@ -29,11 +29,10 @@ use crate::sys; 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 @@ impl SpIBinder { } } -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 @@ unsafe impl<T: Proxy> AsNative<sys::AIBinder> for T { } } -/// 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 0000000000..3ca3b540c4 --- /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 d0a1fbca49..72e5ff9b2a 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 0000000000..1b0dca09f4 --- /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 0000000000..a71ce2c26e --- /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 0000000000..c2ecaa4fe3 --- /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 0000000000..192a1591ae --- /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 13001219d4..241e6683e7 100644 --- a/libs/binder/trusty/usertests-inc.mk +++ b/libs/binder/trusty/usertests-inc.mk @@ -17,3 +17,6 @@ TRUSTY_USER_TESTS += \ 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/bufferqueueconverter/Android.bp b/libs/bufferqueueconverter/Android.bp index 3c8c41fdc8..196161b3e2 100644 --- a/libs/bufferqueueconverter/Android.bp +++ b/libs/bufferqueueconverter/Android.bp @@ -17,9 +17,6 @@ cc_library_headers { 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 f300da57d8..8dabc2c79e 100644 --- a/libs/gralloc/types/Android.bp +++ b/libs/gralloc/types/Android.bp @@ -38,10 +38,7 @@ cc_library { }, 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 d1b2c5f025..6c45746335 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -305,10 +305,6 @@ cc_defaults { cc_library_shared { name: "libgui", vendor_available: true, - vndk: { - enabled: true, - private: true, - }, double_loadable: true, defaults: [ diff --git a/libs/gui/rust/aidl_types/src/lib.rs b/libs/gui/rust/aidl_types/src/lib.rs index 3d29529d23..941b1f9db8 100644 --- a/libs/gui/rust/aidl_types/src/lib.rs +++ b/libs/gui/rust/aidl_types/src/lib.rs @@ -20,6 +20,7 @@ use binder::{ StatusCode, }; +#[allow(dead_code)] macro_rules! stub_unstructured_parcelable { ($name:ident) => { /// Unimplemented stub parcelable. diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp index 9cb298a132..312a1e68b3 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -105,9 +105,6 @@ cc_library_static { cc_library_shared { name: "libui", vendor_available: true, - vndk: { - enabled: true, - }, double_loadable: true, cflags: [ diff --git a/services/inputflinger/InputCommonConverter.cpp b/services/inputflinger/InputCommonConverter.cpp index 6ccd9e7697..417c1f333b 100644 --- a/services/inputflinger/InputCommonConverter.cpp +++ b/services/inputflinger/InputCommonConverter.cpp @@ -20,6 +20,9 @@ using namespace ::aidl::android::hardware::input; namespace android { +const static ui::Transform kIdentityTransform; +const static std::array<uint8_t, 32> kInvalidHmac{}; + static common::Source getSource(uint32_t source) { static_assert(static_cast<common::Source>(AINPUT_SOURCE_UNKNOWN) == common::Source::UNKNOWN, "SOURCE_UNKNOWN mismatch"); @@ -337,4 +340,31 @@ common::MotionEvent notifyMotionArgsToHalMotionEvent(const NotifyMotionArgs& arg return event; } +MotionEvent toMotionEvent(const NotifyMotionArgs& args, const ui::Transform* transform, + const ui::Transform* rawTransform, const std::array<uint8_t, 32>* hmac) { + if (transform == nullptr) transform = &kIdentityTransform; + if (rawTransform == nullptr) rawTransform = &kIdentityTransform; + if (hmac == nullptr) hmac = &kInvalidHmac; + + MotionEvent event; + event.initialize(args.id, args.deviceId, args.source, args.displayId, *hmac, args.action, + args.actionButton, args.flags, args.edgeFlags, args.metaState, + args.buttonState, args.classification, *transform, args.xPrecision, + args.yPrecision, args.xCursorPosition, args.yCursorPosition, *rawTransform, + args.downTime, args.eventTime, args.getPointerCount(), + args.pointerProperties.data(), args.pointerCoords.data()); + return event; +} + +KeyEvent toKeyEvent(const NotifyKeyArgs& args, int32_t repeatCount, + const std::array<uint8_t, 32>* hmac) { + if (hmac == nullptr) hmac = &kInvalidHmac; + + KeyEvent event; + event.initialize(args.id, args.deviceId, args.source, args.displayId, *hmac, args.action, + args.flags, args.keyCode, args.scanCode, args.metaState, repeatCount, + args.downTime, args.eventTime); + return event; +} + } // namespace android diff --git a/services/inputflinger/InputCommonConverter.h b/services/inputflinger/InputCommonConverter.h index 4d3b76885f..0d4cbb0c96 100644 --- a/services/inputflinger/InputCommonConverter.h +++ b/services/inputflinger/InputCommonConverter.h @@ -16,16 +16,25 @@ #pragma once +#include "InputListener.h" + #include <aidl/android/hardware/input/common/Axis.h> #include <aidl/android/hardware/input/common/MotionEvent.h> -#include "InputListener.h" +#include <input/Input.h> namespace android { -/** - * Convert from framework's NotifyMotionArgs to hidl's common::MotionEvent - */ +/** Convert from framework's NotifyMotionArgs to hidl's common::MotionEvent. */ ::aidl::android::hardware::input::common::MotionEvent notifyMotionArgsToHalMotionEvent( const NotifyMotionArgs& args); +/** Convert from NotifyMotionArgs to MotionEvent. */ +MotionEvent toMotionEvent(const NotifyMotionArgs&, const ui::Transform* transform = nullptr, + const ui::Transform* rawTransform = nullptr, + const std::array<uint8_t, 32>* hmac = nullptr); + +/** Convert from NotifyKeyArgs to KeyEvent. */ +KeyEvent toKeyEvent(const NotifyKeyArgs&, int32_t repeatCount = 0, + const std::array<uint8_t, 32>* hmac = nullptr); + } // namespace android diff --git a/services/inputflinger/InputFilterCallbacks.cpp b/services/inputflinger/InputFilterCallbacks.cpp index 6c3144230f..a9bdbec105 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 @@ namespace { using namespace aidl::com::android::server::inputflinger; -class InputFilterThreadImpl : public Thread { -public: - explicit InputFilterThreadImpl(std::function<void()> loop) - : Thread(/*canCallJava=*/true), mThreadLoop(loop) {} - - ~InputFilterThreadImpl() {} - -private: - std::function<void()> mThreadLoop; - - bool threadLoop() override { - mThreadLoop(); - return true; - } -}; - class InputFilterThread : public BnInputThread { public: InputFilterThread(std::shared_ptr<IInputThreadCallback> callback) : mCallback(callback) { - mThread = sp<InputFilterThreadImpl>::make([this]() { loopOnce(); }); - mThread->run("InputFilterThread", ANDROID_PRIORITY_URGENT_DISPLAY); + 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 2f6b8fc6ff..cc0592ef52 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 @@ package com.android.server.inputflinger; * 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 @@ interface IInputThread { /** 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 c2d2b7b0ea..02bc3680bf 100644 --- a/services/inputflinger/dispatcher/InputState.cpp +++ b/services/inputflinger/dispatcher/InputState.cpp @@ -338,9 +338,8 @@ bool InputState::shouldCancelPreviousStream(const MotionEntry& motionEntry) cons // 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/InputTracingPerfettoBackend.cpp b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp index 9c39743569..91ebe9bb5f 100644 --- a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp +++ b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp @@ -149,6 +149,8 @@ bool PerfettoBackend::InputEventDataSource::ruleMatches(const TraceRule& rule, // --- PerfettoBackend --- +bool PerfettoBackend::sUseInProcessBackendForTest{false}; + std::once_flag PerfettoBackend::sDataSourceRegistrationFlag{}; std::atomic<int32_t> PerfettoBackend::sNextInstanceId{1}; @@ -159,7 +161,8 @@ PerfettoBackend::PerfettoBackend(GetPackageUid getPackagesForUid) // we never unregister the InputEventDataSource. std::call_once(sDataSourceRegistrationFlag, []() { perfetto::TracingInitArgs args; - args.backends = perfetto::kSystemBackend; + args.backends = sUseInProcessBackendForTest ? perfetto::kInProcessBackend + : perfetto::kSystemBackend; perfetto::Tracing::Initialize(args); // Register our custom data source for input event tracing. @@ -175,6 +178,9 @@ void PerfettoBackend::traceMotionEvent(const TracedMotionEvent& event, const TracedEventMetadata& metadata) { InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) { auto dataSource = ctx.GetDataSourceLocked(); + if (!dataSource.valid()) { + return; + } dataSource->initializeUidMap(mGetPackageUid); if (dataSource->shouldIgnoreTracedInputEvent(event.eventType)) { return; @@ -196,6 +202,9 @@ void PerfettoBackend::traceKeyEvent(const TracedKeyEvent& event, const TracedEventMetadata& metadata) { InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) { auto dataSource = ctx.GetDataSourceLocked(); + if (!dataSource.valid()) { + return; + } dataSource->initializeUidMap(mGetPackageUid); if (dataSource->shouldIgnoreTracedInputEvent(event.eventType)) { return; @@ -217,6 +226,9 @@ void PerfettoBackend::traceWindowDispatch(const WindowDispatchArgs& dispatchArgs const TracedEventMetadata& metadata) { InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) { auto dataSource = ctx.GetDataSourceLocked(); + if (!dataSource.valid()) { + return; + } dataSource->initializeUidMap(mGetPackageUid); if (!dataSource->getFlags().test(TraceFlag::TRACE_DISPATCHER_WINDOW_DISPATCH)) { return; diff --git a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h index e945066dff..fdfe495c45 100644 --- a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h +++ b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h @@ -51,6 +51,8 @@ class PerfettoBackend : public InputTracingBackendInterface { public: using GetPackageUid = std::function<gui::Uid(std::string)>; + static bool sUseInProcessBackendForTest; + explicit PerfettoBackend(GetPackageUid); ~PerfettoBackend() override = default; @@ -61,6 +63,7 @@ public: private: // Implementation of the perfetto data source. // Each instance of the InputEventDataSource represents a different tracing session. + // Its lifecycle is controlled by perfetto. class InputEventDataSource : public perfetto::DataSource<InputEventDataSource> { public: explicit InputEventDataSource(); diff --git a/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp b/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp index 77d09cbb4b..c0a98f5e5d 100644 --- a/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp +++ b/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp @@ -84,13 +84,18 @@ void ThreadedBackend<Backend>::threadLoop() { std::unique_lock lock(mLock); base::ScopedLockAssertion assumeLocked(mLock); + setIdleStatus(true); + // Wait until we need to process more events or exit. mThreadWakeCondition.wait(lock, [&]() REQUIRES(mLock) { return mThreadExit || !mQueue.empty(); }); if (mThreadExit) { + setIdleStatus(true); return; } + setIdleStatus(false); + mQueue.swap(entries); } // release lock @@ -109,6 +114,36 @@ void ThreadedBackend<Backend>::threadLoop() { entries.clear(); } +template <typename Backend> +std::function<void()> ThreadedBackend<Backend>::getIdleWaiterForTesting() { + std::scoped_lock lock(mLock); + if (!mIdleWaiter) { + mIdleWaiter = std::make_shared<IdleWaiter>(); + } + + // Return a lambda that holds a strong reference to the idle waiter, whose lifetime can extend + // beyond this threaded backend object. + return [idleWaiter = mIdleWaiter]() { + std::unique_lock idleLock(idleWaiter->idleLock); + base::ScopedLockAssertion assumeLocked(idleWaiter->idleLock); + idleWaiter->threadIdleCondition.wait(idleLock, [&]() REQUIRES(idleWaiter->idleLock) { + return idleWaiter->isIdle; + }); + }; +} + +template <typename Backend> +void ThreadedBackend<Backend>::setIdleStatus(bool isIdle) { + if (!mIdleWaiter) { + return; + } + std::scoped_lock idleLock(mIdleWaiter->idleLock); + mIdleWaiter->isIdle = isIdle; + if (isIdle) { + mIdleWaiter->threadIdleCondition.notify_all(); + } +} + // Explicit template instantiation for the PerfettoBackend. template class ThreadedBackend<PerfettoBackend>; diff --git a/services/inputflinger/dispatcher/trace/ThreadedBackend.h b/services/inputflinger/dispatcher/trace/ThreadedBackend.h index 650a87e452..52a84c470c 100644 --- a/services/inputflinger/dispatcher/trace/ThreadedBackend.h +++ b/services/inputflinger/dispatcher/trace/ThreadedBackend.h @@ -42,6 +42,9 @@ public: void traceMotionEvent(const TracedMotionEvent&, const TracedEventMetadata&) override; void traceWindowDispatch(const WindowDispatchArgs&, const TracedEventMetadata&) override; + /** Returns a function that, when called, will block until the tracing thread is idle. */ + std::function<void()> getIdleWaiterForTesting(); + private: std::mutex mLock; bool mThreadExit GUARDED_BY(mLock){false}; @@ -52,12 +55,21 @@ private: TracedEventMetadata>; std::vector<TraceEntry> mQueue GUARDED_BY(mLock); + struct IdleWaiter { + std::mutex idleLock; + std::condition_variable threadIdleCondition; + bool isIdle GUARDED_BY(idleLock){false}; + }; + // The lazy-initialized object used to wait for the tracing thread to idle. + std::shared_ptr<IdleWaiter> mIdleWaiter GUARDED_BY(mLock); + // InputThread stops when its destructor is called. Initialize it last so that it is the // first thing to be destructed. This will guarantee the thread will not access other // members that have already been destructed. InputThread mTracerThread; void threadLoop(); + void setIdleStatus(bool isIdle) REQUIRES(mLock); }; } // namespace android::inputdispatcher::trace::impl diff --git a/services/inputflinger/include/NotifyArgsBuilders.h b/services/inputflinger/include/NotifyArgsBuilders.h index 8ffbc11a13..1bd55958d9 100644 --- a/services/inputflinger/include/NotifyArgsBuilders.h +++ b/services/inputflinger/include/NotifyArgsBuilders.h @@ -30,8 +30,11 @@ namespace android { class MotionArgsBuilder { public: - MotionArgsBuilder(int32_t action, int32_t source) { + MotionArgsBuilder(int32_t action, int32_t source) : mEventId(InputEvent::nextId()) { mAction = action; + if (mAction == AMOTION_EVENT_ACTION_CANCEL) { + addFlag(AMOTION_EVENT_FLAG_CANCELED); + } mSource = source; mEventTime = systemTime(SYSTEM_TIME_MONOTONIC); mDownTime = mEventTime; @@ -97,7 +100,7 @@ public: return *this; } - NotifyMotionArgs build() { + NotifyMotionArgs build() const { std::vector<PointerProperties> pointerProperties; std::vector<PointerCoords> pointerCoords; for (const PointerBuilder& pointer : mPointers) { @@ -106,19 +109,17 @@ public: } // Set mouse cursor position for the most common cases to avoid boilerplate. + float resolvedCursorX = mRawXCursorPosition; + float resolvedCursorY = mRawYCursorPosition; if (mSource == AINPUT_SOURCE_MOUSE && !MotionEvent::isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition) && BitSet64::hasBit(pointerCoords[0].bits, AMOTION_EVENT_AXIS_X) && BitSet64::hasBit(pointerCoords[0].bits, AMOTION_EVENT_AXIS_Y)) { - mRawXCursorPosition = pointerCoords[0].getX(); - mRawYCursorPosition = pointerCoords[0].getY(); - } - - if (mAction == AMOTION_EVENT_ACTION_CANCEL) { - addFlag(AMOTION_EVENT_FLAG_CANCELED); + resolvedCursorX = pointerCoords[0].getX(); + resolvedCursorY = pointerCoords[0].getY(); } - return {InputEvent::nextId(), + return {mEventId, mEventTime, /*readTime=*/mEventTime, mDeviceId, @@ -137,13 +138,14 @@ public: pointerCoords.data(), /*xPrecision=*/0, /*yPrecision=*/0, - mRawXCursorPosition, - mRawYCursorPosition, + resolvedCursorX, + resolvedCursorY, mDownTime, /*videoFrames=*/{}}; } private: + const int32_t mEventId; int32_t mAction; int32_t mDeviceId{DEFAULT_DEVICE_ID}; uint32_t mSource; @@ -163,7 +165,7 @@ private: class KeyArgsBuilder { public: - KeyArgsBuilder(int32_t action, int32_t source) { + KeyArgsBuilder(int32_t action, int32_t source) : mEventId(InputEvent::nextId()) { mAction = action; mSource = source; mEventTime = systemTime(SYSTEM_TIME_MONOTONIC); @@ -206,7 +208,7 @@ public: } NotifyKeyArgs build() const { - return {InputEvent::nextId(), + return {mEventId, mEventTime, /*readTime=*/mEventTime, mDeviceId, @@ -222,6 +224,7 @@ public: } private: + const int32_t mEventId; int32_t mAction; int32_t mDeviceId = DEFAULT_DEVICE_ID; uint32_t mSource; diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 7d27d4a9ce..8d915996f7 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 @@ void TouchInputMapper::populateDeviceInfo(InputDeviceInfo& info) { 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 @@ bool TouchInputMapper::hasExternalStylus() const { * 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 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) 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 @@ std::list<NotifyArgs> TouchInputMapper::consumeRawTouches(nsecs_t when, nsecs_t } 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 @@ std::list<NotifyArgs> TouchInputMapper::consumeRawTouches(nsecs_t when, nsecs_t // 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 4b39e4099c..8451675944 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 @@ protected: 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 721cdfdfb0..f558ba1196 100644 --- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp @@ -185,6 +185,7 @@ private: 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 @@ private: } 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 a544fa36ae..6df339ed67 100644 --- a/services/inputflinger/rust/input_filter.rs +++ b/services/inputflinger/rust/input_filter.rs @@ -396,14 +396,16 @@ pub mod test_callbacks { 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 @@ pub mod test_callbacks { 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 @@ pub mod test_callbacks { 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() + } + + 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 is_finish_called(&self) -> bool { - self.0.read().unwrap().is_finish_called + 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 2d503aee70..96e5681ead 100644 --- a/services/inputflinger/rust/input_filter_thread.rs +++ b/services/inputflinger/rust/input_filter_thread.rs @@ -33,8 +33,6 @@ use com_android_server_inputflinger::aidl::com::android::server::inputflinger::I 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 @@ pub struct InputFilterThread { 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 @@ impl InputFilterThread { 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 @@ impl InputFilterThread { /// 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 @@ impl InputFilterThread { 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 @@ impl InputFilterThread { 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 @@ impl ThreadCallbackHandler { #[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.request_timeout_at_time(500); - test_thread.dispatch_next(); + test_thread.register_thread_callback(Box::new(test_thread_callback.clone())); - test_thread.move_time_forward(100); + let now = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_milliseconds(); + test_thread.request_timeout_at_time((now + 100) * 1000000); - 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 09fbf40842..0f18a2f395 100644 --- a/services/inputflinger/rust/slow_keys_filter.rs +++ b/services/inputflinger/rust/slow_keys_filter.rs @@ -207,13 +207,19 @@ impl ThreadCallback for SlowKeysFilter { #[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 @@ mod tests { 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 @@ mod tests { #[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 @@ mod tests { #[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(); - - filter.notify_key(&KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT }); - test_thread.dispatch_next(); - - test_thread.move_time_forward(10); - - filter.notify_key(&KeyEvent { action: KeyEventAction::UP, ..BASE_KEY_EVENT }); - test_thread.dispatch_next(); + 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 + }); + + std::thread::sleep(Duration::from_nanos(SLOW_KEYS_THRESHOLD_NS as u64 / 2)); + + now = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds(); + filter.notify_key(&KeyEvent { + action: KeyEventAction::UP, + downTime: now, + eventTime: now, + ..BASE_KEY_EVENT + }); - 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(); - - test_thread.move_time_forward(100); + std::thread::sleep(Duration::from_nanos(2 * SLOW_KEYS_THRESHOLD_NS as u64)); - 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 @@ mod tests { 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 @@ mod tests { 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/Android.bp b/services/inputflinger/tests/Android.bp index 6ae9790aac..9b5db23151 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -67,6 +67,8 @@ cc_test { "InputProcessorConverter_test.cpp", "InputDispatcher_test.cpp", "InputReader_test.cpp", + "InputTraceSession.cpp", + "InputTracingTest.cpp", "InstrumentedInputReader.cpp", "LatencyTracker_test.cpp", "MultiTouchMotionAccumulator_test.cpp", diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp b/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp index e231bccb79..1360cd0208 100644 --- a/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp +++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp @@ -466,8 +466,15 @@ void FakeInputDispatcherPolicy::assertFilterInputEventWasCalledInternal( mFilteredEvent = nullptr; } -gui::Uid FakeInputDispatcherPolicy::getPackageUid(std::string) { - return gui::Uid::INVALID; +gui::Uid FakeInputDispatcherPolicy::getPackageUid(std::string pkg) { + std::scoped_lock lock(mLock); + auto it = mPackageUidMap.find(pkg); + return it != mPackageUidMap.end() ? it->second : gui::Uid::INVALID; +} + +void FakeInputDispatcherPolicy::addPackageUidMapping(std::string package, gui::Uid uid) { + std::scoped_lock lock(mLock); + mPackageUidMap.insert_or_assign(std::move(package), uid); } } // namespace android diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.h b/services/inputflinger/tests/FakeInputDispatcherPolicy.h index d83924f202..2cc018ef05 100644 --- a/services/inputflinger/tests/FakeInputDispatcherPolicy.h +++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.h @@ -115,6 +115,7 @@ public: void setUnhandledKeyHandler(std::function<std::optional<KeyEvent>(const KeyEvent&)> handler); void assertUnhandledKeyReported(int32_t keycode); void assertUnhandledKeyNotReported(); + void addPackageUidMapping(std::string package, gui::Uid uid); private: std::mutex mLock; @@ -150,6 +151,8 @@ private: std::queue<int32_t> mReportedUnhandledKeycodes GUARDED_BY(mLock); std::function<std::optional<KeyEvent>(const KeyEvent&)> mUnhandledKeyHandler GUARDED_BY(mLock); + std::map<std::string, gui::Uid> mPackageUidMap GUARDED_BY(mLock); + /** * All three ANR-related callbacks behave the same way, so we use this generic function to wait * for a specific container to become non-empty. When the container is non-empty, return the diff --git a/services/inputflinger/tests/FakeWindows.h b/services/inputflinger/tests/FakeWindows.h index c0c8975e76..26c2b4b1e7 100644 --- a/services/inputflinger/tests/FakeWindows.h +++ b/services/inputflinger/tests/FakeWindows.h @@ -157,6 +157,16 @@ public: inline void setSpy(bool spy) { mInfo.setInputConfig(InputConfig::SPY, spy); } + inline void setSecure(bool secure) { + if (secure) { + mInfo.layoutParamsFlags |= gui::WindowInfo::Flag::SECURE; + } else { + using namespace ftl::flag_operators; + mInfo.layoutParamsFlags &= ~gui::WindowInfo::Flag::SECURE; + } + mInfo.setInputConfig(InputConfig::SENSITIVE_FOR_TRACING, secure); + } + inline void setInterceptsStylus(bool interceptsStylus) { mInfo.setInputConfig(InputConfig::INTERCEPTS_STYLUS, interceptsStylus); } @@ -229,10 +239,14 @@ public: std::unique_ptr<KeyEvent> consumeKey(bool handled = true); - inline void consumeKeyEvent(const ::testing::Matcher<KeyEvent>& matcher) { + inline std::unique_ptr<KeyEvent> consumeKeyEvent(const ::testing::Matcher<KeyEvent>& matcher) { std::unique_ptr<KeyEvent> keyEvent = consumeKey(); - ASSERT_NE(nullptr, keyEvent); - ASSERT_THAT(*keyEvent, matcher); + EXPECT_NE(nullptr, keyEvent); + if (!keyEvent) { + return nullptr; + } + EXPECT_THAT(*keyEvent, matcher); + return keyEvent; } inline void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) { diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 1d46c9a1e2..367bc70a1c 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -9790,163 +9790,13 @@ TEST_F(MultiTouchInputMapperTest_ExternalDevice, Viewports_Fallback) { 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 @@ TEST_F(MultiTouchInputMapperTest, Process_UnCapturedTouchpadPointer) { 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 @@ TEST_F(MultiTouchInputMapperTest, WhenCapturedAndNotCaptured_GetSources) { // 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/inputflinger/tests/InputTraceSession.cpp b/services/inputflinger/tests/InputTraceSession.cpp new file mode 100644 index 0000000000..32acb5f288 --- /dev/null +++ b/services/inputflinger/tests/InputTraceSession.cpp @@ -0,0 +1,209 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "InputTraceSession.h" + +#include <NotifyArgsBuilders.h> +#include <android-base/logging.h> +#include <gtest/gtest.h> +#include <input/PrintTools.h> + +#include <utility> + +namespace android { + +using perfetto::protos::pbzero::AndroidInputEvent; +using perfetto::protos::pbzero::AndroidInputEventConfig; +using perfetto::protos::pbzero::AndroidKeyEvent; +using perfetto::protos::pbzero::AndroidMotionEvent; +using perfetto::protos::pbzero::AndroidWindowInputDispatchEvent; + +// These operator<< definitions must be in the global namespace for them to be accessible to the +// GTEST library. They cannot be in the anonymous namespace. +static std::ostream& operator<<(std::ostream& out, + const std::variant<KeyEvent, MotionEvent>& event) { + std::visit([&](const auto& e) { out << e; }, event); + return out; +} + +static std::ostream& operator<<(std::ostream& out, + const InputTraceSession::WindowDispatchEvent& event) { + out << "Window dispatch to windowId: " << event.window->getId() << ", event: " << event.event; + return out; +} + +namespace { + +inline uint32_t getId(const std::variant<KeyEvent, MotionEvent>& event) { + return std::visit([&](const auto& e) { return e.getId(); }, event); +} + +std::unique_ptr<perfetto::TracingSession> startTrace( + const std::function<void(protozero::HeapBuffered<AndroidInputEventConfig>&)>& configure) { + protozero::HeapBuffered<AndroidInputEventConfig> inputEventConfig{}; + configure(inputEventConfig); + + perfetto::TraceConfig config; + config.add_buffers()->set_size_kb(1024); // Record up to 1 MiB. + auto* dataSourceConfig = config.add_data_sources()->mutable_config(); + dataSourceConfig->set_name("android.input.inputevent"); + dataSourceConfig->set_android_input_event_config_raw(inputEventConfig.SerializeAsString()); + + std::unique_ptr<perfetto::TracingSession> tracingSession(perfetto::Tracing::NewTrace()); + tracingSession->Setup(config); + tracingSession->StartBlocking(); + return tracingSession; +} + +std::string stopTrace(std::unique_ptr<perfetto::TracingSession> tracingSession) { + tracingSession->StopBlocking(); + std::vector<char> traceChars(tracingSession->ReadTraceBlocking()); + return {traceChars.data(), traceChars.size()}; +} + +// Decodes the trace, and returns all of the traced input events, and whether they were each +// traced as a redacted event. +auto decodeTrace(const std::string& rawTrace) { + using namespace perfetto::protos::pbzero; + + ArrayMap<AndroidMotionEvent::Decoder, bool /*redacted*/> tracedMotions; + ArrayMap<AndroidKeyEvent::Decoder, bool /*redacted*/> tracedKeys; + ArrayMap<AndroidWindowInputDispatchEvent::Decoder, bool /*redacted*/> tracedWindowDispatches; + + Trace::Decoder trace{rawTrace}; + if (trace.has_packet()) { + auto it = trace.packet(); + while (it) { + TracePacket::Decoder packet{it->as_bytes()}; + if (packet.has_android_input_event()) { + AndroidInputEvent::Decoder event{packet.android_input_event()}; + if (event.has_dispatcher_motion_event()) { + tracedMotions.emplace_back(event.dispatcher_motion_event(), + /*redacted=*/false); + } + if (event.has_dispatcher_motion_event_redacted()) { + tracedMotions.emplace_back(event.dispatcher_motion_event_redacted(), + /*redacted=*/true); + } + if (event.has_dispatcher_key_event()) { + tracedKeys.emplace_back(event.dispatcher_key_event(), + /*redacted=*/false); + } + if (event.has_dispatcher_key_event_redacted()) { + tracedKeys.emplace_back(event.dispatcher_key_event_redacted(), + /*redacted=*/true); + } + if (event.has_dispatcher_window_dispatch_event()) { + tracedWindowDispatches.emplace_back(event.dispatcher_window_dispatch_event(), + /*redacted=*/false); + } + if (event.has_dispatcher_window_dispatch_event_redacted()) { + tracedWindowDispatches + .emplace_back(event.dispatcher_window_dispatch_event_redacted(), + /*redacted=*/true); + } + } + it++; + } + } + return std::tuple{std::move(tracedMotions), std::move(tracedKeys), + std::move(tracedWindowDispatches)}; +} + +bool eventMatches(const MotionEvent& expected, const AndroidMotionEvent::Decoder& traced) { + return static_cast<uint32_t>(expected.getId()) == traced.event_id(); +} + +bool eventMatches(const KeyEvent& expected, const AndroidKeyEvent::Decoder& traced) { + return static_cast<uint32_t>(expected.getId()) == traced.event_id(); +} + +bool eventMatches(const InputTraceSession::WindowDispatchEvent& expected, + const AndroidWindowInputDispatchEvent::Decoder& traced) { + return static_cast<uint32_t>(getId(expected.event)) == traced.event_id() && + expected.window->getId() == traced.window_id(); +} + +template <typename ExpectedEvents, typename TracedEvents> +void verifyExpectedEventsTraced(const ExpectedEvents& expectedEvents, + const TracedEvents& tracedEvents, std::string_view name) { + uint32_t totalExpectedCount = 0; + + for (const auto& [expectedEvent, expectedLevel] : expectedEvents) { + int32_t totalMatchCount = 0; + int32_t redactedMatchCount = 0; + for (const auto& [tracedEvent, isRedacted] : tracedEvents) { + if (eventMatches(expectedEvent, tracedEvent)) { + totalMatchCount++; + if (isRedacted) { + redactedMatchCount++; + } + } + } + switch (expectedLevel) { + case Level::NONE: + ASSERT_EQ(totalMatchCount, 0) << "Event should not be traced, but it was traced" + << "\n\tExpected event: " << expectedEvent; + break; + case Level::REDACTED: + case Level::COMPLETE: + ASSERT_EQ(totalMatchCount, 1) + << "Event should match exactly one traced event, but it matched: " + << totalMatchCount << "\n\tExpected event: " << expectedEvent; + ASSERT_EQ(redactedMatchCount, expectedLevel == Level::REDACTED ? 1 : 0); + totalExpectedCount++; + break; + } + } + + ASSERT_EQ(tracedEvents.size(), totalExpectedCount) + << "The number of traced " << name + << " events does not exactly match the number of expected events"; +} + +} // namespace + +InputTraceSession::InputTraceSession( + std::function<void(protozero::HeapBuffered<AndroidInputEventConfig>&)> configure) + : mPerfettoSession(startTrace(std::move(configure))) {} + +InputTraceSession::~InputTraceSession() { + const auto rawTrace = stopTrace(std::move(mPerfettoSession)); + verifyExpectations(rawTrace); +} + +void InputTraceSession::expectMotionTraced(Level level, const MotionEvent& event) { + mExpectedMotions.emplace_back(event, level); +} + +void InputTraceSession::expectKeyTraced(Level level, const KeyEvent& event) { + mExpectedKeys.emplace_back(event, level); +} + +void InputTraceSession::expectDispatchTraced(Level level, const WindowDispatchEvent& event) { + mExpectedWindowDispatches.emplace_back(event, level); +} + +void InputTraceSession::verifyExpectations(const std::string& rawTrace) { + auto [tracedMotions, tracedKeys, tracedWindowDispatches] = decodeTrace(rawTrace); + + verifyExpectedEventsTraced(mExpectedMotions, tracedMotions, "motion"); + verifyExpectedEventsTraced(mExpectedKeys, tracedKeys, "key"); + verifyExpectedEventsTraced(mExpectedWindowDispatches, tracedWindowDispatches, + "window dispatch"); +} + +} // namespace android diff --git a/services/inputflinger/tests/InputTraceSession.h b/services/inputflinger/tests/InputTraceSession.h new file mode 100644 index 0000000000..ed20bc8343 --- /dev/null +++ b/services/inputflinger/tests/InputTraceSession.h @@ -0,0 +1,85 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "FakeWindows.h" + +#include <android-base/logging.h> +#include <gtest/gtest.h> +#include <input/Input.h> +#include <perfetto/config/android/android_input_event_config.pbzero.h> +#include <perfetto/trace/android/android_input_event.pbzero.h> +#include <perfetto/trace/trace.pbzero.h> +#include <perfetto/tracing.h> +#include <variant> +#include <vector> + +namespace android { + +/** + * Tracing level constants used for adding expectations to the InputTraceSession. + */ +enum class Level { + NONE, + REDACTED, + COMPLETE, +}; + +template <typename K, typename V> +using ArrayMap = std::vector<std::pair<K, V>>; + +/** + * A scoped representation of a tracing session that is used to make assertions on the trace. + * + * When the trace session is created, an "android.input.inputevent" trace will be started + * synchronously with the given configuration. While the trace is ongoing, the caller must + * specify the events that are expected to be in the trace using the expect* methods. + * + * When the session is destroyed, the trace is stopped synchronously, and all expectations will + * be verified using the gtest framework. This acts as a strict verifier, where the verification + * will fail both if an expected event does not show up in the trace and if there is an extra + * event in the trace that was not expected. Ordering is NOT verified for any events. + */ +class InputTraceSession { +public: + explicit InputTraceSession( + std::function<void( + protozero::HeapBuffered<perfetto::protos::pbzero::AndroidInputEventConfig>&)> + configure); + + ~InputTraceSession(); + + void expectMotionTraced(Level level, const MotionEvent& event); + + void expectKeyTraced(Level level, const KeyEvent& event); + + struct WindowDispatchEvent { + std::variant<KeyEvent, MotionEvent> event; + sp<FakeWindowHandle> window; + }; + void expectDispatchTraced(Level level, const WindowDispatchEvent& event); + +private: + std::unique_ptr<perfetto::TracingSession> mPerfettoSession; + ArrayMap<WindowDispatchEvent, Level> mExpectedWindowDispatches; + ArrayMap<MotionEvent, Level> mExpectedMotions; + ArrayMap<KeyEvent, Level> mExpectedKeys; + + void verifyExpectations(const std::string& rawTrace); +}; + +} // namespace android diff --git a/services/inputflinger/tests/InputTracingTest.cpp b/services/inputflinger/tests/InputTracingTest.cpp new file mode 100644 index 0000000000..fe4d6d9a8f --- /dev/null +++ b/services/inputflinger/tests/InputTracingTest.cpp @@ -0,0 +1,732 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "../InputCommonConverter.h" +#include "../dispatcher/InputDispatcher.h" +#include "../dispatcher/trace/InputTracingPerfettoBackend.h" +#include "../dispatcher/trace/ThreadedBackend.h" +#include "FakeApplicationHandle.h" +#include "FakeInputDispatcherPolicy.h" +#include "FakeWindows.h" +#include "InputTraceSession.h" +#include "TestEventMatchers.h" + +#include <NotifyArgsBuilders.h> +#include <android-base/logging.h> +#include <gtest/gtest.h> +#include <input/Input.h> +#include <perfetto/trace/android/android_input_event.pbzero.h> +#include <perfetto/trace/trace.pbzero.h> +#include <private/android_filesystem_config.h> +#include <map> +#include <vector> + +namespace android::inputdispatcher::trace { + +using perfetto::protos::pbzero::AndroidInputEventConfig; + +namespace { + +constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT; + +// Ensure common actions are interchangeable between keys and motions for convenience. +static_assert(static_cast<int32_t>(AMOTION_EVENT_ACTION_DOWN) == + static_cast<int32_t>(AKEY_EVENT_ACTION_DOWN)); +static_assert(static_cast<int32_t>(AMOTION_EVENT_ACTION_UP) == + static_cast<int32_t>(AKEY_EVENT_ACTION_UP)); +constexpr int32_t ACTION_DOWN = AMOTION_EVENT_ACTION_DOWN; +constexpr int32_t ACTION_MOVE = AMOTION_EVENT_ACTION_MOVE; +constexpr int32_t ACTION_UP = AMOTION_EVENT_ACTION_UP; +constexpr int32_t ACTION_CANCEL = AMOTION_EVENT_ACTION_CANCEL; + +constexpr gui::Pid PID{1}; + +constexpr gui::Uid ALLOWED_UID_1{10012}; +constexpr gui::Uid ALLOWED_UID_2{10013}; +constexpr gui::Uid DISALLOWED_UID_1{1}; +constexpr gui::Uid DISALLOWED_UID_2{99}; +constexpr gui::Uid UNLISTED_UID{12345}; + +const std::string ALLOWED_PKG_1{"allowed.pkg.1"}; +const std::string ALLOWED_PKG_2{"allowed.pkg.2"}; +const std::string DISALLOWED_PKG_1{"disallowed.pkg.1"}; +const std::string DISALLOWED_PKG_2{"disallowed.pkg.2"}; + +const std::shared_ptr<FakeApplicationHandle> APP = std::make_shared<FakeApplicationHandle>(); + +} // namespace + +// --- InputTracingTest --- + +class InputTracingTest : public testing::Test { +protected: + std::unique_ptr<FakeInputDispatcherPolicy> mFakePolicy; + std::unique_ptr<InputDispatcher> mDispatcher; + + void SetUp() override { + impl::PerfettoBackend::sUseInProcessBackendForTest = true; + + mFakePolicy = std::make_unique<FakeInputDispatcherPolicy>(); + mFakePolicy->addPackageUidMapping(ALLOWED_PKG_1, ALLOWED_UID_1); + mFakePolicy->addPackageUidMapping(ALLOWED_PKG_2, ALLOWED_UID_2); + mFakePolicy->addPackageUidMapping(DISALLOWED_PKG_1, DISALLOWED_UID_1); + mFakePolicy->addPackageUidMapping(DISALLOWED_PKG_2, DISALLOWED_UID_2); + + auto tracingBackend = std::make_unique<impl::ThreadedBackend<impl::PerfettoBackend>>( + impl::PerfettoBackend([this](const auto& pkg) { + return static_cast<InputDispatcherPolicyInterface&>(*mFakePolicy) + .getPackageUid(pkg); + })); + mRequestTracerIdle = tracingBackend->getIdleWaiterForTesting(); + mDispatcher = std::make_unique<InputDispatcher>(*mFakePolicy, std::move(tracingBackend)); + + mDispatcher->setInputDispatchMode(/*enabled=*/true, /*frozen=*/false); + ASSERT_EQ(OK, mDispatcher->start()); + } + + void TearDown() override { + ASSERT_EQ(OK, mDispatcher->stop()); + mDispatcher.reset(); + mFakePolicy.reset(); + } + + void waitForTracerIdle() { + mDispatcher->waitForIdle(); + mRequestTracerIdle(); + } + + void setFocusedWindow(const sp<gui::WindowInfoHandle>& window) { + gui::FocusRequest request; + request.token = window->getToken(); + request.windowName = window->getName(); + request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC); + request.displayId = window->getInfo()->displayId; + mDispatcher->setFocusedWindow(request); + } + + void tapAndExpect(const std::vector<const sp<FakeWindowHandle>>& windows, + Level inboundTraceLevel, Level dispatchTraceLevel, InputTraceSession& s) { + const auto down = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(110)) + .build(); + mDispatcher->notifyMotion(down); + s.expectMotionTraced(inboundTraceLevel, toMotionEvent(down)); + for (const auto& window : windows) { + auto consumed = window->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + s.expectDispatchTraced(dispatchTraceLevel, {*consumed, window}); + } + + const auto up = MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(110)) + .build(); + mDispatcher->notifyMotion(up); + s.expectMotionTraced(inboundTraceLevel, toMotionEvent(up)); + for (const auto& window : windows) { + auto consumed = window->consumeMotionEvent(WithMotionAction(ACTION_UP)); + s.expectDispatchTraced(dispatchTraceLevel, {*consumed, window}); + } + } + + void keypressAndExpect(const std::vector<const sp<FakeWindowHandle>>& windows, + Level inboundTraceLevel, Level dispatchTraceLevel, + InputTraceSession& s) { + const auto down = KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).build(); + mDispatcher->notifyKey(down); + s.expectKeyTraced(inboundTraceLevel, toKeyEvent(down)); + for (const auto& window : windows) { + auto consumed = window->consumeKeyEvent(WithKeyAction(ACTION_DOWN)); + s.expectDispatchTraced(dispatchTraceLevel, {*consumed, window}); + } + + const auto up = KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).build(); + mDispatcher->notifyKey(up); + s.expectKeyTraced(inboundTraceLevel, toKeyEvent(up)); + for (const auto& window : windows) { + auto consumed = window->consumeKeyEvent(WithKeyAction(ACTION_UP)); + s.expectDispatchTraced(dispatchTraceLevel, {*consumed, window}); + } + } + +private: + std::function<void()> mRequestTracerIdle; +}; + +TEST_F(InputTracingTest, EmptyConfigTracesNothing) { + InputTraceSession s{[](auto& config) {}}; + + auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID); + mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); + setFocusedWindow(window); + window->consumeFocusEvent(true); + + tapAndExpect({window}, Level::NONE, Level::NONE, s); + keypressAndExpect({window}, Level::NONE, Level::NONE, s); + + waitForTracerIdle(); +} + +TEST_F(InputTracingTest, TraceAll) { + InputTraceSession s{ + [](auto& config) { config->set_mode(AndroidInputEventConfig::TRACE_MODE_TRACE_ALL); }}; + + auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID); + mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); + setFocusedWindow(window); + window->consumeFocusEvent(true); + + tapAndExpect({window}, Level::COMPLETE, Level::COMPLETE, s); + keypressAndExpect({window}, Level::COMPLETE, Level::COMPLETE, s); + + waitForTracerIdle(); +} + +TEST_F(InputTracingTest, NoRulesTracesNothing) { + InputTraceSession s{[](auto& config) { + config->set_trace_dispatcher_input_events(true); + config->set_trace_dispatcher_window_dispatch(true); + config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES); + }}; + + auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID); + mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); + setFocusedWindow(window); + window->consumeFocusEvent(true); + + tapAndExpect({window}, Level::NONE, Level::NONE, s); + keypressAndExpect({window}, Level::NONE, Level::NONE, s); + + waitForTracerIdle(); +} + +TEST_F(InputTracingTest, EmptyRuleMatchesEverything) { + InputTraceSession s{[](auto& config) { + config->set_trace_dispatcher_input_events(true); + config->set_trace_dispatcher_window_dispatch(true); + config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES); + // Rule: Match everything as COMPLETE + auto rule = config->add_rules(); + rule->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_COMPLETE); + }}; + + auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID); + mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); + setFocusedWindow(window); + window->consumeFocusEvent(true); + + tapAndExpect({window}, Level::COMPLETE, Level::COMPLETE, s); + keypressAndExpect({window}, Level::COMPLETE, Level::COMPLETE, s); + + waitForTracerIdle(); +} + +TEST_F(InputTracingTest, UnspecifiedTracelLevel) { + InputTraceSession s{[](auto& config) { + config->set_trace_dispatcher_input_events(true); + config->set_trace_dispatcher_window_dispatch(true); + config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES); + // Rule: Match everything, trace level unspecified + auto rule = config->add_rules(); + }}; + + auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID); + mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); + setFocusedWindow(window); + window->consumeFocusEvent(true); + + // Event is not traced by default if trace level is unspecified + tapAndExpect({window}, Level::NONE, Level::NONE, s); + keypressAndExpect({window}, Level::NONE, Level::NONE, s); + + waitForTracerIdle(); +} + +TEST_F(InputTracingTest, MatchSecureWindow) { + InputTraceSession s{[](auto& config) { + config->set_trace_dispatcher_input_events(true); + config->set_trace_dispatcher_window_dispatch(true); + config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES); + // Rule: Match secure windows as COMPLETE + auto rule = config->add_rules(); + rule->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_COMPLETE); + rule->set_match_secure(true); + }}; + + // Add a normal window and a spy window. + auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID); + auto spy = sp<FakeWindowHandle>::make(APP, mDispatcher, "Spy", DISPLAY_ID); + spy->setSpy(true); + spy->setTrustedOverlay(true); + mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0}); + + // Since neither are secure windows, events should not be traced. + tapAndExpect({spy, window}, Level::NONE, Level::NONE, s); + + // Events should be matched as secure if any of the target windows is marked as secure. + spy->setSecure(true); + mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0}); + tapAndExpect({spy, window}, Level::COMPLETE, Level::COMPLETE, s); + + spy->setSecure(false); + window->setSecure(true); + mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0}); + tapAndExpect({spy, window}, Level::COMPLETE, Level::COMPLETE, s); + + spy->setSecure(true); + window->setSecure(true); + mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0}); + tapAndExpect({spy, window}, Level::COMPLETE, Level::COMPLETE, s); + + spy->setSecure(false); + window->setSecure(false); + mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0}); + tapAndExpect({spy, window}, Level::NONE, Level::NONE, s); + + waitForTracerIdle(); +} + +TEST_F(InputTracingTest, MatchImeConnectionActive) { + InputTraceSession s{[](auto& config) { + config->set_trace_dispatcher_input_events(true); + config->set_trace_dispatcher_window_dispatch(true); + config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES); + // Rule: Match IME Connection Active as COMPLETE + auto rule = config->add_rules(); + rule->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_COMPLETE); + rule->set_match_ime_connection_active(true); + }}; + + // Add a normal window and a spy window. + auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID); + auto spy = sp<FakeWindowHandle>::make(APP, mDispatcher, "Spy", DISPLAY_ID); + spy->setSpy(true); + spy->setTrustedOverlay(true); + mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0}); + + // Since IME connection is not active, events should not be traced. + tapAndExpect({spy, window}, Level::NONE, Level::NONE, s); + + mDispatcher->setInputMethodConnectionIsActive(true); + tapAndExpect({spy, window}, Level::COMPLETE, Level::COMPLETE, s); + + mDispatcher->setInputMethodConnectionIsActive(false); + tapAndExpect({spy, window}, Level::NONE, Level::NONE, s); + + waitForTracerIdle(); +} + +TEST_F(InputTracingTest, MatchAllPackages) { + InputTraceSession s{[](auto& config) { + config->set_trace_dispatcher_input_events(true); + config->set_trace_dispatcher_window_dispatch(true); + config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES); + // Rule: Match all package as COMPLETE + auto rule = config->add_rules(); + rule->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_COMPLETE); + rule->add_match_all_packages(ALLOWED_PKG_1); + rule->add_match_all_packages(ALLOWED_PKG_2); + }}; + + // All windows are allowlisted. + auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID); + window->setOwnerInfo(PID, ALLOWED_UID_1); + auto spy = sp<FakeWindowHandle>::make(APP, mDispatcher, "Spy", DISPLAY_ID); + spy->setOwnerInfo(PID, ALLOWED_UID_2); + spy->setSpy(true); + spy->setTrustedOverlay(true); + auto systemSpy = sp<FakeWindowHandle>::make(APP, mDispatcher, "Spy", DISPLAY_ID); + systemSpy->setOwnerInfo(PID, gui::Uid{AID_SYSTEM}); + systemSpy->setSpy(true); + systemSpy->setTrustedOverlay(true); + mDispatcher->onWindowInfosChanged( + {{*systemSpy->getInfo(), *spy->getInfo(), *window->getInfo()}, {}, 0, 0}); + + tapAndExpect({systemSpy, spy, window}, Level::COMPLETE, Level::COMPLETE, s); + + // Add a disallowed spy. This will result in the event not being traced for all windows. + auto disallowedSpy = sp<FakeWindowHandle>::make(APP, mDispatcher, "Spy", DISPLAY_ID); + disallowedSpy->setOwnerInfo(PID, DISALLOWED_UID_1); + disallowedSpy->setSpy(true); + disallowedSpy->setTrustedOverlay(true); + mDispatcher->onWindowInfosChanged({{*systemSpy->getInfo(), *spy->getInfo(), + *disallowedSpy->getInfo(), *window->getInfo()}, + {}, + 0, + 0}); + + tapAndExpect({systemSpy, spy, disallowedSpy, window}, Level::NONE, Level::NONE, s); + + // Change the owner of the disallowed spy to one for which we don't have a package mapping. + disallowedSpy->setOwnerInfo(PID, UNLISTED_UID); + mDispatcher->onWindowInfosChanged({{*systemSpy->getInfo(), *spy->getInfo(), + *disallowedSpy->getInfo(), *window->getInfo()}, + {}, + 0, + 0}); + + tapAndExpect({systemSpy, spy, disallowedSpy, window}, Level::NONE, Level::NONE, s); + + // Remove the disallowed spy. Events are traced again. + mDispatcher->onWindowInfosChanged( + {{*systemSpy->getInfo(), *spy->getInfo(), *window->getInfo()}, {}, 0, 0}); + + tapAndExpect({systemSpy, spy, window}, Level::COMPLETE, Level::COMPLETE, s); + + waitForTracerIdle(); +} + +TEST_F(InputTracingTest, MatchAnyPackages) { + InputTraceSession s{[](auto& config) { + config->set_trace_dispatcher_input_events(true); + config->set_trace_dispatcher_window_dispatch(true); + config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES); + // Rule: Match any package as COMPLETE + auto rule = config->add_rules(); + rule->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_COMPLETE); + rule->add_match_any_packages(ALLOWED_PKG_1); + rule->add_match_any_packages(ALLOWED_PKG_2); + }}; + + // Just a disallowed window. Events are not traced. + auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID); + window->setOwnerInfo(PID, DISALLOWED_UID_1); + mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); + + tapAndExpect({window}, Level::NONE, Level::NONE, s); + + // Add a spy for which we don't have a package mapping. Events are still not traced. + auto disallowedSpy = sp<FakeWindowHandle>::make(APP, mDispatcher, "Spy", DISPLAY_ID); + disallowedSpy->setOwnerInfo(PID, UNLISTED_UID); + disallowedSpy->setSpy(true); + disallowedSpy->setTrustedOverlay(true); + mDispatcher->onWindowInfosChanged({{*disallowedSpy->getInfo(), *window->getInfo()}, {}, 0, 0}); + + tapAndExpect({disallowedSpy, window}, Level::NONE, Level::NONE, s); + + // Add an allowed spy. Events are now traced for all packages. + auto spy = sp<FakeWindowHandle>::make(APP, mDispatcher, "Spy", DISPLAY_ID); + spy->setOwnerInfo(PID, ALLOWED_UID_1); + spy->setSpy(true); + spy->setTrustedOverlay(true); + mDispatcher->onWindowInfosChanged( + {{*disallowedSpy->getInfo(), *spy->getInfo(), *window->getInfo()}, {}, 0, 0}); + + tapAndExpect({disallowedSpy, spy, window}, Level::COMPLETE, Level::COMPLETE, s); + + // Add another disallowed spy. Events are still traced. + auto disallowedSpy2 = sp<FakeWindowHandle>::make(APP, mDispatcher, "Spy", DISPLAY_ID); + disallowedSpy2->setOwnerInfo(PID, DISALLOWED_UID_2); + disallowedSpy2->setSpy(true); + disallowedSpy2->setTrustedOverlay(true); + mDispatcher->onWindowInfosChanged({{*disallowedSpy->getInfo(), *disallowedSpy2->getInfo(), + *spy->getInfo(), *window->getInfo()}, + {}, + 0, + 0}); + + tapAndExpect({disallowedSpy, disallowedSpy2, spy, window}, Level::COMPLETE, Level::COMPLETE, s); + + waitForTracerIdle(); +} + +TEST_F(InputTracingTest, MultipleMatchersInOneRule) { + InputTraceSession s{[](auto& config) { + config->set_trace_dispatcher_input_events(true); + config->set_trace_dispatcher_window_dispatch(true); + config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES); + // Rule: Match all of the following conditions as COMPLETE + auto rule = config->add_rules(); + rule->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_COMPLETE); + rule->add_match_all_packages(ALLOWED_PKG_1); + rule->add_match_all_packages(ALLOWED_PKG_2); + rule->add_match_any_packages(ALLOWED_PKG_1); + rule->add_match_any_packages(DISALLOWED_PKG_1); + rule->set_match_secure(false); + rule->set_match_ime_connection_active(false); + }}; + + // A single window into an allowed UID. Matches all matchers. + auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID); + window->setOwnerInfo(PID, ALLOWED_UID_1); + mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); + + tapAndExpect({window}, Level::COMPLETE, Level::COMPLETE, s); + + // Secure window does not match. + window->setSecure(true); + mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); + + tapAndExpect({window}, Level::NONE, Level::NONE, s); + + // IME Connection Active does not match. + window->setSecure(false); + mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); + mDispatcher->setInputMethodConnectionIsActive(true); + + tapAndExpect({window}, Level::NONE, Level::NONE, s); + + // Event going to DISALLOWED_PKG_1 does not match because it's not listed in match_all_packages. + mDispatcher->setInputMethodConnectionIsActive(false); + auto disallowedSpy = sp<FakeWindowHandle>::make(APP, mDispatcher, "Spy", DISPLAY_ID); + disallowedSpy->setOwnerInfo(PID, DISALLOWED_UID_1); + disallowedSpy->setSpy(true); + disallowedSpy->setTrustedOverlay(true); + mDispatcher->onWindowInfosChanged({{*disallowedSpy->getInfo(), *window->getInfo()}, {}, 0, 0}); + + tapAndExpect({disallowedSpy, window}, Level::NONE, Level::NONE, s); + + // Event going to ALLOWED_PKG_1 does not match because it's not listed in match_any_packages. + window->setOwnerInfo(PID, ALLOWED_UID_2); + mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); + + tapAndExpect({window}, Level::NONE, Level::NONE, s); + + // All conditions match. + auto spy = sp<FakeWindowHandle>::make(APP, mDispatcher, "Spy", DISPLAY_ID); + spy->setOwnerInfo(PID, ALLOWED_UID_1); + spy->setSpy(true); + spy->setTrustedOverlay(true); + mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0}); + + tapAndExpect({spy, window}, Level::COMPLETE, Level::COMPLETE, s); + + waitForTracerIdle(); +} + +TEST_F(InputTracingTest, MultipleRulesMatchInOrder) { + InputTraceSession s{[](auto& config) { + config->set_trace_dispatcher_input_events(true); + config->set_trace_dispatcher_window_dispatch(true); + config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES); + // Rule: Don't trace secure events + auto rule1 = config->add_rules(); + rule1->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_NONE); + rule1->set_match_secure(true); + // Rule: Trace matched packages as COMPLETE when IME inactive + auto rule2 = config->add_rules(); + rule2->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_COMPLETE); + rule2->add_match_all_packages(ALLOWED_PKG_1); + rule2->add_match_all_packages(ALLOWED_PKG_2); + rule2->set_match_ime_connection_active(false); + // Rule: Trace the rest of the events as REDACTED + auto rule3 = config->add_rules(); + rule3->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_REDACTED); + }}; + + auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID); + window->setOwnerInfo(PID, ALLOWED_UID_1); + mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); + + tapAndExpect({window}, Level::COMPLETE, Level::COMPLETE, s); + + // Verify that the first rule that matches in the order that they are specified is the + // one that applies to the event. + mDispatcher->setInputMethodConnectionIsActive(true); + tapAndExpect({window}, Level::REDACTED, Level::REDACTED, s); + + mDispatcher->setInputMethodConnectionIsActive(false); + auto spy = sp<FakeWindowHandle>::make(APP, mDispatcher, "Spy", DISPLAY_ID); + spy->setOwnerInfo(PID, ALLOWED_UID_2); + spy->setSpy(true); + spy->setTrustedOverlay(true); + spy->setSecure(true); + mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0}); + + tapAndExpect({spy, window}, Level::NONE, Level::NONE, s); + + spy->setSecure(false); + mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0}); + + tapAndExpect({spy, window}, Level::COMPLETE, Level::COMPLETE, s); + + spy->setOwnerInfo(PID, DISALLOWED_UID_1); + mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0}); + + tapAndExpect({spy, window}, Level::REDACTED, Level::REDACTED, s); + + waitForTracerIdle(); +} + +TEST_F(InputTracingTest, TraceInboundEvents) { + InputTraceSession s{[](auto& config) { + // Only trace inbounds events - don't trace window dispatch + config->set_trace_dispatcher_input_events(true); + config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES); + // Rule: Trace everything as REDACTED + auto rule1 = config->add_rules(); + rule1->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_REDACTED); + }}; + + auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID); + window->setOwnerInfo(PID, ALLOWED_UID_1); + mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); + + // Only the inbound events are traced. No dispatch events are traced. + tapAndExpect({window}, Level::REDACTED, Level::NONE, s); + + // Notify a down event, which should be traced. + const auto down = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(110)) + .build(); + s.expectMotionTraced(Level::REDACTED, toMotionEvent(down)); + mDispatcher->notifyMotion(down); + auto consumed = window->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + s.expectDispatchTraced(Level::NONE, {*consumed, window}); + + // Force a cancel event to be synthesized. This should not be traced, because only inbound + // events are requested. + mDispatcher->cancelCurrentTouch(); + consumed = window->consumeMotionEvent(WithMotionAction(ACTION_CANCEL)); + s.expectMotionTraced(Level::NONE, *consumed); + s.expectDispatchTraced(Level::NONE, {*consumed, window}); + + waitForTracerIdle(); +} + +TEST_F(InputTracingTest, TraceWindowDispatch) { + InputTraceSession s{[](auto& config) { + // Only trace window dispatch - don't trace event details + config->set_trace_dispatcher_window_dispatch(true); + config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES); + // Rule: Trace everything as REDACTED + auto rule1 = config->add_rules(); + rule1->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_REDACTED); + }}; + + auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID); + window->setOwnerInfo(PID, ALLOWED_UID_1); + mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); + + // Only dispatch events are traced. No inbound events are traced. + tapAndExpect({window}, Level::NONE, Level::REDACTED, s); + + // Notify a down event; the dispatch should be traced. + const auto down = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(110)) + .build(); + s.expectMotionTraced(Level::NONE, toMotionEvent(down)); + mDispatcher->notifyMotion(down); + auto consumed = window->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + s.expectDispatchTraced(Level::REDACTED, {*consumed, window}); + + // Force a cancel event to be synthesized. All events that are dispatched should be traced. + mDispatcher->cancelCurrentTouch(); + consumed = window->consumeMotionEvent(WithMotionAction(ACTION_CANCEL)); + s.expectMotionTraced(Level::NONE, *consumed); + s.expectDispatchTraced(Level::REDACTED, {*consumed, window}); +} + +TEST_F(InputTracingTest, SimultaneousTracingSessions) { + auto s1 = std::make_unique<InputTraceSession>( + [](auto& config) { config->set_mode(AndroidInputEventConfig::TRACE_MODE_TRACE_ALL); }); + + auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID); + mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); + setFocusedWindow(window); + window->consumeFocusEvent(true); + + tapAndExpect({window}, Level::COMPLETE, Level::COMPLETE, *s1); + keypressAndExpect({window}, Level::COMPLETE, Level::COMPLETE, *s1); + + auto s2 = std::make_unique<InputTraceSession>([](auto& config) { + config->set_trace_dispatcher_input_events(true); + config->set_trace_dispatcher_window_dispatch(true); + config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES); + // Rule: Trace all events as REDACTED when IME inactive + auto rule = config->add_rules(); + rule->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_REDACTED); + rule->set_match_ime_connection_active(false); + }); + + auto s3 = std::make_unique<InputTraceSession>([](auto& config) { + // Only trace window dispatch + config->set_trace_dispatcher_window_dispatch(true); + config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES); + // Rule: Trace non-secure events as COMPLETE + auto rule = config->add_rules(); + rule->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_COMPLETE); + rule->set_match_secure(false); + }); + + // Down event should be recorded on all traces. + const auto down = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(110)) + .build(); + mDispatcher->notifyMotion(down); + s1->expectMotionTraced(Level::COMPLETE, toMotionEvent(down)); + s2->expectMotionTraced(Level::REDACTED, toMotionEvent(down)); + s3->expectMotionTraced(Level::NONE, toMotionEvent(down)); + auto consumed = window->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + s1->expectDispatchTraced(Level::COMPLETE, {*consumed, window}); + s2->expectDispatchTraced(Level::REDACTED, {*consumed, window}); + s3->expectDispatchTraced(Level::COMPLETE, {*consumed, window}); + + // Move event when IME is active. + mDispatcher->setInputMethodConnectionIsActive(true); + const auto move1 = MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(110)) + .build(); + mDispatcher->notifyMotion(move1); + s1->expectMotionTraced(Level::COMPLETE, toMotionEvent(move1)); + s2->expectMotionTraced(Level::NONE, toMotionEvent(move1)); + s3->expectMotionTraced(Level::NONE, toMotionEvent(move1)); + consumed = window->consumeMotionEvent(WithMotionAction(ACTION_MOVE)); + s1->expectDispatchTraced(Level::COMPLETE, {*consumed, window}); + s2->expectDispatchTraced(Level::NONE, {*consumed, window}); + s3->expectDispatchTraced(Level::COMPLETE, {*consumed, window}); + + // Move event after window became secure. + mDispatcher->setInputMethodConnectionIsActive(false); + window->setSecure(true); + mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); + const auto move2 = MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(110)) + .build(); + mDispatcher->notifyMotion(move2); + s1->expectMotionTraced(Level::COMPLETE, toMotionEvent(move2)); + s2->expectMotionTraced(Level::REDACTED, toMotionEvent(move2)); + s3->expectMotionTraced(Level::NONE, toMotionEvent(move2)); + consumed = window->consumeMotionEvent(WithMotionAction(ACTION_MOVE)); + s1->expectDispatchTraced(Level::COMPLETE, {*consumed, window}); + s2->expectDispatchTraced(Level::REDACTED, {*consumed, window}); + s3->expectDispatchTraced(Level::NONE, {*consumed, window}); + + waitForTracerIdle(); + s2.reset(); + + // Up event. + window->setSecure(false); + mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); + const auto up = MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(110)) + .build(); + mDispatcher->notifyMotion(up); + s1->expectMotionTraced(Level::COMPLETE, toMotionEvent(up)); + s3->expectMotionTraced(Level::NONE, toMotionEvent(up)); + consumed = window->consumeMotionEvent(WithMotionAction(ACTION_UP)); + s1->expectDispatchTraced(Level::COMPLETE, {*consumed, window}); + s3->expectDispatchTraced(Level::COMPLETE, {*consumed, window}); + + waitForTracerIdle(); + s3.reset(); + + tapAndExpect({window}, Level::COMPLETE, Level::COMPLETE, *s1); + keypressAndExpect({window}, Level::COMPLETE, Level::COMPLETE, *s1); + + waitForTracerIdle(); + s1.reset(); +} + +} // namespace android::inputdispatcher::trace diff --git a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp index 0e3fdbb0dc..10dc9276d2 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 @@ size_t LayerState::getHash() const { 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 39fce2b629..03758b345a 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, updateSourceCrop) { } 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 35d0ffb6e9..a1210b4e21 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_doesNotMatchDifferentCompositionTypes } 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_matchesSingleDifferenceInSingleLayer) } 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_alwaysMatchesClientComposition) { } 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 @@ struct PredictionTest : public testing::Test { }; 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_recordCandidateAndRetrieveExactMatch) { } 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, getPredictedPlan_recordCandidateAndRetrieveApproximateMatc } 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 0aee7d497c..ffc1dd7979 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 @@ pdwilliams@google.com racarr@google.com ramindani@google.com rnlee@google.com +sallyqi@google.com scroggo@google.com vishnun@google.com diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 7b313c3c99..21f1cb3e4a 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> @@ -5252,17 +5253,19 @@ bool SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelin 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); + } } } } @@ -7805,20 +7808,6 @@ status_t SurfaceFlinger::setSchedAttr(bool enabled) { 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); diff --git a/services/vibratorservice/test/VibratorHalControllerTest.cpp b/services/vibratorservice/test/VibratorHalControllerTest.cpp index 9b95d74899..15fde914f6 100644 --- a/services/vibratorservice/test/VibratorHalControllerTest.cpp +++ b/services/vibratorservice/test/VibratorHalControllerTest.cpp @@ -255,16 +255,17 @@ TEST_F(VibratorHalControllerTest, TestScheduledCallbackSurvivesReconnection) { .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 1933a118ef..b584f64bbf 100644 --- a/services/vibratorservice/test/test_utils.h +++ b/services/vibratorservice/test/test_utils.h @@ -85,10 +85,36 @@ private: ~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_ |