Merge "Remove API level mentions from typedefs" into main
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 4b7af45..3711362 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -683,7 +683,7 @@
return err;
}
-int Parcel::compareData(const Parcel& other) {
+int Parcel::compareData(const Parcel& other) const {
size_t size = dataSize();
if (size != other.dataSize()) {
return size < other.dataSize() ? -1 : 1;
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 5cc0830..e2b23be 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -92,7 +92,7 @@
LIBBINDER_EXPORTED status_t appendFrom(const Parcel* parcel, size_t start, size_t len);
- LIBBINDER_EXPORTED int compareData(const Parcel& other);
+ LIBBINDER_EXPORTED int compareData(const Parcel& other) const;
LIBBINDER_EXPORTED status_t compareDataInRange(size_t thisOffset, const Parcel& other,
size_t otherOffset, size_t length,
int* result) const;
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index 2deb254..4545d7b 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -15,6 +15,8 @@
"libbinder_ndk_sys",
"libdowncast_rs",
"liblibc",
+ "liblog_rust",
+ "libnix",
],
host_supported: true,
vendor_available: true,
@@ -79,6 +81,9 @@
shared_libs: [
"libbinder_ndk",
],
+ rustlibs: [
+ "liblibc",
+ ],
host_supported: true,
vendor_available: true,
product_available: true,
@@ -129,9 +134,18 @@
// rustified
"libbinder_ndk_bindgen_flags.txt",
],
+ bindgen_flags: [
+ "--blocklist-type",
+ "sockaddr",
+ "--raw-line",
+ "use libc::sockaddr;",
+ ],
shared_libs: [
"libbinder_ndk",
],
+ rustlibs: [
+ "liblibc",
+ ],
host_supported: true,
vendor_available: true,
product_available: true,
@@ -185,6 +199,8 @@
"libbinder_ndk_sys",
"libdowncast_rs",
"liblibc",
+ "liblog_rust",
+ "libnix",
],
}
@@ -196,4 +212,7 @@
auto_gen_config: true,
clippy_lints: "none",
lints: "none",
+ rustlibs: [
+ "liblibc",
+ ],
}
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index e048696..0e8e388 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -104,6 +104,8 @@
mod service;
#[cfg(not(trusty))]
mod state;
+#[cfg(not(any(android_vendor, android_vndk)))]
+mod system_only;
use binder_ndk_sys as sys;
@@ -120,6 +122,8 @@
};
#[cfg(not(trusty))]
pub use state::{ProcessState, ThreadState};
+#[cfg(not(any(android_vendor, android_vndk)))]
+pub use system_only::{Accessor, ConnectionInfo};
/// Binder result containing a [`Status`] on error.
pub type Result<T> = std::result::Result<T, Status>;
diff --git a/libs/binder/rust/src/system_only.rs b/libs/binder/rust/src/system_only.rs
new file mode 100644
index 0000000..a91d84d
--- /dev/null
+++ b/libs/binder/rust/src/system_only.rs
@@ -0,0 +1,187 @@
+/*
+ * 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::proxy::SpIBinder;
+use crate::sys;
+
+use std::ffi::{c_void, CStr, CString};
+use std::os::raw::c_char;
+
+use libc::sockaddr;
+use nix::sys::socket::{SockaddrLike, UnixAddr, VsockAddr};
+use std::sync::Arc;
+use std::{fmt, ptr};
+
+/// Rust wrapper around ABinderRpc_Accessor objects for RPC binder service management.
+///
+/// Dropping the `Accessor` will drop the underlying object and the binder it owns.
+pub struct Accessor {
+ accessor: *mut sys::ABinderRpc_Accessor,
+}
+
+impl fmt::Debug for Accessor {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "ABinderRpc_Accessor({:p})", self.accessor)
+ }
+}
+
+/// Socket connection info required for libbinder to connect to a service.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum ConnectionInfo {
+ /// For vsock connection
+ Vsock(VsockAddr),
+ /// For unix domain socket connection
+ Unix(UnixAddr),
+}
+
+/// Safety: A `Accessor` is a wrapper around `ABinderRpc_Accessor` which is
+/// `Sync` and `Send`. As
+/// `ABinderRpc_Accessor` is threadsafe, this structure is too.
+/// The Fn owned the Accessor has `Sync` and `Send` properties
+unsafe impl Send for Accessor {}
+
+/// Safety: A `Accessor` is a wrapper around `ABinderRpc_Accessor` which is
+/// `Sync` and `Send`. As `ABinderRpc_Accessor` is threadsafe, this structure is too.
+/// The Fn owned the Accessor has `Sync` and `Send` properties
+unsafe impl Sync for Accessor {}
+
+impl Accessor {
+ /// Create a new accessor that will call the given callback when its
+ /// connection info is required.
+ /// The callback object and all objects it captures are owned by the Accessor
+ /// and will be deleted some time after the Accessor is Dropped. If the callback
+ /// is being called when the Accessor is Dropped, the callback will not be deleted
+ /// immediately.
+ pub fn new<F>(instance: &str, callback: F) -> Accessor
+ where
+ F: Fn(&str) -> Option<ConnectionInfo> + Send + Sync + 'static,
+ {
+ let callback: *mut c_void = Arc::into_raw(Arc::new(callback)) as *mut c_void;
+ let inst = CString::new(instance).unwrap();
+
+ // Safety: The function pointer is a valid connection_info callback.
+ // This call returns an owned `ABinderRpc_Accessor` pointer which
+ // must be destroyed via `ABinderRpc_Accessor_delete` when no longer
+ // needed.
+ // When the underlying ABinderRpc_Accessor is deleted, it will call
+ // the cookie_decr_refcount callback to release its strong ref.
+ let accessor = unsafe {
+ sys::ABinderRpc_Accessor_new(
+ inst.as_ptr(),
+ Some(Self::connection_info::<F>),
+ callback,
+ Some(Self::cookie_decr_refcount::<F>),
+ )
+ };
+
+ Accessor { accessor }
+ }
+
+ /// Get the underlying binder for this Accessor for when it needs to be either
+ /// registered with service manager or sent to another process.
+ pub fn as_binder(&self) -> Option<SpIBinder> {
+ // Safety: `ABinderRpc_Accessor_asBinder` 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::ABinderRpc_Accessor_asBinder(self.accessor)) }
+ }
+
+ /// Callback invoked from C++ when the connection info is needed.
+ ///
+ /// # Safety
+ ///
+ /// The `instance` parameter must be a non-null pointer to a valid C string for
+ /// CStr::from_ptr. The memory must contain a valid null terminator at the end of
+ /// the string within isize::MAX from the pointer. The memory must not be mutated for
+ /// the duration of this function call and must be valid for reads from the pointer
+ /// to the null terminator.
+ /// The `cookie` parameter must be the cookie for an `Arc<F>` and
+ /// the caller must hold a ref-count to it.
+ unsafe extern "C" fn connection_info<F>(
+ instance: *const c_char,
+ cookie: *mut c_void,
+ ) -> *mut binder_ndk_sys::ABinderRpc_ConnectionInfo
+ where
+ F: Fn(&str) -> Option<ConnectionInfo> + Send + Sync + 'static,
+ {
+ if cookie.is_null() || instance.is_null() {
+ log::error!("Cookie({cookie:p}) or instance({instance:p}) is null!");
+ return ptr::null_mut();
+ }
+ // Safety: The caller promises that `cookie` is for an Arc<F>.
+ let callback = unsafe { (cookie as *const F).as_ref().unwrap() };
+
+ // Safety: The caller in libbinder_ndk will have already verified this is a valid
+ // C string
+ let inst = unsafe {
+ match CStr::from_ptr(instance).to_str() {
+ Ok(s) => s,
+ Err(err) => {
+ log::error!("Failed to get a valid C string! {err:?}");
+ return ptr::null_mut();
+ }
+ }
+ };
+
+ let connection = match callback(inst) {
+ Some(con) => con,
+ None => {
+ return ptr::null_mut();
+ }
+ };
+
+ match connection {
+ ConnectionInfo::Vsock(addr) => {
+ // Safety: The sockaddr is being copied in the NDK API
+ unsafe { sys::ABinderRpc_ConnectionInfo_new(addr.as_ptr(), addr.len()) }
+ }
+ ConnectionInfo::Unix(addr) => {
+ // Safety: The sockaddr is being copied in the NDK API
+ // The cast is from sockaddr_un* to sockaddr*.
+ unsafe {
+ sys::ABinderRpc_ConnectionInfo_new(addr.as_ptr() as *const sockaddr, addr.len())
+ }
+ }
+ }
+ }
+
+ /// Callback that decrements the ref-count.
+ /// This is invoked from C++ when a binder is unlinked.
+ ///
+ /// # Safety
+ ///
+ /// The `cookie` parameter must be the cookie for an `Arc<F>` and
+ /// the owner must give up a ref-count to it.
+ unsafe extern "C" fn cookie_decr_refcount<F>(cookie: *mut c_void)
+ where
+ F: Fn(&str) -> Option<ConnectionInfo> + Send + Sync + 'static,
+ {
+ // Safety: The caller promises that `cookie` is for an Arc<F>.
+ unsafe { Arc::decrement_strong_count(cookie as *const F) };
+ }
+}
+
+impl Drop for Accessor {
+ fn drop(&mut self) {
+ // Safety: `self.accessor` is always a valid, owned
+ // `ABinderRpc_Accessor` pointer returned by
+ // `ABinderRpc_Accessor_new` when `self` was created. This delete
+ // method can only be called once when `self` is dropped.
+ unsafe {
+ sys::ABinderRpc_Accessor_delete(self.accessor);
+ }
+ }
+}
diff --git a/libs/binder/rust/sys/BinderBindings.hpp b/libs/binder/rust/sys/BinderBindings.hpp
index 65fa2ca..bd666fe 100644
--- a/libs/binder/rust/sys/BinderBindings.hpp
+++ b/libs/binder/rust/sys/BinderBindings.hpp
@@ -20,6 +20,7 @@
#include <android/binder_parcel.h>
#include <android/binder_parcel_platform.h>
#include <android/binder_process.h>
+#include <android/binder_rpc.h>
#include <android/binder_shell.h>
#include <android/binder_stability.h>
#include <android/binder_status.h>
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index 5359832..bdb7e4a 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -384,8 +384,8 @@
use std::time::Duration;
use binder::{
- BinderFeatures, DeathRecipient, FromIBinder, IBinder, Interface, SpIBinder, StatusCode,
- Strong,
+ Accessor, BinderFeatures, DeathRecipient, FromIBinder, IBinder, Interface, SpIBinder,
+ StatusCode, Strong,
};
// Import from impl API for testing only, should not be necessary as long as
// you are using AIDL.
@@ -908,6 +908,43 @@
assert_eq!(service.test().unwrap(), service_name);
}
+ struct ToBeDeleted {
+ deleted: Arc<AtomicBool>,
+ }
+
+ impl Drop for ToBeDeleted {
+ fn drop(&mut self) {
+ assert!(!self.deleted.load(Ordering::Relaxed));
+ self.deleted.store(true, Ordering::Relaxed);
+ }
+ }
+
+ #[test]
+ fn test_accessor_callback_destruction() {
+ let deleted: Arc<AtomicBool> = Arc::new(AtomicBool::new(false));
+ {
+ let accessor: Accessor;
+ {
+ let helper = ToBeDeleted { deleted: deleted.clone() };
+ let get_connection_info = move |_instance: &str| {
+ // Capture this object so we can see it get destructed
+ // after the parent scope
+ let _ = &helper;
+ None
+ };
+ accessor = Accessor::new("foo.service", get_connection_info);
+ }
+
+ match accessor.as_binder() {
+ Some(_) => {
+ assert!(!deleted.load(Ordering::Relaxed));
+ }
+ None => panic!("failed to get that accessor binder"),
+ }
+ }
+ assert!(deleted.load(Ordering::Relaxed));
+ }
+
#[tokio::test]
async fn reassociate_rust_binder_async() {
let service_name = "testing_service";
diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp
index fbab8f0..cac054e 100644
--- a/libs/binder/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/Android.bp
@@ -39,6 +39,7 @@
"smoreland@google.com",
"waghpawan@google.com",
],
+ triage_assignee: "smoreland@google.com",
use_for_presubmit: true,
},
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index e378b86..07f0143 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -25,6 +25,8 @@
#include <binder/ParcelableHolder.h>
#include <binder/PersistableBundle.h>
#include <binder/Status.h>
+#include <fuzzbinder/random_binder.h>
+#include <fuzzbinder/random_fd.h>
#include <utils/Flattenable.h>
#include "../../Utils.h"
@@ -115,14 +117,6 @@
p.setDataPosition(pos);
FUZZ_LOG() << "setDataPosition done";
},
- [] (const ::android::Parcel& p, FuzzedDataProvider& provider) {
- size_t len = provider.ConsumeIntegralInRange<size_t>(0, 1024);
- std::vector<uint8_t> bytes = provider.ConsumeBytes<uint8_t>(len);
- FUZZ_LOG() << "about to setData: " <<(bytes.data() ? HexString(bytes.data(), bytes.size()) : "null");
- // TODO: allow all read and write operations
- (*const_cast<::android::Parcel*>(&p)).setData(bytes.data(), bytes.size());
- FUZZ_LOG() << "setData done";
- },
PARCEL_READ_NO_STATUS(size_t, allowFds),
PARCEL_READ_NO_STATUS(size_t, hasFileDescriptors),
PARCEL_READ_NO_STATUS(std::vector<android::sp<android::IBinder>>, debugReadAllStrongBinders),
@@ -404,5 +398,113 @@
FUZZ_LOG() << " toString() result: " << toString;
},
};
+
+std::vector<ParcelWrite<::android::Parcel>> BINDER_PARCEL_WRITE_FUNCTIONS {
+ [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
+ FUZZ_LOG() << "about to call setDataSize";
+ size_t len = provider.ConsumeIntegralInRange<size_t>(0, 1024);
+ p.setDataSize(len);
+ },
+ [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
+ FUZZ_LOG() << "about to call setDataCapacity";
+ size_t len = provider.ConsumeIntegralInRange<size_t>(0, 1024);
+ p.setDataCapacity(len);
+ },
+ [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
+ FUZZ_LOG() << "about to call setData";
+ size_t len = provider.ConsumeIntegralInRange<size_t>(0, 1024);
+ std::vector<uint8_t> bytes = provider.ConsumeBytes<uint8_t>(len);
+ p.setData(bytes.data(), bytes.size());
+ },
+ [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* options) {
+ FUZZ_LOG() << "about to call appendFrom";
+
+ std::vector<uint8_t> bytes = provider.ConsumeBytes<uint8_t>(provider.ConsumeIntegralInRange<size_t>(0, 4096));
+ ::android::Parcel p2;
+ fillRandomParcel(&p2, FuzzedDataProvider(bytes.data(), bytes.size()), options);
+
+ int32_t start = provider.ConsumeIntegral<int32_t>();
+ int32_t len = provider.ConsumeIntegral<int32_t>();
+ p.appendFrom(&p2, start, len);
+ },
+ [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
+ FUZZ_LOG() << "about to call pushAllowFds";
+ bool val = provider.ConsumeBool();
+ p.pushAllowFds(val);
+ },
+ [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
+ FUZZ_LOG() << "about to call restoreAllowFds";
+ bool val = provider.ConsumeBool();
+ p.restoreAllowFds(val);
+ },
+ // markForBinder - covered by fillRandomParcel, aborts if called multiple times
+ // markForRpc - covered by fillRandomParcel, aborts if called multiple times
+ [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
+ FUZZ_LOG() << "about to call writeInterfaceToken";
+ std::string interface = provider.ConsumeRandomLengthString();
+ p.writeInterfaceToken(android::String16(interface.c_str()));
+ },
+ [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
+ FUZZ_LOG() << "about to call setEnforceNoDataAvail";
+ p.setEnforceNoDataAvail(provider.ConsumeBool());
+ },
+ [] (::android::Parcel& p, FuzzedDataProvider& /* provider */, android::RandomParcelOptions* /*options*/) {
+ FUZZ_LOG() << "about to call setServiceFuzzing";
+ p.setServiceFuzzing();
+ },
+ [] (::android::Parcel& p, FuzzedDataProvider& /* provider */, android::RandomParcelOptions* /*options*/) {
+ FUZZ_LOG() << "about to call freeData";
+ p.freeData();
+ },
+ [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
+ FUZZ_LOG() << "about to call write";
+ size_t len = provider.ConsumeIntegralInRange<size_t>(0, 256);
+ std::vector<uint8_t> bytes = provider.ConsumeBytes<uint8_t>(len);
+ p.write(bytes.data(), bytes.size());
+ },
+ // write* - write functions all implemented by calling 'write' itself.
+ [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* options) {
+ FUZZ_LOG() << "about to call writeStrongBinder";
+
+ // TODO: this logic is somewhat duplicated with random parcel
+ android::sp<android::IBinder> binder;
+ if (provider.ConsumeBool() && options->extraBinders.size() > 0) {
+ binder = options->extraBinders.at(
+ provider.ConsumeIntegralInRange<size_t>(0, options->extraBinders.size() - 1));
+ } else {
+ binder = android::getRandomBinder(&provider);
+ options->extraBinders.push_back(binder);
+ }
+
+ p.writeStrongBinder(binder);
+ },
+ [] (::android::Parcel& p, FuzzedDataProvider& /* provider */, android::RandomParcelOptions* /*options*/) {
+ FUZZ_LOG() << "about to call writeFileDescriptor (no ownership)";
+ p.writeFileDescriptor(STDERR_FILENO, false /* takeOwnership */);
+ },
+ [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* options) {
+ FUZZ_LOG() << "about to call writeFileDescriptor (take ownership)";
+ std::vector<unique_fd> fds = android::getRandomFds(&provider);
+ if (fds.size() == 0) return;
+
+ p.writeDupFileDescriptor(fds.at(0).get());
+ options->extraFds.insert(options->extraFds.end(),
+ std::make_move_iterator(fds.begin() + 1),
+ std::make_move_iterator(fds.end()));
+ },
+ // TODO: writeBlob
+ // TODO: writeDupImmutableBlobFileDescriptor
+ // TODO: writeObject (or make the API private more likely)
+ [] (::android::Parcel& p, FuzzedDataProvider& /* provider */, android::RandomParcelOptions* /*options*/) {
+ FUZZ_LOG() << "about to call writeNoException";
+ p.writeNoException();
+ },
+ [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
+ FUZZ_LOG() << "about to call replaceCallingWorkSourceUid";
+ uid_t uid = provider.ConsumeIntegral<uid_t>();
+ p.replaceCallingWorkSourceUid(uid);
+ },
+};
+
// clang-format on
#pragma clang diagnostic pop
diff --git a/libs/binder/tests/parcel_fuzzer/binder.h b/libs/binder/tests/parcel_fuzzer/binder.h
index 0c51d68..b0ac140 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.h
+++ b/libs/binder/tests/parcel_fuzzer/binder.h
@@ -21,3 +21,4 @@
#include "parcel_fuzzer.h"
extern std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS;
+extern std::vector<ParcelWrite<::android::Parcel>> BINDER_PARCEL_WRITE_FUNCTIONS;
diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
index e3a3371..3f8d71d 100644
--- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
@@ -20,8 +20,11 @@
#include "aidl/parcelables/GenericDataParcelable.h"
#include "aidl/parcelables/SingleDataParcelable.h"
+#include <android/binder_libbinder.h>
#include <android/binder_parcel_utils.h>
#include <android/binder_parcelable_utils.h>
+#include <fuzzbinder/random_binder.h>
+#include <fuzzbinder/random_fd.h>
#include "util.h"
@@ -211,16 +214,51 @@
binder_status_t status = AParcel_marshal(p.aParcel(), buffer, start, len);
FUZZ_LOG() << "status: " << status;
},
- [](const NdkParcelAdapter& /*p*/, FuzzedDataProvider& provider) {
- FUZZ_LOG() << "about to unmarshal AParcel";
+};
+std::vector<ParcelWrite<NdkParcelAdapter>> BINDER_NDK_PARCEL_WRITE_FUNCTIONS{
+ [] (NdkParcelAdapter& p, FuzzedDataProvider& provider, android::RandomParcelOptions* options) {
+ FUZZ_LOG() << "about to call AParcel_writeStrongBinder";
+
+ // TODO: this logic is somewhat duplicated with random parcel
+ android::sp<android::IBinder> binder;
+ if (provider.ConsumeBool() && options->extraBinders.size() > 0) {
+ binder = options->extraBinders.at(
+ provider.ConsumeIntegralInRange<size_t>(0, options->extraBinders.size() - 1));
+ } else {
+ binder = android::getRandomBinder(&provider);
+ options->extraBinders.push_back(binder);
+ }
+
+ ndk::SpAIBinder abinder = ndk::SpAIBinder(AIBinder_fromPlatformBinder(binder));
+ AParcel_writeStrongBinder(p.aParcel(), abinder.get());
+ },
+ [] (NdkParcelAdapter& p, FuzzedDataProvider& provider, android::RandomParcelOptions* options) {
+ FUZZ_LOG() << "about to call AParcel_writeParcelFileDescriptor";
+
+ auto fds = android::getRandomFds(&provider);
+ if (fds.size() == 0) return;
+
+ AParcel_writeParcelFileDescriptor(p.aParcel(), fds.at(0).get());
+ options->extraFds.insert(options->extraFds.end(),
+ std::make_move_iterator(fds.begin() + 1),
+ std::make_move_iterator(fds.end()));
+ },
+ // all possible data writes can be done as a series of 4-byte reads
+ [] (NdkParcelAdapter& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
+ FUZZ_LOG() << "about to call AParcel_writeInt32";
+ int32_t val = provider.ConsumeIntegral<int32_t>();
+ AParcel_writeInt32(p.aParcel(), val);
+ },
+ [] (NdkParcelAdapter& p, FuzzedDataProvider& /* provider */, android::RandomParcelOptions* /*options*/) {
+ FUZZ_LOG() << "about to call AParcel_reset";
+ AParcel_reset(p.aParcel());
+ },
+ [](NdkParcelAdapter& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
+ FUZZ_LOG() << "about to call AParcel_unmarshal";
size_t len = provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes());
- std::vector<uint8_t> parcelData = provider.ConsumeBytes<uint8_t>(len);
- const uint8_t* buffer = parcelData.data();
- const size_t bufferLen = parcelData.size();
- NdkParcelAdapter adapter;
- binder_status_t status = AParcel_unmarshal(adapter.aParcel(), buffer, bufferLen);
+ std::vector<uint8_t> data = provider.ConsumeBytes<uint8_t>(len);
+ binder_status_t status = AParcel_unmarshal(p.aParcel(), data.data(), data.size());
FUZZ_LOG() << "status: " << status;
},
-
};
// clang-format on
diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.h b/libs/binder/tests/parcel_fuzzer/binder_ndk.h
index d19f25b..0c8b725 100644
--- a/libs/binder/tests/parcel_fuzzer/binder_ndk.h
+++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.h
@@ -50,3 +50,4 @@
};
extern std::vector<ParcelRead<NdkParcelAdapter>> BINDER_NDK_PARCEL_READ_FUNCTIONS;
+extern std::vector<ParcelWrite<NdkParcelAdapter>> BINDER_NDK_PARCEL_WRITE_FUNCTIONS;
diff --git a/libs/binder/tests/parcel_fuzzer/main.cpp b/libs/binder/tests/parcel_fuzzer/main.cpp
index a57d07f..192f9d5 100644
--- a/libs/binder/tests/parcel_fuzzer/main.cpp
+++ b/libs/binder/tests/parcel_fuzzer/main.cpp
@@ -80,6 +80,7 @@
(void)binder->transact(code, data, &reply, flag);
}
+// start with a Parcel full of data (e.g. like you get from another process)
template <typename P>
void doReadFuzz(const char* backend, const std::vector<ParcelRead<P>>& reads,
FuzzedDataProvider&& provider) {
@@ -95,10 +96,10 @@
RandomParcelOptions options;
P p;
- fillRandomParcel(&p, std::move(provider), &options);
+ fillRandomParcel(&p, std::move(provider), &options); // consumes provider
// since we are only using a byte to index
- CHECK(reads.size() <= 255) << reads.size();
+ CHECK_LE(reads.size(), 255u) << reads.size();
FUZZ_LOG() << "backend: " << backend;
FUZZ_LOG() << "input: " << HexString(p.data(), p.dataSize());
@@ -115,26 +116,29 @@
}
}
-// Append two random parcels.
template <typename P>
-void doAppendFuzz(const char* backend, FuzzedDataProvider&& provider) {
- int32_t start = provider.ConsumeIntegral<int32_t>();
- int32_t len = provider.ConsumeIntegral<int32_t>();
-
- std::vector<uint8_t> bytes = provider.ConsumeBytes<uint8_t>(
- provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes()));
-
- // same options so that FDs and binders could be shared in both Parcels
+void doReadWriteFuzz(const char* backend, const std::vector<ParcelRead<P>>& reads,
+ const std::vector<ParcelWrite<P>>& writes, FuzzedDataProvider&& provider) {
RandomParcelOptions options;
+ P p;
- P p0, p1;
- fillRandomParcel(&p0, FuzzedDataProvider(bytes.data(), bytes.size()), &options);
- fillRandomParcel(&p1, std::move(provider), &options);
+ // since we are only using a byte to index
+ CHECK_LE(reads.size() + writes.size(), 255u) << reads.size();
FUZZ_LOG() << "backend: " << backend;
- FUZZ_LOG() << "start: " << start << " len: " << len;
- p0.appendFrom(&p1, start, len);
+ while (provider.remaining_bytes() > 0) {
+ uint8_t idx = provider.ConsumeIntegralInRange<uint8_t>(0, reads.size() + writes.size() - 1);
+
+ FUZZ_LOG() << "Instruction " << idx << " avail: " << p.dataAvail()
+ << " pos: " << p.dataPosition() << " cap: " << p.dataCapacity();
+
+ if (idx < reads.size()) {
+ reads.at(idx)(p, provider);
+ } else {
+ writes.at(idx - reads.size())(p, provider, &options);
+ }
+ }
}
void* NothingClass_onCreate(void* args) {
@@ -156,7 +160,7 @@
FuzzedDataProvider provider = FuzzedDataProvider(data, size);
- const std::function<void(FuzzedDataProvider &&)> fuzzBackend[] = {
+ const std::function<void(FuzzedDataProvider&&)> fuzzBackend[] = {
[](FuzzedDataProvider&& provider) {
doTransactFuzz<
::android::hardware::Parcel>("hwbinder",
@@ -187,10 +191,14 @@
std::move(provider));
},
[](FuzzedDataProvider&& provider) {
- doAppendFuzz<::android::Parcel>("binder", std::move(provider));
+ doReadWriteFuzz<::android::Parcel>("binder", BINDER_PARCEL_READ_FUNCTIONS,
+ BINDER_PARCEL_WRITE_FUNCTIONS,
+ std::move(provider));
},
[](FuzzedDataProvider&& provider) {
- doAppendFuzz<NdkParcelAdapter>("binder_ndk", std::move(provider));
+ doReadWriteFuzz<NdkParcelAdapter>("binder_ndk", BINDER_NDK_PARCEL_READ_FUNCTIONS,
+ BINDER_NDK_PARCEL_WRITE_FUNCTIONS,
+ std::move(provider));
},
};
diff --git a/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h b/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h
index 765a93e..dbd0cae 100644
--- a/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h
+++ b/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h
@@ -15,9 +15,13 @@
*/
#pragma once
+#include <fuzzbinder/random_parcel.h>
#include <fuzzer/FuzzedDataProvider.h>
#include <functional>
template <typename P>
using ParcelRead = std::function<void(const P& p, FuzzedDataProvider& provider)>;
+template <typename P>
+using ParcelWrite = std::function<void(P& p, FuzzedDataProvider& provider,
+ android::RandomParcelOptions* options)>;
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index 60fb00e..701fb43 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -207,3 +207,10 @@
description: "Allow user to enable key repeats or configure timeout before key repeat and key repeat delay rates."
bug: "336585002"
}
+
+flag {
+ name: "rotary_input_telemetry"
+ namespace: "wear_frameworks"
+ description: "Enable telemetry for rotary input"
+ bug: "370353565"
+}
diff --git a/services/inputflinger/InputThread.cpp b/services/inputflinger/InputThread.cpp
index bd4b192..7cf4e39 100644
--- a/services/inputflinger/InputThread.cpp
+++ b/services/inputflinger/InputThread.cpp
@@ -26,6 +26,16 @@
namespace {
+bool applyInputEventProfile(const Thread& thread) {
+#if defined(__ANDROID__)
+ return SetTaskProfiles(thread.getTid(), {"InputPolicy"});
+#else
+ // Since thread information is not available and there's no benefit of
+ // applying the task profile on host, return directly.
+ return true;
+#endif
+}
+
// Implementation of Thread from libutils.
class InputThreadImpl : public Thread {
public:
@@ -47,11 +57,11 @@
InputThread::InputThread(std::string name, std::function<void()> loop, std::function<void()> wake,
bool isInCriticalPath)
- : mName(name), mThreadWake(wake) {
+ : mThreadWake(wake) {
mThread = sp<InputThreadImpl>::make(loop);
- mThread->run(mName.c_str(), ANDROID_PRIORITY_URGENT_DISPLAY);
+ mThread->run(name.c_str(), ANDROID_PRIORITY_URGENT_DISPLAY);
if (input_flags::enable_input_policy_profile() && isInCriticalPath) {
- if (!applyInputEventProfile()) {
+ if (!applyInputEventProfile(*mThread)) {
LOG(ERROR) << "Couldn't apply input policy profile for " << name;
}
}
@@ -75,14 +85,4 @@
#endif
}
-bool InputThread::applyInputEventProfile() {
-#if defined(__ANDROID__)
- return SetTaskProfiles(mThread->getTid(), {"InputPolicy"});
-#else
- // Since thread information is not available and there's no benefit of
- // applying the task profile on host, return directly.
- return true;
-#endif
-}
-
} // namespace android
diff --git a/services/inputflinger/include/InputThread.h b/services/inputflinger/include/InputThread.h
index 595f5a2..ed92b8f 100644
--- a/services/inputflinger/include/InputThread.h
+++ b/services/inputflinger/include/InputThread.h
@@ -35,10 +35,8 @@
bool isCallingThread();
private:
- std::string mName;
std::function<void()> mThreadWake;
sp<Thread> mThread;
- bool applyInputEventProfile();
};
} // namespace android
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index b76e8c5..b3cd35c 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -90,10 +90,14 @@
"libstatslog",
"libstatspull",
"libutils",
+ "libstatssocket",
],
static_libs: [
"libchrome-gestures",
"libui-types",
+ "libexpresslog",
+ "libtextclassifier_hash_static",
+ "libstatslog_express",
],
header_libs: [
"libbatteryservice_headers",
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index b72cc6e..c633b49 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -20,6 +20,8 @@
#include "RotaryEncoderInputMapper.h"
+#include <Counter.h>
+#include <com_android_input_flags.h>
#include <utils/Timers.h>
#include <optional>
@@ -27,14 +29,26 @@
namespace android {
+using android::expresslog::Counter;
+
+constexpr float kDefaultResolution = 0;
constexpr float kDefaultScaleFactor = 1.0f;
+constexpr int32_t kDefaultMinRotationsToLog = 3;
RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig)
+ : RotaryEncoderInputMapper(deviceContext, readerConfig,
+ Counter::logIncrement /* telemetryLogCounter */) {}
+
+RotaryEncoderInputMapper::RotaryEncoderInputMapper(
+ InputDeviceContext& deviceContext, const InputReaderConfiguration& readerConfig,
+ std::function<void(const char*, int64_t)> telemetryLogCounter)
: InputMapper(deviceContext, readerConfig),
mSource(AINPUT_SOURCE_ROTARY_ENCODER),
mScalingFactor(kDefaultScaleFactor),
- mOrientation(ui::ROTATION_0) {}
+ mResolution(kDefaultResolution),
+ mOrientation(ui::ROTATION_0),
+ mTelemetryLogCounter(telemetryLogCounter) {}
RotaryEncoderInputMapper::~RotaryEncoderInputMapper() {}
@@ -51,6 +65,7 @@
if (!res.has_value()) {
ALOGW("Rotary Encoder device configuration file didn't specify resolution!\n");
}
+ mResolution = res.value_or(kDefaultResolution);
std::optional<float> scalingFactor = config.getFloat("device.scalingFactor");
if (!scalingFactor.has_value()) {
ALOGW("Rotary Encoder device configuration file didn't specify scaling factor,"
@@ -59,7 +74,22 @@
}
mScalingFactor = scalingFactor.value_or(kDefaultScaleFactor);
info.addMotionRange(AMOTION_EVENT_AXIS_SCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
- res.value_or(0.0f) * mScalingFactor);
+ mResolution * mScalingFactor);
+
+ if (com::android::input::flags::rotary_input_telemetry()) {
+ mMinRotationsToLog = config.getInt("rotary_encoder.min_rotations_to_log");
+ if (!mMinRotationsToLog.has_value()) {
+ ALOGI("Rotary Encoder device configuration file didn't specify min log rotation.");
+ } else if (*mMinRotationsToLog <= 0) {
+ ALOGE("Rotary Encoder device configuration specified non-positive min log rotation "
+ ": %d. Telemetry logging of rotations disabled.",
+ *mMinRotationsToLog);
+ mMinRotationsToLog = {};
+ } else {
+ ALOGD("Rotary Encoder telemetry enabled. mMinRotationsToLog=%d",
+ *mMinRotationsToLog);
+ }
+ }
}
}
@@ -121,10 +151,29 @@
return out;
}
+void RotaryEncoderInputMapper::logScroll(float scroll) {
+ if (mResolution <= 0 || !mMinRotationsToLog) return;
+
+ mUnloggedScrolls += fabs(scroll);
+
+ // unitsPerRotation = (2 * PI * radians) * (units per radian (i.e. resolution))
+ const float unitsPerRotation = 2 * M_PI * mResolution;
+ const float scrollsPerMinRotationsToLog = *mMinRotationsToLog * unitsPerRotation;
+ const int32_t numMinRotationsToLog =
+ static_cast<int32_t>(mUnloggedScrolls / scrollsPerMinRotationsToLog);
+ mUnloggedScrolls = std::fmod(mUnloggedScrolls, scrollsPerMinRotationsToLog);
+ if (numMinRotationsToLog) {
+ mTelemetryLogCounter("input.value_rotary_input_device_full_rotation_count",
+ numMinRotationsToLog * (*mMinRotationsToLog));
+ }
+}
+
std::list<NotifyArgs> RotaryEncoderInputMapper::sync(nsecs_t when, nsecs_t readTime) {
std::list<NotifyArgs> out;
float scroll = mRotaryEncoderScrollAccumulator.getRelativeVWheel();
+ logScroll(scroll);
+
if (mSlopController) {
scroll = mSlopController->consumeEvent(when, scroll);
}
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
index 7e80415..d74ced1 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
@@ -46,13 +46,39 @@
int32_t mSource;
float mScalingFactor;
+ /** Units per rotation, provided via the `device.res` IDC property. */
+ float mResolution;
ui::Rotation mOrientation;
+ /**
+ * The minimum number of rotations to log for telemetry.
+ * Provided via `rotary_encoder.min_rotations_to_log` IDC property. If no value is provided in
+ * the IDC file, or if a non-positive value is provided, the telemetry will be disabled, and
+ * this value is set to the empty optional.
+ */
+ std::optional<int32_t> mMinRotationsToLog;
+ /**
+ * A function to log count for telemetry.
+ * The char* is the logging key, and the int64_t is the value to log.
+ * Abstracting the actual logging APIs via this function is helpful for simple unit testing.
+ */
+ std::function<void(const char*, int64_t)> mTelemetryLogCounter;
ui::LogicalDisplayId mDisplayId = ui::LogicalDisplayId::INVALID;
std::unique_ptr<SlopController> mSlopController;
+ /** Amount of raw scrolls (pre-slop) not yet logged for telemetry. */
+ float mUnloggedScrolls = 0;
+
explicit RotaryEncoderInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig);
+
+ /** This is a test constructor that allows injecting the expresslog Counter logic. */
+ RotaryEncoderInputMapper(InputDeviceContext& deviceContext,
+ const InputReaderConfiguration& readerConfig,
+ std::function<void(const char*, int64_t)> expressLogCounter);
[[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime);
+
+ /** Logs a given amount of scroll for telemetry. */
+ void logScroll(float scroll);
};
} // namespace android
diff --git a/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp b/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp
index 6607bc7..486d893 100644
--- a/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp
+++ b/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp
@@ -23,6 +23,8 @@
#include <android-base/logging.h>
#include <android_companion_virtualdevice_flags.h>
+#include <com_android_input_flags.h>
+#include <flag_macros.h>
#include <gtest/gtest.h>
#include <input/DisplayViewport.h>
#include <linux/input-event-codes.h>
@@ -100,6 +102,15 @@
EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL_HI_RES))
.WillRepeatedly(Return(false));
}
+
+ std::map<const char*, int64_t> mTelemetryLogCounts;
+
+ /**
+ * A fake function for telemetry logging.
+ * Records the log counts in the `mTelemetryLogCounts` map.
+ */
+ std::function<void(const char*, int64_t)> mTelemetryLogCounter =
+ [this](const char* key, int64_t value) { mTelemetryLogCounts[key] += value; };
};
TEST_F(RotaryEncoderInputMapperTest, ConfigureDisplayIdWithAssociatedViewport) {
@@ -187,4 +198,142 @@
WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), WithScroll(0.5f)))));
}
+TEST_F_WITH_FLAGS(RotaryEncoderInputMapperTest, RotaryInputTelemetryFlagOff_NoRotationLogging,
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(com::android::input::flags,
+ rotary_input_telemetry))) {
+ mPropertyMap.addProperty("device.res", "3");
+ mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration,
+ mTelemetryLogCounter);
+ InputDeviceInfo info;
+ mMapper->populateDeviceInfo(info);
+
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 70);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+ ASSERT_EQ(mTelemetryLogCounts.find("input.value_rotary_input_device_full_rotation_count"),
+ mTelemetryLogCounts.end());
+}
+
+TEST_F_WITH_FLAGS(RotaryEncoderInputMapperTest, ZeroResolution_NoRotationLogging,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
+ rotary_input_telemetry))) {
+ mPropertyMap.addProperty("device.res", "-3");
+ mPropertyMap.addProperty("rotary_encoder.min_rotations_to_log", "2");
+ mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration,
+ mTelemetryLogCounter);
+ InputDeviceInfo info;
+ mMapper->populateDeviceInfo(info);
+
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 700);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+ ASSERT_EQ(mTelemetryLogCounts.find("input.value_rotary_input_device_full_rotation_count"),
+ mTelemetryLogCounts.end());
+}
+
+TEST_F_WITH_FLAGS(RotaryEncoderInputMapperTest, NegativeMinLogRotation_NoRotationLogging,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
+ rotary_input_telemetry))) {
+ mPropertyMap.addProperty("device.res", "3");
+ mPropertyMap.addProperty("rotary_encoder.min_rotations_to_log", "-2");
+ mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration,
+ mTelemetryLogCounter);
+ InputDeviceInfo info;
+ mMapper->populateDeviceInfo(info);
+
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 700);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+ ASSERT_EQ(mTelemetryLogCounts.find("input.value_rotary_input_device_full_rotation_count"),
+ mTelemetryLogCounts.end());
+}
+
+TEST_F_WITH_FLAGS(RotaryEncoderInputMapperTest, ZeroMinLogRotation_NoRotationLogging,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
+ rotary_input_telemetry))) {
+ mPropertyMap.addProperty("device.res", "3");
+ mPropertyMap.addProperty("rotary_encoder.min_rotations_to_log", "0");
+ mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration,
+ mTelemetryLogCounter);
+ InputDeviceInfo info;
+ mMapper->populateDeviceInfo(info);
+
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 700);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+ ASSERT_EQ(mTelemetryLogCounts.find("input.value_rotary_input_device_full_rotation_count"),
+ mTelemetryLogCounts.end());
+}
+
+TEST_F_WITH_FLAGS(RotaryEncoderInputMapperTest, NoMinLogRotation_NoRotationLogging,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
+ rotary_input_telemetry))) {
+ // 3 units per radian, 2 * M_PI * 3 = ~18.85 units per rotation.
+ mPropertyMap.addProperty("device.res", "3");
+ mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration,
+ mTelemetryLogCounter);
+ InputDeviceInfo info;
+ mMapper->populateDeviceInfo(info);
+
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 700);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+ ASSERT_EQ(mTelemetryLogCounts.find("input.value_rotary_input_device_full_rotation_count"),
+ mTelemetryLogCounts.end());
+}
+
+TEST_F_WITH_FLAGS(RotaryEncoderInputMapperTest, RotationLogging,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
+ rotary_input_telemetry))) {
+ // 3 units per radian, 2 * M_PI * 3 = ~18.85 units per rotation.
+ // Multiples of `unitsPerRoation`, to easily follow the assertions below.
+ // [18.85, 37.7, 56.55, 75.4, 94.25, 113.1, 131.95, 150.8]
+ mPropertyMap.addProperty("device.res", "3");
+ mPropertyMap.addProperty("rotary_encoder.min_rotations_to_log", "2");
+
+ mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration,
+ mTelemetryLogCounter);
+ InputDeviceInfo info;
+ mMapper->populateDeviceInfo(info);
+
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 15); // total scroll = 15
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_EQ(mTelemetryLogCounts.find("input.value_rotary_input_device_full_rotation_count"),
+ mTelemetryLogCounts.end());
+
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 13); // total scroll = 28
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ // Expect 0 since `min_rotations_to_log` = 2, and total scroll 28 only has 1 rotation.
+ ASSERT_EQ(mTelemetryLogCounts.find("input.value_rotary_input_device_full_rotation_count"),
+ mTelemetryLogCounts.end());
+
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 10); // total scroll = 38
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ // Total scroll includes >= `min_rotations_to_log` (2), expect log.
+ ASSERT_EQ(mTelemetryLogCounts["input.value_rotary_input_device_full_rotation_count"], 2);
+
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, -22); // total scroll = 60
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ // Expect no additional telemetry. Total rotation is 3, and total unlogged rotation is 1, which
+ // is less than `min_rotations_to_log`.
+ ASSERT_EQ(mTelemetryLogCounts["input.value_rotary_input_device_full_rotation_count"], 2);
+
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, -16); // total scroll = 76
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ // Total unlogged rotation >= `min_rotations_to_log` (2), so expect 2 more logged rotation.
+ ASSERT_EQ(mTelemetryLogCounts["input.value_rotary_input_device_full_rotation_count"], 4);
+
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, -76); // total scroll = 152
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ // Total unlogged scroll >= 4*`min_rotations_to_log`. Expect *all* unlogged rotations to be
+ // logged, even if that's more than multiple of `min_rotations_to_log`.
+ ASSERT_EQ(mTelemetryLogCounts["input.value_rotary_input_device_full_rotation_count"], 8);
+}
+
} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/FrontEnd/LayerHandle.cpp b/services/surfaceflinger/FrontEnd/LayerHandle.cpp
index 75e4e3a..ffd51a4 100644
--- a/services/surfaceflinger/FrontEnd/LayerHandle.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerHandle.cpp
@@ -28,7 +28,7 @@
LayerHandle::~LayerHandle() {
if (mFlinger) {
- mFlinger->onHandleDestroyed(this, mLayer, mLayerId);
+ mFlinger->onHandleDestroyed(mLayer, mLayerId);
}
}
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 06560d0..8d16b9f 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -952,7 +952,7 @@
// Do not wait the future to avoid deadlocks
// between main and Perfetto threads (b/313130597)
static_cast<void>(mScheduler->schedule(
- [&, onLayersSnapshot]() FTL_FAKE_GUARD(mStateLock)
+ [&, traceFlags, onLayersSnapshot]() FTL_FAKE_GUARD(mStateLock)
FTL_FAKE_GUARD(kMainThreadContext) {
auto snapshot =
takeLayersSnapshotProto(traceFlags, TimePoint::now(),
@@ -4723,6 +4723,7 @@
for (auto& state : states) {
resolvedStates.emplace_back(std::move(state));
auto& resolvedState = resolvedStates.back();
+ resolvedState.layerId = LayerHandle::getLayerId(resolvedState.state.surface);
if (resolvedState.state.hasBufferChanges() && resolvedState.state.hasValidBuffer() &&
resolvedState.state.surface) {
sp<Layer> layer = LayerHandle::getLayer(resolvedState.state.surface);
@@ -4734,9 +4735,8 @@
if (resolvedState.externalTexture) {
resolvedState.state.bufferData->buffer = resolvedState.externalTexture->getBuffer();
}
- mBufferCountTracker.increment(resolvedState.state.surface->localBinder());
+ mBufferCountTracker.increment(resolvedState.layerId);
}
- resolvedState.layerId = LayerHandle::getLayerId(resolvedState.state.surface);
if (resolvedState.state.what & layer_state_t::eReparent) {
resolvedState.parentId =
getLayerIdFromSurfaceControl(resolvedState.state.parentSurfaceControlForChild);
@@ -5179,7 +5179,7 @@
std::atomic<int32_t>* pendingBufferCounter = layer->getPendingBufferCounter();
if (pendingBufferCounter) {
std::string counterName = layer->getPendingBufferCounterName();
- mBufferCountTracker.add(outResult.handle->localBinder(), counterName,
+ mBufferCountTracker.add(LayerHandle::getLayerId(outResult.handle), counterName,
pendingBufferCounter);
}
} break;
@@ -5227,7 +5227,7 @@
return NO_ERROR;
}
-void SurfaceFlinger::onHandleDestroyed(BBinder* handle, sp<Layer>& layer, uint32_t layerId) {
+void SurfaceFlinger::onHandleDestroyed(sp<Layer>& layer, uint32_t layerId) {
{
// Used to remove stalled transactions which uses an internal lock.
ftl::FakeGuard guard(kMainThreadContext);
@@ -5240,7 +5240,7 @@
Mutex::Autolock stateLock(mStateLock);
layer->onHandleDestroyed();
- mBufferCountTracker.remove(handle);
+ mBufferCountTracker.remove(layerId);
layer.clear();
setTransactionFlags(eTransactionFlushNeeded | eTransactionNeeded);
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 3eb72cc..db0e15e 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -296,7 +296,7 @@
// Called when all clients have released all their references to
// this layer. The layer may still be kept alive by its parents but
// the client can no longer modify this layer directly.
- void onHandleDestroyed(BBinder* handle, sp<Layer>& layer, uint32_t layerId);
+ void onHandleDestroyed(sp<Layer>& layer, uint32_t layerId);
TransactionCallbackInvoker& getTransactionCallbackInvoker() {
return mTransactionCallbackInvoker;
@@ -433,32 +433,32 @@
// This is done to avoid lock contention with the main thread.
class BufferCountTracker {
public:
- void increment(BBinder* layerHandle) {
+ void increment(uint32_t layerId) {
std::lock_guard<std::mutex> lock(mLock);
- auto it = mCounterByLayerHandle.find(layerHandle);
- if (it != mCounterByLayerHandle.end()) {
+ auto it = mCounterByLayerId.find(layerId);
+ if (it != mCounterByLayerId.end()) {
auto [name, pendingBuffers] = it->second;
int32_t count = ++(*pendingBuffers);
SFTRACE_INT(name.c_str(), count);
} else {
- ALOGW("Handle not found! %p", layerHandle);
+ ALOGW("Layer ID not found! %d", layerId);
}
}
- void add(BBinder* layerHandle, const std::string& name, std::atomic<int32_t>* counter) {
+ void add(uint32_t layerId, const std::string& name, std::atomic<int32_t>* counter) {
std::lock_guard<std::mutex> lock(mLock);
- mCounterByLayerHandle[layerHandle] = std::make_pair(name, counter);
+ mCounterByLayerId[layerId] = std::make_pair(name, counter);
}
- void remove(BBinder* layerHandle) {
+ void remove(uint32_t layerId) {
std::lock_guard<std::mutex> lock(mLock);
- mCounterByLayerHandle.erase(layerHandle);
+ mCounterByLayerId.erase(layerId);
}
private:
std::mutex mLock;
- std::unordered_map<BBinder*, std::pair<std::string, std::atomic<int32_t>*>>
- mCounterByLayerHandle GUARDED_BY(mLock);
+ std::unordered_map<uint32_t, std::pair<std::string, std::atomic<int32_t>*>>
+ mCounterByLayerId GUARDED_BY(mLock);
};
enum class BootStage {
diff --git a/services/surfaceflinger/tests/benchmarks/Android.bp b/services/surfaceflinger/tests/benchmarks/Android.bp
index 1c47be34..22fca08 100644
--- a/services/surfaceflinger/tests/benchmarks/Android.bp
+++ b/services/surfaceflinger/tests/benchmarks/Android.bp
@@ -22,7 +22,6 @@
static_libs: [
"libgmock",
"libgtest",
- "libc++fs",
],
header_libs: [
"libsurfaceflinger_mocks_headers",
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index c043b88..4dec5f6 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -637,7 +637,7 @@
void destroyAllLayerHandles() {
ftl::FakeGuard guard(kMainThreadContext);
for (auto [layerId, legacyLayer] : mFlinger->mLegacyLayers) {
- mFlinger->onHandleDestroyed(nullptr, legacyLayer, layerId);
+ mFlinger->onHandleDestroyed(legacyLayer, layerId);
}
}