Merge "Pipe display install orientation 1/2"
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 4374abe..555be1ed7 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -113,8 +113,8 @@
if (!found) {
// Although it is tested, explicitly rebuilding qualified name, in case it
// becomes something unexpected.
- LOG(ERROR) << "Could not find " << aname.package << "." << aname.iface << "/"
- << aname.instance << " in the VINTF manifest.";
+ LOG(INFO) << "Could not find " << aname.package << "." << aname.iface << "/"
+ << aname.instance << " in the VINTF manifest.";
}
return found;
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 931c5e3..1be3a69 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -191,6 +191,12 @@
}
prebuilt_etc {
+ name: "android.hardware.wifi.direct.prebuilt.xml",
+ src: "android.hardware.wifi.direct.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
name: "android.hardware.wifi.passpoint.prebuilt.xml",
src: "android.hardware.wifi.passpoint.xml",
defaults: ["frameworks_native_data_etc_defaults"],
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index f5abb85..7067830 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -93,20 +93,20 @@
// ----------------------------------------------------------------------
-#define DECLARE_META_INTERFACE(INTERFACE) \
-public: \
- static const ::android::String16 descriptor; \
- static ::android::sp<I##INTERFACE> asInterface( \
- const ::android::sp<::android::IBinder>& obj); \
- virtual const ::android::String16& getInterfaceDescriptor() const; \
- I##INTERFACE(); \
- virtual ~I##INTERFACE(); \
- static bool setDefaultImpl(std::unique_ptr<I##INTERFACE> impl); \
- static const std::unique_ptr<I##INTERFACE>& getDefaultImpl(); \
-private: \
- static std::unique_ptr<I##INTERFACE> default_impl; \
-public: \
-
+#define DECLARE_META_INTERFACE(INTERFACE) \
+public: \
+ static const ::android::String16 descriptor; \
+ static ::android::sp<I##INTERFACE> asInterface(const ::android::sp<::android::IBinder>& obj); \
+ virtual const ::android::String16& getInterfaceDescriptor() const; \
+ I##INTERFACE(); \
+ virtual ~I##INTERFACE(); \
+ static bool setDefaultImpl(::android::sp<I##INTERFACE> impl); \
+ static const ::android::sp<I##INTERFACE>& getDefaultImpl(); \
+ \
+private: \
+ static ::android::sp<I##INTERFACE> default_impl; \
+ \
+public:
#define __IINTF_CONCAT(x, y) (x ## y)
@@ -142,8 +142,8 @@
} \
return intr; \
} \
- std::unique_ptr<ITYPE> ITYPE::default_impl; \
- bool ITYPE::setDefaultImpl(std::unique_ptr<ITYPE> impl) { \
+ ::android::sp<ITYPE> ITYPE::default_impl; \
+ bool ITYPE::setDefaultImpl(::android::sp<ITYPE> impl) { \
/* Only one user of this interface can use this function */ \
/* at a time. This is a heuristic to detect if two different */ \
/* users in the same process use this function. */ \
@@ -154,7 +154,7 @@
} \
return false; \
} \
- const std::unique_ptr<ITYPE>& ITYPE::getDefaultImpl() { return ITYPE::default_impl; } \
+ const ::android::sp<ITYPE>& ITYPE::getDefaultImpl() { return ITYPE::default_impl; } \
ITYPE::INAME() {} \
ITYPE::~INAME() {}
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index e4df98a..90cbf9d 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -32,7 +32,7 @@
"com.android.uwb",
"com.android.virt",
],
- min_sdk_version: "current",
+ min_sdk_version: "Tiramisu",
}
rust_library {
@@ -79,7 +79,7 @@
"com.android.uwb",
"com.android.virt",
],
- min_sdk_version: "current",
+ min_sdk_version: "Tiramisu",
lints: "none",
clippy_lints: "none",
}
@@ -137,7 +137,7 @@
"com.android.uwb",
"com.android.virt",
],
- min_sdk_version: "current",
+ min_sdk_version: "Tiramisu",
}
// TODO(b/184872979): remove once the Rust API is created.
@@ -154,7 +154,7 @@
"com.android.uwb",
"com.android.virt",
],
- min_sdk_version: "current",
+ min_sdk_version: "Tiramisu",
}
rust_test {
diff --git a/libs/binder/rust/binder_tokio/lib.rs b/libs/binder/rust/binder_tokio/lib.rs
index 47dcdc2..9dcef42 100644
--- a/libs/binder/rust/binder_tokio/lib.rs
+++ b/libs/binder/rust/binder_tokio/lib.rs
@@ -28,8 +28,8 @@
//!
//! [`Tokio`]: crate::Tokio
-use binder::public_api::{BinderAsyncPool, BoxFuture, Strong};
-use binder::{FromIBinder, StatusCode, BinderAsyncRuntime};
+use binder::{BinderAsyncPool, BoxFuture, FromIBinder, StatusCode, Strong};
+use binder::binder_impl::BinderAsyncRuntime;
use std::future::Future;
/// Retrieve an existing service for a particular interface, sleeping for a few
@@ -37,12 +37,12 @@
pub async fn get_interface<T: FromIBinder + ?Sized + 'static>(name: &str) -> Result<Strong<T>, StatusCode> {
if binder::is_handling_transaction() {
// See comment in the BinderAsyncPool impl.
- return binder::public_api::get_interface::<T>(name);
+ return binder::get_interface::<T>(name);
}
let name = name.to_string();
let res = tokio::task::spawn_blocking(move || {
- binder::public_api::get_interface::<T>(&name)
+ binder::get_interface::<T>(&name)
}).await;
// The `is_panic` branch is not actually reachable in Android as we compile
@@ -61,12 +61,12 @@
pub async fn wait_for_interface<T: FromIBinder + ?Sized + 'static>(name: &str) -> Result<Strong<T>, StatusCode> {
if binder::is_handling_transaction() {
// See comment in the BinderAsyncPool impl.
- return binder::public_api::wait_for_interface::<T>(name);
+ return binder::wait_for_interface::<T>(name);
}
let name = name.to_string();
let res = tokio::task::spawn_blocking(move || {
- binder::public_api::wait_for_interface::<T>(&name)
+ binder::wait_for_interface::<T>(&name)
}).await;
// The `is_panic` branch is not actually reachable in Android as we compile
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 4d6b294..7895a72 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -536,13 +536,13 @@
/// ```
macro_rules! binder_fn_get_class {
($class:ty) => {
- binder_fn_get_class!($crate::InterfaceClass::new::<$class>());
+ binder_fn_get_class!($crate::binder_impl::InterfaceClass::new::<$class>());
};
($constructor:expr) => {
- fn get_class() -> $crate::InterfaceClass {
+ fn get_class() -> $crate::binder_impl::InterfaceClass {
static CLASS_INIT: std::sync::Once = std::sync::Once::new();
- static mut CLASS: Option<$crate::InterfaceClass> = None;
+ static mut CLASS: Option<$crate::binder_impl::InterfaceClass> = None;
CLASS_INIT.call_once(|| unsafe {
// Safety: This assignment is guarded by the `CLASS_INIT` `Once`
@@ -772,7 +772,7 @@
native: $native($on_transact),
proxy: $proxy {},
$(async: $async_interface,)?
- stability: $crate::Stability::default(),
+ stability: $crate::binder_impl::Stability::default(),
}
}
};
@@ -811,7 +811,7 @@
$($fname: $fty = $finit),*
},
$(async: $async_interface,)?
- stability: $crate::Stability::default(),
+ stability: $crate::binder_impl::Stability::default(),
}
}
};
@@ -828,9 +828,9 @@
} => {
$crate::declare_binder_interface! {
$interface[$descriptor] {
- @doc[concat!("A binder [`Remotable`]($crate::Remotable) that holds an [`", stringify!($interface), "`] object.")]
+ @doc[concat!("A binder [`Remotable`]($crate::binder_impl::Remotable) that holds an [`", stringify!($interface), "`] object.")]
native: $native($on_transact),
- @doc[concat!("A binder [`Proxy`]($crate::Proxy) that holds an [`", stringify!($interface), "`] remote interface.")]
+ @doc[concat!("A binder [`Proxy`]($crate::binder_impl::Proxy) that holds an [`", stringify!($interface), "`] remote interface.")]
proxy: $proxy {
$($fname: $fty = $finit),*
},
@@ -867,7 +867,7 @@
}
}
- impl $crate::Proxy for $proxy
+ impl $crate::binder_impl::Proxy for $proxy
where
$proxy: $interface,
{
@@ -875,7 +875,7 @@
$descriptor
}
- fn from_binder(mut binder: $crate::SpIBinder) -> $crate::Result<Self> {
+ fn from_binder(mut binder: $crate::SpIBinder) -> std::result::Result<Self, $crate::StatusCode> {
Ok(Self { binder, $($fname: $finit),* })
}
}
@@ -887,19 +887,19 @@
impl $native {
/// Create a new binder service.
pub fn new_binder<T: $interface + Sync + Send + 'static>(inner: T, features: $crate::BinderFeatures) -> $crate::Strong<dyn $interface> {
- let mut binder = $crate::Binder::new_with_stability($native(Box::new(inner)), $stability);
+ let mut binder = $crate::binder_impl::Binder::new_with_stability($native(Box::new(inner)), $stability);
#[cfg(not(android_vndk))]
- $crate::IBinderInternal::set_requesting_sid(&mut binder, features.set_requesting_sid);
+ $crate::binder_impl::IBinderInternal::set_requesting_sid(&mut binder, features.set_requesting_sid);
$crate::Strong::new(Box::new(binder))
}
}
- impl $crate::Remotable for $native {
+ impl $crate::binder_impl::Remotable for $native {
fn get_descriptor() -> &'static str {
$descriptor
}
- fn on_transact(&self, code: $crate::TransactionCode, data: &$crate::BorrowedParcel<'_>, reply: &mut $crate::BorrowedParcel<'_>) -> $crate::Result<()> {
+ fn on_transact(&self, code: $crate::binder_impl::TransactionCode, data: &$crate::binder_impl::BorrowedParcel<'_>, reply: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
match $on_transact(&*self.0, code, data, reply) {
// The C++ backend converts UNEXPECTED_NULL into an exception
Err($crate::StatusCode::UNEXPECTED_NULL) => {
@@ -913,19 +913,19 @@
}
}
- fn on_dump(&self, file: &std::fs::File, args: &[&std::ffi::CStr]) -> $crate::Result<()> {
+ fn on_dump(&self, file: &std::fs::File, args: &[&std::ffi::CStr]) -> std::result::Result<(), $crate::StatusCode> {
self.0.dump(file, args)
}
- fn get_class() -> $crate::InterfaceClass {
+ fn get_class() -> $crate::binder_impl::InterfaceClass {
static CLASS_INIT: std::sync::Once = std::sync::Once::new();
- static mut CLASS: Option<$crate::InterfaceClass> = None;
+ static mut CLASS: Option<$crate::binder_impl::InterfaceClass> = None;
CLASS_INIT.call_once(|| unsafe {
// Safety: This assignment is guarded by the `CLASS_INIT` `Once`
// variable, and therefore is thread-safe, as it can only occur
// once.
- CLASS = Some($crate::InterfaceClass::new::<$crate::Binder<$native>>());
+ CLASS = Some($crate::binder_impl::InterfaceClass::new::<$crate::binder_impl::Binder<$native>>());
});
unsafe {
// Safety: The `CLASS` variable can only be mutated once, above,
@@ -936,25 +936,25 @@
}
impl $crate::FromIBinder for dyn $interface {
- fn try_from(mut ibinder: $crate::SpIBinder) -> $crate::Result<$crate::Strong<dyn $interface>> {
- use $crate::AssociateClass;
+ fn try_from(mut ibinder: $crate::SpIBinder) -> std::result::Result<$crate::Strong<dyn $interface>, $crate::StatusCode> {
+ use $crate::binder_impl::AssociateClass;
let existing_class = ibinder.get_class();
if let Some(class) = existing_class {
- if class != <$native as $crate::Remotable>::get_class() &&
- class.get_descriptor() == <$native as $crate::Remotable>::get_descriptor()
+ if class != <$native as $crate::binder_impl::Remotable>::get_class() &&
+ class.get_descriptor() == <$native as $crate::binder_impl::Remotable>::get_descriptor()
{
// The binder object's descriptor string matches what we
// expect. We still need to treat this local or already
// associated object as remote, because we can't cast it
// into a Rust service object without a matching class
// pointer.
- return Ok($crate::Strong::new(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?)));
+ return Ok($crate::Strong::new(Box::new(<$proxy as $crate::binder_impl::Proxy>::from_binder(ibinder)?)));
}
}
- if ibinder.associate_class(<$native as $crate::Remotable>::get_class()) {
- let service: $crate::Result<$crate::Binder<$native>> =
+ if ibinder.associate_class(<$native as $crate::binder_impl::Remotable>::get_class()) {
+ let service: std::result::Result<$crate::binder_impl::Binder<$native>, $crate::StatusCode> =
std::convert::TryFrom::try_from(ibinder.clone());
if let Ok(service) = service {
// We were able to associate with our expected class and
@@ -962,7 +962,7 @@
return Ok($crate::Strong::new(Box::new(service)));
} else {
// Service is remote
- return Ok($crate::Strong::new(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?)));
+ return Ok($crate::Strong::new(Box::new(<$proxy as $crate::binder_impl::Proxy>::from_binder(ibinder)?)));
}
}
@@ -970,18 +970,18 @@
}
}
- impl $crate::parcel::Serialize for dyn $interface + '_
+ impl $crate::binder_impl::Serialize for dyn $interface + '_
where
dyn $interface: $crate::Interface
{
- fn serialize(&self, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
+ fn serialize(&self, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
let binder = $crate::Interface::as_binder(self);
parcel.write(&binder)
}
}
- impl $crate::parcel::SerializeOption for dyn $interface + '_ {
- fn serialize_option(this: Option<&Self>, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
+ impl $crate::binder_impl::SerializeOption for dyn $interface + '_ {
+ fn serialize_option(this: Option<&Self>, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
parcel.write(&this.map($crate::Interface::as_binder))
}
}
@@ -1004,25 +1004,25 @@
$(
// Async interface trait implementations.
impl<P: $crate::BinderAsyncPool> $crate::FromIBinder for dyn $async_interface<P> {
- fn try_from(mut ibinder: $crate::SpIBinder) -> $crate::Result<$crate::Strong<dyn $async_interface<P>>> {
- use $crate::AssociateClass;
+ fn try_from(mut ibinder: $crate::SpIBinder) -> std::result::Result<$crate::Strong<dyn $async_interface<P>>, $crate::StatusCode> {
+ use $crate::binder_impl::AssociateClass;
let existing_class = ibinder.get_class();
if let Some(class) = existing_class {
- if class != <$native as $crate::Remotable>::get_class() &&
- class.get_descriptor() == <$native as $crate::Remotable>::get_descriptor()
+ if class != <$native as $crate::binder_impl::Remotable>::get_class() &&
+ class.get_descriptor() == <$native as $crate::binder_impl::Remotable>::get_descriptor()
{
// The binder object's descriptor string matches what we
// expect. We still need to treat this local or already
// associated object as remote, because we can't cast it
// into a Rust service object without a matching class
// pointer.
- return Ok($crate::Strong::new(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?)));
+ return Ok($crate::Strong::new(Box::new(<$proxy as $crate::binder_impl::Proxy>::from_binder(ibinder)?)));
}
}
- if ibinder.associate_class(<$native as $crate::Remotable>::get_class()) {
- let service: $crate::Result<$crate::Binder<$native>> =
+ if ibinder.associate_class(<$native as $crate::binder_impl::Remotable>::get_class()) {
+ let service: std::result::Result<$crate::binder_impl::Binder<$native>, $crate::StatusCode> =
std::convert::TryFrom::try_from(ibinder.clone());
if let Ok(service) = service {
// We were able to associate with our expected class and
@@ -1031,7 +1031,7 @@
//return Ok($crate::Strong::new(Box::new(service)));
} else {
// Service is remote
- return Ok($crate::Strong::new(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?)));
+ return Ok($crate::Strong::new(Box::new(<$proxy as $crate::binder_impl::Proxy>::from_binder(ibinder)?)));
}
}
@@ -1039,15 +1039,15 @@
}
}
- impl<P: $crate::BinderAsyncPool> $crate::parcel::Serialize for dyn $async_interface<P> + '_ {
- fn serialize(&self, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
+ impl<P: $crate::BinderAsyncPool> $crate::binder_impl::Serialize for dyn $async_interface<P> + '_ {
+ fn serialize(&self, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
let binder = $crate::Interface::as_binder(self);
parcel.write(&binder)
}
}
- impl<P: $crate::BinderAsyncPool> $crate::parcel::SerializeOption for dyn $async_interface<P> + '_ {
- fn serialize_option(this: Option<&Self>, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
+ impl<P: $crate::BinderAsyncPool> $crate::binder_impl::SerializeOption for dyn $async_interface<P> + '_ {
+ fn serialize_option(this: Option<&Self>, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
parcel.write(&this.map($crate::Interface::as_binder))
}
}
@@ -1067,11 +1067,11 @@
}
}
- impl<P: $crate::BinderAsyncPool> $crate::ToAsyncInterface<P> for dyn $interface {
+ impl<P: $crate::BinderAsyncPool> $crate::binder_impl::ToAsyncInterface<P> for dyn $interface {
type Target = dyn $async_interface<P>;
}
- impl<P: $crate::BinderAsyncPool> $crate::ToSyncInterface for dyn $async_interface<P> {
+ impl<P: $crate::BinderAsyncPool> $crate::binder_impl::ToSyncInterface for dyn $async_interface<P> {
type Target = dyn $interface;
}
)?
@@ -1103,29 +1103,29 @@
}
}
- impl $crate::parcel::Serialize for $enum {
- fn serialize(&self, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
+ impl $crate::binder_impl::Serialize for $enum {
+ fn serialize(&self, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
parcel.write(&self.0)
}
}
- impl $crate::parcel::SerializeArray for $enum {
- fn serialize_array(slice: &[Self], parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
+ impl $crate::binder_impl::SerializeArray for $enum {
+ fn serialize_array(slice: &[Self], parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
let v: Vec<$backing> = slice.iter().map(|x| x.0).collect();
- <$backing as binder::parcel::SerializeArray>::serialize_array(&v[..], parcel)
+ <$backing as $crate::binder_impl::SerializeArray>::serialize_array(&v[..], parcel)
}
}
- impl $crate::parcel::Deserialize for $enum {
- fn deserialize(parcel: &$crate::parcel::BorrowedParcel<'_>) -> $crate::Result<Self> {
+ impl $crate::binder_impl::Deserialize for $enum {
+ fn deserialize(parcel: &$crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<Self, $crate::StatusCode> {
parcel.read().map(Self)
}
}
- impl $crate::parcel::DeserializeArray for $enum {
- fn deserialize_array(parcel: &$crate::parcel::BorrowedParcel<'_>) -> $crate::Result<Option<Vec<Self>>> {
+ impl $crate::binder_impl::DeserializeArray for $enum {
+ fn deserialize_array(parcel: &$crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<Option<Vec<Self>>, $crate::StatusCode> {
let v: Option<Vec<$backing>> =
- <$backing as binder::parcel::DeserializeArray>::deserialize_array(parcel)?;
+ <$backing as $crate::binder_impl::DeserializeArray>::deserialize_array(parcel)?;
Ok(v.map(|v| v.into_iter().map(Self).collect()))
}
}
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 20d90f7..1d7de98 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -101,45 +101,50 @@
mod binder_async;
mod error;
mod native;
+mod parcel;
mod state;
use binder_ndk_sys as sys;
-pub mod parcel;
-
-pub use crate::binder::{
- BinderFeatures, FromIBinder, IBinder, IBinderInternal, Interface, InterfaceClass, Remotable,
- Stability, Strong, ToAsyncInterface, ToSyncInterface, TransactionCode, TransactionFlags, Weak,
- FIRST_CALL_TRANSACTION, FLAG_CLEAR_BUF, FLAG_ONEWAY, FLAG_PRIVATE_LOCAL, LAST_CALL_TRANSACTION,
+pub use binder::{BinderFeatures, FromIBinder, IBinder, Interface, Strong, Weak};
+pub use crate::binder_async::{BinderAsyncPool, BoxFuture};
+pub use error::{ExceptionCode, Status, StatusCode};
+pub use native::{
+ add_service, force_lazy_services_persist, is_handling_transaction, register_lazy_service,
};
-pub use crate::binder_async::{BoxFuture, BinderAsyncPool, BinderAsyncRuntime};
-pub use error::{status_t, ExceptionCode, Result, Status, StatusCode};
-pub use native::{add_service, force_lazy_services_persist, is_handling_transaction, register_lazy_service, Binder};
-pub use parcel::{BorrowedParcel, Parcel};
-pub use proxy::{get_interface, get_service, wait_for_interface, wait_for_service};
-pub use proxy::{AssociateClass, DeathRecipient, Proxy, SpIBinder, WpIBinder};
+pub use parcel::{ParcelFileDescriptor, Parcelable, ParcelableHolder};
+pub use proxy::{
+ get_interface, get_service, wait_for_interface, wait_for_service, DeathRecipient, SpIBinder,
+ WpIBinder,
+};
pub use state::{ProcessState, ThreadState};
+/// Binder result containing a [`Status`] on error.
+pub type Result<T> = std::result::Result<T, Status>;
+
+/// Advanced Binder APIs needed internally by AIDL or when manually using Binder
+/// without AIDL.
+pub mod binder_impl {
+ pub use crate::binder::{
+ IBinderInternal, InterfaceClass, Remotable, Stability, ToAsyncInterface, ToSyncInterface,
+ TransactionCode, TransactionFlags, FIRST_CALL_TRANSACTION, FLAG_CLEAR_BUF, FLAG_ONEWAY,
+ FLAG_PRIVATE_LOCAL, LAST_CALL_TRANSACTION,
+ };
+ pub use crate::binder_async::BinderAsyncRuntime;
+ pub use crate::error::status_t;
+ pub use crate::native::Binder;
+ pub use crate::parcel::{
+ BorrowedParcel, Deserialize, DeserializeArray, DeserializeOption, Parcel,
+ ParcelableMetadata, Serialize, SerializeArray, SerializeOption, NON_NULL_PARCELABLE_FLAG,
+ NULL_PARCELABLE_FLAG,
+ };
+ pub use crate::proxy::{AssociateClass, Proxy};
+}
+
/// Unstable, in-development API that only allowlisted clients are allowed to use.
+#[doc(hidden)]
pub mod unstable_api {
pub use crate::binder::AsNative;
pub use crate::proxy::unstable_api::new_spibinder;
pub use crate::sys::AIBinder;
}
-
-/// The public API usable outside AIDL-generated interface crates.
-pub mod public_api {
- pub use super::parcel::{ParcelFileDescriptor, ParcelableHolder};
- pub use super::{
- add_service, force_lazy_services_persist, get_interface, register_lazy_service,
- wait_for_interface,
- };
- pub use super::{
- BinderAsyncPool, BinderFeatures, BoxFuture, DeathRecipient, ExceptionCode, IBinder,
- Interface, ProcessState, SpIBinder, Status, StatusCode, Strong, ThreadState, Weak,
- WpIBinder,
- };
-
- /// Binder result containing a [`Status`] on error.
- pub type Result<T> = std::result::Result<T, Status>;
-}
diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs
index 206b90c..256fa8b 100644
--- a/libs/binder/rust/src/parcel.rs
+++ b/libs/binder/rust/src/parcel.rs
@@ -496,7 +496,7 @@
{
let start = self.get_data_position();
let parcelable_size: i32 = self.read()?;
- if parcelable_size < 0 {
+ if parcelable_size < 4 {
return Err(StatusCode::BAD_VALUE);
}
diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs
index 61f88b6..0c7e48d 100644
--- a/libs/binder/rust/src/parcel/parcelable.rs
+++ b/libs/binder/rust/src/parcel/parcelable.rs
@@ -802,35 +802,32 @@
#[macro_export]
macro_rules! impl_serialize_for_parcelable {
($parcelable:ident) => {
- impl $crate::parcel::Serialize for $parcelable {
+ impl $crate::binder_impl::Serialize for $parcelable {
fn serialize(
&self,
- parcel: &mut $crate::parcel::BorrowedParcel<'_>,
- ) -> $crate::Result<()> {
- <Self as $crate::parcel::SerializeOption>::serialize_option(
- Some(self),
- parcel,
- )
+ parcel: &mut $crate::binder_impl::BorrowedParcel<'_>,
+ ) -> std::result::Result<(), $crate::StatusCode> {
+ <Self as $crate::binder_impl::SerializeOption>::serialize_option(Some(self), parcel)
}
}
- impl $crate::parcel::SerializeArray for $parcelable {}
+ impl $crate::binder_impl::SerializeArray for $parcelable {}
- impl $crate::parcel::SerializeOption for $parcelable {
+ impl $crate::binder_impl::SerializeOption for $parcelable {
fn serialize_option(
this: Option<&Self>,
- parcel: &mut $crate::parcel::BorrowedParcel<'_>,
- ) -> $crate::Result<()> {
+ parcel: &mut $crate::binder_impl::BorrowedParcel<'_>,
+ ) -> std::result::Result<(), $crate::StatusCode> {
if let Some(this) = this {
- use $crate::parcel::Parcelable;
- parcel.write(&$crate::parcel::NON_NULL_PARCELABLE_FLAG)?;
+ use $crate::Parcelable;
+ parcel.write(&$crate::binder_impl::NON_NULL_PARCELABLE_FLAG)?;
this.write_to_parcel(parcel)
} else {
- parcel.write(&$crate::parcel::NULL_PARCELABLE_FLAG)
+ parcel.write(&$crate::binder_impl::NULL_PARCELABLE_FLAG)
}
}
}
- }
+ };
}
/// Implement `Deserialize` trait and friends for a parcelable
@@ -842,54 +839,54 @@
#[macro_export]
macro_rules! impl_deserialize_for_parcelable {
($parcelable:ident) => {
- impl $crate::parcel::Deserialize for $parcelable {
+ impl $crate::binder_impl::Deserialize for $parcelable {
fn deserialize(
- parcel: &$crate::parcel::BorrowedParcel<'_>,
- ) -> $crate::Result<Self> {
- $crate::parcel::DeserializeOption::deserialize_option(parcel)
+ parcel: &$crate::binder_impl::BorrowedParcel<'_>,
+ ) -> std::result::Result<Self, $crate::StatusCode> {
+ $crate::binder_impl::DeserializeOption::deserialize_option(parcel)
.transpose()
.unwrap_or(Err($crate::StatusCode::UNEXPECTED_NULL))
}
fn deserialize_from(
&mut self,
- parcel: &$crate::parcel::BorrowedParcel<'_>,
- ) -> $crate::Result<()> {
+ parcel: &$crate::binder_impl::BorrowedParcel<'_>,
+ ) -> std::result::Result<(), $crate::StatusCode> {
let status: i32 = parcel.read()?;
- if status == $crate::parcel::NULL_PARCELABLE_FLAG {
+ if status == $crate::binder_impl::NULL_PARCELABLE_FLAG {
Err($crate::StatusCode::UNEXPECTED_NULL)
} else {
- use $crate::parcel::Parcelable;
+ use $crate::Parcelable;
self.read_from_parcel(parcel)
}
}
}
- impl $crate::parcel::DeserializeArray for $parcelable {}
+ impl $crate::binder_impl::DeserializeArray for $parcelable {}
- impl $crate::parcel::DeserializeOption for $parcelable {
+ impl $crate::binder_impl::DeserializeOption for $parcelable {
fn deserialize_option(
- parcel: &$crate::parcel::BorrowedParcel<'_>,
- ) -> $crate::Result<Option<Self>> {
+ parcel: &$crate::binder_impl::BorrowedParcel<'_>,
+ ) -> std::result::Result<Option<Self>, $crate::StatusCode> {
let mut result = None;
Self::deserialize_option_from(&mut result, parcel)?;
Ok(result)
}
fn deserialize_option_from(
this: &mut Option<Self>,
- parcel: &$crate::parcel::BorrowedParcel<'_>,
- ) -> $crate::Result<()> {
+ parcel: &$crate::binder_impl::BorrowedParcel<'_>,
+ ) -> std::result::Result<(), $crate::StatusCode> {
let status: i32 = parcel.read()?;
- if status == $crate::parcel::NULL_PARCELABLE_FLAG {
+ if status == $crate::binder_impl::NULL_PARCELABLE_FLAG {
*this = None;
Ok(())
} else {
- use $crate::parcel::Parcelable;
+ use $crate::Parcelable;
this.get_or_insert_with(Self::default)
.read_from_parcel(parcel)
}
}
}
- }
+ };
}
impl<T: Serialize> Serialize for Box<T> {
@@ -918,7 +915,7 @@
#[cfg(test)]
mod tests {
- use crate::Parcel;
+ use crate::parcel::Parcel;
use super::*;
#[test]
diff --git a/libs/binder/rust/src/parcel/parcelable_holder.rs b/libs/binder/rust/src/parcel/parcelable_holder.rs
index b4282b2..bc70ea6 100644
--- a/libs/binder/rust/src/parcel/parcelable_holder.rs
+++ b/libs/binder/rust/src/parcel/parcelable_holder.rs
@@ -15,8 +15,8 @@
*/
use crate::binder::Stability;
-use crate::error::{Result, StatusCode};
-use crate::parcel::{Parcel, BorrowedParcel, Parcelable};
+use crate::error::StatusCode;
+use crate::parcel::{BorrowedParcel, Parcel, Parcelable};
use crate::{impl_deserialize_for_parcelable, impl_serialize_for_parcelable};
use downcast_rs::{impl_downcast, DowncastSync};
@@ -97,7 +97,7 @@
}
/// Set the parcelable contained in this `ParcelableHolder`.
- pub fn set_parcelable<T>(&mut self, p: Arc<T>) -> Result<()>
+ pub fn set_parcelable<T>(&mut self, p: Arc<T>) -> Result<(), StatusCode>
where
T: Any + Parcelable + ParcelableMetadata + std::fmt::Debug + Send + Sync,
{
@@ -126,7 +126,7 @@
/// * `Ok(None)` if the holder is empty or the descriptor does not match
/// * `Ok(Some(_))` if the object holds a parcelable of type `T`
/// with the correct descriptor
- pub fn get_parcelable<T>(&self) -> Result<Option<Arc<T>>>
+ pub fn get_parcelable<T>(&self) -> Result<Option<Arc<T>>, StatusCode>
where
T: Any + Parcelable + ParcelableMetadata + Default + std::fmt::Debug + Send + Sync,
{
@@ -180,7 +180,7 @@
impl_deserialize_for_parcelable!(ParcelableHolder);
impl Parcelable for ParcelableHolder {
- fn write_to_parcel(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
+ fn write_to_parcel(&self, parcel: &mut BorrowedParcel<'_>) -> Result<(), StatusCode> {
parcel.write(&self.stability)?;
let mut data = self.data.lock().unwrap();
@@ -219,7 +219,7 @@
}
}
- fn read_from_parcel(&mut self, parcel: &BorrowedParcel<'_>) -> Result<()> {
+ fn read_from_parcel(&mut self, parcel: &BorrowedParcel<'_>) -> Result<(), StatusCode> {
self.stability = parcel.read()?;
let data_size: i32 = parcel.read()?;
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index 80dc476..50daf1c 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -17,11 +17,13 @@
//! Rust Binder crate integration tests
use binder::{declare_binder_enum, declare_binder_interface};
-use binder::parcel::BorrowedParcel;
-use binder::{
- Binder, BinderFeatures, IBinderInternal, Interface, StatusCode, ThreadState, TransactionCode,
- FIRST_CALL_TRANSACTION,
+use binder::{BinderFeatures, Interface, StatusCode, ThreadState};
+// Import from internal API for testing only, do not use this module in
+// production.
+use binder::binder_impl::{
+ Binder, BorrowedParcel, IBinderInternal, TransactionCode, FIRST_CALL_TRANSACTION,
};
+
use std::convert::{TryFrom, TryInto};
use std::ffi::CStr;
use std::fs::File;
@@ -120,7 +122,7 @@
}
impl Interface for TestService {
- fn dump(&self, _file: &File, args: &[&CStr]) -> binder::Result<()> {
+ fn dump(&self, _file: &File, args: &[&CStr]) -> Result<(), StatusCode> {
let mut dump_args = self.dump_args.lock().unwrap();
dump_args.extend(args.iter().map(|s| s.to_str().unwrap().to_owned()));
Ok(())
@@ -128,22 +130,22 @@
}
impl ITest for TestService {
- fn test(&self) -> binder::Result<String> {
+ fn test(&self) -> Result<String, StatusCode> {
Ok(self.s.clone())
}
- fn get_dump_args(&self) -> binder::Result<Vec<String>> {
+ fn get_dump_args(&self) -> Result<Vec<String>, StatusCode> {
let args = self.dump_args.lock().unwrap().clone();
Ok(args)
}
- fn get_selinux_context(&self) -> binder::Result<String> {
+ fn get_selinux_context(&self) -> Result<String, StatusCode> {
let sid =
ThreadState::with_calling_sid(|sid| sid.map(|s| s.to_string_lossy().into_owned()));
sid.ok_or(StatusCode::UNEXPECTED_NULL)
}
- fn get_is_handling_transaction(&self) -> binder::Result<bool> {
+ fn get_is_handling_transaction(&self) -> Result<bool, StatusCode> {
Ok(binder::is_handling_transaction())
}
}
@@ -151,31 +153,31 @@
/// Trivial testing binder interface
pub trait ITest: Interface {
/// Returns a test string
- fn test(&self) -> binder::Result<String>;
+ fn test(&self) -> Result<String, StatusCode>;
/// Return the arguments sent via dump
- fn get_dump_args(&self) -> binder::Result<Vec<String>>;
+ fn get_dump_args(&self) -> Result<Vec<String>, StatusCode>;
/// Returns the caller's SELinux context
- fn get_selinux_context(&self) -> binder::Result<String>;
+ fn get_selinux_context(&self) -> Result<String, StatusCode>;
/// Returns the value of calling `is_handling_transaction`.
- fn get_is_handling_transaction(&self) -> binder::Result<bool>;
+ fn get_is_handling_transaction(&self) -> Result<bool, StatusCode>;
}
/// Async trivial testing binder interface
pub trait IATest<P>: Interface {
/// Returns a test string
- fn test(&self) -> binder::BoxFuture<'static, binder::Result<String>>;
+ fn test(&self) -> binder::BoxFuture<'static, Result<String, StatusCode>>;
/// Return the arguments sent via dump
- fn get_dump_args(&self) -> binder::BoxFuture<'static, binder::Result<Vec<String>>>;
+ fn get_dump_args(&self) -> binder::BoxFuture<'static, Result<Vec<String>, StatusCode>>;
/// Returns the caller's SELinux context
- fn get_selinux_context(&self) -> binder::BoxFuture<'static, binder::Result<String>>;
+ fn get_selinux_context(&self) -> binder::BoxFuture<'static, Result<String, StatusCode>>;
/// Returns the value of calling `is_handling_transaction`.
- fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, binder::Result<bool>>;
+ fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, Result<bool, StatusCode>>;
}
declare_binder_interface! {
@@ -193,7 +195,7 @@
code: TransactionCode,
_data: &BorrowedParcel<'_>,
reply: &mut BorrowedParcel<'_>,
-) -> binder::Result<()> {
+) -> Result<(), StatusCode> {
match code.try_into()? {
TestTransactionCode::Test => reply.write(&service.test()?),
TestTransactionCode::GetDumpArgs => reply.write(&service.get_dump_args()?),
@@ -203,21 +205,21 @@
}
impl ITest for BpTest {
- fn test(&self) -> binder::Result<String> {
+ fn test(&self) -> Result<String, StatusCode> {
let reply =
self.binder
.transact(TestTransactionCode::Test as TransactionCode, 0, |_| Ok(()))?;
reply.read()
}
- fn get_dump_args(&self) -> binder::Result<Vec<String>> {
+ fn get_dump_args(&self) -> Result<Vec<String>, StatusCode> {
let reply =
self.binder
.transact(TestTransactionCode::GetDumpArgs as TransactionCode, 0, |_| Ok(()))?;
reply.read()
}
- fn get_selinux_context(&self) -> binder::Result<String> {
+ fn get_selinux_context(&self) -> Result<String, StatusCode> {
let reply = self.binder.transact(
TestTransactionCode::GetSelinuxContext as TransactionCode,
0,
@@ -226,7 +228,7 @@
reply.read()
}
- fn get_is_handling_transaction(&self) -> binder::Result<bool> {
+ fn get_is_handling_transaction(&self) -> Result<bool, StatusCode> {
let reply = self.binder.transact(
TestTransactionCode::GetIsHandlingTransaction as TransactionCode,
0,
@@ -237,7 +239,7 @@
}
impl<P: binder::BinderAsyncPool> IATest<P> for BpTest {
- fn test(&self) -> binder::BoxFuture<'static, binder::Result<String>> {
+ fn test(&self) -> binder::BoxFuture<'static, Result<String, StatusCode>> {
let binder = self.binder.clone();
P::spawn(
move || binder.transact(TestTransactionCode::Test as TransactionCode, 0, |_| Ok(())),
@@ -245,7 +247,7 @@
)
}
- fn get_dump_args(&self) -> binder::BoxFuture<'static, binder::Result<Vec<String>>> {
+ fn get_dump_args(&self) -> binder::BoxFuture<'static, Result<Vec<String>, StatusCode>> {
let binder = self.binder.clone();
P::spawn(
move || binder.transact(TestTransactionCode::GetDumpArgs as TransactionCode, 0, |_| Ok(())),
@@ -253,7 +255,7 @@
)
}
- fn get_selinux_context(&self) -> binder::BoxFuture<'static, binder::Result<String>> {
+ fn get_selinux_context(&self) -> binder::BoxFuture<'static, Result<String, StatusCode>> {
let binder = self.binder.clone();
P::spawn(
move || binder.transact(TestTransactionCode::GetSelinuxContext as TransactionCode, 0, |_| Ok(())),
@@ -261,7 +263,7 @@
)
}
- fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, binder::Result<bool>> {
+ fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, Result<bool, StatusCode>> {
let binder = self.binder.clone();
P::spawn(
move || binder.transact(TestTransactionCode::GetIsHandlingTransaction as TransactionCode, 0, |_| Ok(())),
@@ -271,40 +273,40 @@
}
impl ITest for Binder<BnTest> {
- fn test(&self) -> binder::Result<String> {
+ fn test(&self) -> Result<String, StatusCode> {
self.0.test()
}
- fn get_dump_args(&self) -> binder::Result<Vec<String>> {
+ fn get_dump_args(&self) -> Result<Vec<String>, StatusCode> {
self.0.get_dump_args()
}
- fn get_selinux_context(&self) -> binder::Result<String> {
+ fn get_selinux_context(&self) -> Result<String, StatusCode> {
self.0.get_selinux_context()
}
- fn get_is_handling_transaction(&self) -> binder::Result<bool> {
+ fn get_is_handling_transaction(&self) -> Result<bool, StatusCode> {
self.0.get_is_handling_transaction()
}
}
impl<P: binder::BinderAsyncPool> IATest<P> for Binder<BnTest> {
- fn test(&self) -> binder::BoxFuture<'static, binder::Result<String>> {
+ fn test(&self) -> binder::BoxFuture<'static, Result<String, StatusCode>> {
let res = self.0.test();
Box::pin(async move { res })
}
- fn get_dump_args(&self) -> binder::BoxFuture<'static, binder::Result<Vec<String>>> {
+ fn get_dump_args(&self) -> binder::BoxFuture<'static, Result<Vec<String>, StatusCode>> {
let res = self.0.get_dump_args();
Box::pin(async move { res })
}
- fn get_selinux_context(&self) -> binder::BoxFuture<'static, binder::Result<String>> {
+ fn get_selinux_context(&self) -> binder::BoxFuture<'static, Result<String, StatusCode>> {
let res = self.0.get_selinux_context();
Box::pin(async move { res })
}
- fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, binder::Result<bool>> {
+ fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, Result<bool, StatusCode>> {
let res = self.0.get_is_handling_transaction();
Box::pin(async move { res })
}
@@ -325,7 +327,7 @@
_code: TransactionCode,
_data: &BorrowedParcel<'_>,
_reply: &mut BorrowedParcel<'_>,
-) -> binder::Result<()> {
+) -> Result<(), StatusCode> {
Ok(())
}
@@ -363,9 +365,12 @@
use std::time::Duration;
use binder::{
- Binder, BinderFeatures, DeathRecipient, FromIBinder, IBinder, IBinderInternal, Interface,
- SpIBinder, StatusCode, Strong,
+ 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.
+ use binder::binder_impl::{Binder, IBinderInternal, TransactionCode};
use binder_tokio::Tokio;
@@ -743,8 +748,7 @@
let _process = ScopedServiceProcess::new(service_name);
let test_client: Strong<dyn ITest> =
- binder::get_interface(service_name)
- .expect("Did not get test binder service");
+ binder::get_interface(service_name).expect("Did not get test binder service");
let mut remote = test_client.as_binder();
assert!(remote.is_binder_alive());
remote.ping_binder().expect("Could not ping remote service");
@@ -925,7 +929,7 @@
let service2 = service2.as_binder();
let parcel = service1.prepare_transact().unwrap();
- let res = service2.submit_transact(super::TestTransactionCode::Test as binder::TransactionCode, parcel, 0);
+ let res = service2.submit_transact(super::TestTransactionCode::Test as TransactionCode, parcel, 0);
match res {
Ok(_) => panic!("submit_transact should fail"),
diff --git a/libs/binder/rust/tests/serialization.rs b/libs/binder/rust/tests/serialization.rs
index 1fc761e..b62da7b 100644
--- a/libs/binder/rust/tests/serialization.rs
+++ b/libs/binder/rust/tests/serialization.rs
@@ -18,11 +18,12 @@
//! access.
use binder::declare_binder_interface;
-use binder::parcel::ParcelFileDescriptor;
use binder::{
- Binder, BinderFeatures, BorrowedParcel, ExceptionCode, Interface, Result, SpIBinder, Status,
- StatusCode, TransactionCode,
+ BinderFeatures, ExceptionCode, Interface, ParcelFileDescriptor, SpIBinder, Status, StatusCode,
};
+// Import from impl API for testing only, should not be necessary as long as you
+// are using AIDL.
+use binder::binder_impl::{BorrowedParcel, Binder, TransactionCode};
use std::ffi::{c_void, CStr, CString};
use std::sync::Once;
@@ -113,7 +114,7 @@
code: TransactionCode,
parcel: &BorrowedParcel<'_>,
reply: &mut BorrowedParcel<'_>,
-) -> Result<()> {
+) -> Result<(), StatusCode> {
match code {
bindings::Transaction_TEST_BOOL => {
assert_eq!(parcel.read::<bool>()?, true);
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 0227043..b7594df 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -1346,6 +1346,21 @@
SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(windowInfosListener));
return remote()->transact(BnSurfaceComposer::REMOVE_WINDOW_INFOS_LISTENER, data, &reply);
}
+
+ status_t setOverrideFrameRate(uid_t uid, float frameRate) override {
+ Parcel data, reply;
+ SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+ SAFE_PARCEL(data.writeUint32, uid);
+ SAFE_PARCEL(data.writeFloat, frameRate);
+
+ status_t err = remote()->transact(BnSurfaceComposer::SET_OVERRIDE_FRAME_RATE, data, &reply);
+ if (err != NO_ERROR) {
+ ALOGE("setOverrideFrameRate: failed to transact %s (%d)", strerror(-err), err);
+ return err;
+ }
+
+ return NO_ERROR;
+ }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -2306,6 +2321,17 @@
return removeWindowInfosListener(listener);
}
+ case SET_OVERRIDE_FRAME_RATE: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+
+ uid_t uid;
+ SAFE_PARCEL(data.readUint32, &uid);
+
+ float frameRate;
+ SAFE_PARCEL(data.readFloat, &frameRate);
+
+ return setOverrideFrameRate(uid, frameRate);
+ }
default: {
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index b4f6cd5..31456cd 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -2094,6 +2094,10 @@
displayModeId);
}
+status_t SurfaceComposerClient::setOverrideFrameRate(uid_t uid, float frameRate) {
+ return ComposerService::getComposerService()->setOverrideFrameRate(uid, frameRate);
+}
+
void SurfaceComposerClient::setAutoLowLatencyMode(const sp<IBinder>& display, bool on) {
ComposerService::getComposerService()->setAutoLowLatencyMode(display, on);
}
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index f37580c..fb4fb7e 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -559,6 +559,13 @@
int8_t compatibility, int8_t changeFrameRateStrategy) = 0;
/*
+ * Set the override frame rate for a specified uid by GameManagerService.
+ * Passing the frame rate and uid to SurfaceFlinger to update the override mapping
+ * in the scheduler.
+ */
+ virtual status_t setOverrideFrameRate(uid_t uid, float frameRate) = 0;
+
+ /*
* Sets the frame timeline vsync info received from choreographer that corresponds to next
* buffer submitted on that surface.
*/
@@ -678,6 +685,7 @@
SET_BOOT_DISPLAY_MODE,
CLEAR_BOOT_DISPLAY_MODE,
GET_PREFERRED_BOOT_DISPLAY_MODE,
+ SET_OVERRIDE_FRAME_RATE,
// Always append new enum to the end.
};
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index c192323..4f92878 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -25,6 +25,7 @@
#include <binder/IBinder.h>
+#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <utils/Singleton.h>
#include <utils/SortedVector.h>
@@ -175,6 +176,9 @@
static status_t clearBootDisplayMode(const sp<IBinder>& display);
// Gets the display mode in which the device boots if there is no user-preferred display mode
static status_t getPreferredBootDisplayMode(const sp<IBinder>& display, ui::DisplayModeId*);
+ // Sets the frame rate of a particular app (uid). This is currently called
+ // by GameManager.
+ static status_t setOverrideFrameRate(uid_t uid, float frameRate);
// Switches on/off Auto Low Latency Mode on the connected display. This should only be
// called if the connected display supports Auto Low Latency Mode as reported by
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 999874b..0ebd11c 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -31,9 +31,11 @@
#include <gui/SyncScreenCaptureListener.h>
#include <inttypes.h>
#include <private/gui/ComposerService.h>
+#include <sys/types.h>
#include <ui/BufferQueueDefs.h>
#include <ui/DisplayMode.h>
#include <ui/Rect.h>
+#include <utils/Errors.h>
#include <utils/String8.h>
#include <limits>
@@ -925,6 +927,8 @@
return NO_ERROR;
}
+ status_t setOverrideFrameRate(uid_t /*uid*/, float /*frameRate*/) override { return NO_ERROR; }
+
protected:
IBinder* onAsBinder() override { return nullptr; }
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index 702e8b0..171cbaa 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -313,6 +313,7 @@
PrintTo(settings.shadow, os);
*os << "\n .stretchEffect = ";
PrintTo(settings.stretchEffect, os);
+ *os << "\n .whitePointNits = " << settings.whitePointNits;
*os << "\n}";
}
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 006c478..f5a22ec 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -29,6 +29,11 @@
],
}
+cc_library_headers {
+ name: "libui_fuzzableDataspaces_headers",
+ export_include_dirs: ["include/ui/fuzzer/"],
+}
+
cc_defaults {
name: "libui-defaults",
clang: true,
@@ -123,6 +128,7 @@
srcs: [
"DebugUtils.cpp",
"DeviceProductInfo.cpp",
+ "DisplayIdentification.cpp",
"DisplayMode.cpp",
"DynamicDisplayInfo.cpp",
"Fence.cpp",
@@ -160,6 +166,7 @@
"android.hardware.graphics.allocator@2.0",
"android.hardware.graphics.allocator@3.0",
"android.hardware.graphics.allocator@4.0",
+ "android.hardware.graphics.allocator-V1-ndk",
"android.hardware.graphics.common-V3-ndk",
"android.hardware.graphics.common@1.2",
"android.hardware.graphics.mapper@2.0",
@@ -167,6 +174,7 @@
"android.hardware.graphics.mapper@3.0",
"android.hardware.graphics.mapper@4.0",
"libbase",
+ "libbinder_ndk",
"libcutils",
"libgralloctypes",
"libhidlbase",
@@ -183,6 +191,7 @@
],
static_libs: [
+ "libaidlcommonsupport",
"libarect",
"libgrallocusage",
"libmath",
diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp b/libs/ui/DisplayIdentification.cpp
similarity index 86%
rename from services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
rename to libs/ui/DisplayIdentification.cpp
index 83c2b2e..16ed82a 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
+++ b/libs/ui/DisplayIdentification.cpp
@@ -24,12 +24,63 @@
#include <log/log.h>
-#include "DisplayIdentification.h"
-#include "Hash.h"
+#include <ui/DisplayIdentification.h>
namespace android {
namespace {
+template <class T>
+inline T load(const void* p) {
+ static_assert(std::is_integral<T>::value, "T must be integral");
+
+ T r;
+ std::memcpy(&r, p, sizeof(r));
+ return r;
+}
+
+uint64_t rotateByAtLeast1(uint64_t val, uint8_t shift) {
+ return (val >> shift) | (val << (64 - shift));
+}
+
+uint64_t shiftMix(uint64_t val) {
+ return val ^ (val >> 47);
+}
+
+uint64_t hash64Len16(uint64_t u, uint64_t v) {
+ constexpr uint64_t kMul = 0x9ddfea08eb382d69;
+ uint64_t a = (u ^ v) * kMul;
+ a ^= (a >> 47);
+ uint64_t b = (v ^ a) * kMul;
+ b ^= (b >> 47);
+ b *= kMul;
+ return b;
+}
+
+uint64_t hash64Len0To16(const char* s, uint64_t len) {
+ constexpr uint64_t k2 = 0x9ae16a3b2f90404f;
+ constexpr uint64_t k3 = 0xc949d7c7509e6557;
+
+ if (len > 8) {
+ const uint64_t a = load<uint64_t>(s);
+ const uint64_t b = load<uint64_t>(s + len - 8);
+ return hash64Len16(a, rotateByAtLeast1(b + len, static_cast<uint8_t>(len))) ^ b;
+ }
+ if (len >= 4) {
+ const uint32_t a = load<uint32_t>(s);
+ const uint32_t b = load<uint32_t>(s + len - 4);
+ return hash64Len16(len + (a << 3), b);
+ }
+ if (len > 0) {
+ const unsigned char a = static_cast<unsigned char>(s[0]);
+ const unsigned char b = static_cast<unsigned char>(s[len >> 1]);
+ const unsigned char c = static_cast<unsigned char>(s[len - 1]);
+ const uint32_t y = static_cast<uint32_t>(a) + (static_cast<uint32_t>(b) << 8);
+ const uint32_t z = static_cast<uint32_t>(len) + (static_cast<uint32_t>(c) << 2);
+ return shiftMix(y * k2 ^ z * k3) * k2;
+ }
+ return k2;
+}
+
using byte_view = std::basic_string_view<uint8_t>;
constexpr size_t kEdidBlockSize = 128;
@@ -339,5 +390,13 @@
return PhysicalDisplayId::fromEdid(0, kVirtualEdidManufacturerId, id);
}
-} // namespace android
+uint64_t cityHash64Len0To16(std::string_view sv) {
+ auto len = sv.length();
+ if (len > 16) {
+ ALOGE("%s called with length %zu. Only hashing the first 16 chars", __FUNCTION__, len);
+ len = 16;
+ }
+ return hash64Len0To16(sv.data(), len);
+}
+} // namespace android
\ No newline at end of file
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
index 3fc99bb..1f8a2f0 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -16,6 +16,12 @@
#define LOG_TAG "Gralloc4"
+#include <aidl/android/hardware/graphics/allocator/AllocationError.h>
+#include <aidl/android/hardware/graphics/allocator/AllocationResult.h>
+#include <aidl/android/hardware/graphics/common/BufferUsage.h>
+#include <aidlcommonsupport/NativeHandle.h>
+#include <android/binder_enums.h>
+#include <android/binder_manager.h>
#include <hidl/ServiceManagement.h>
#include <hwbinder/IPCThreadState.h>
#include <ui/Gralloc4.h>
@@ -27,6 +33,8 @@
#include <sync/sync.h>
#pragma clang diagnostic pop
+using aidl::android::hardware::graphics::allocator::AllocationError;
+using aidl::android::hardware::graphics::allocator::AllocationResult;
using aidl::android::hardware::graphics::common::ExtendableType;
using aidl::android::hardware::graphics::common::PlaneLayoutComponentType;
using aidl::android::hardware::graphics::common::StandardMetadataType;
@@ -36,7 +44,10 @@
using android::hardware::graphics::mapper::V4_0::BufferDescriptor;
using android::hardware::graphics::mapper::V4_0::Error;
using android::hardware::graphics::mapper::V4_0::IMapper;
+using AidlIAllocator = ::aidl::android::hardware::graphics::allocator::IAllocator;
+using AidlBufferUsage = ::aidl::android::hardware::graphics::common::BufferUsage;
using AidlDataspace = ::aidl::android::hardware::graphics::common::Dataspace;
+using AidlNativeHandle = ::aidl::android::hardware::common::NativeHandle;
using BufferDump = android::hardware::graphics::mapper::V4_0::IMapper::BufferDump;
using MetadataDump = android::hardware::graphics::mapper::V4_0::IMapper::MetadataDump;
using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
@@ -48,6 +59,7 @@
namespace {
static constexpr Error kTransactionError = Error::NO_RESOURCES;
+static const auto kAidlAllocatorServiceName = AidlIAllocator::descriptor + std::string("/default");
uint64_t getValidUsageBits() {
static const uint64_t validUsageBits = []() -> uint64_t {
@@ -61,6 +73,17 @@
return validUsageBits;
}
+uint64_t getValidUsageBits41() {
+ static const uint64_t validUsageBits = []() -> uint64_t {
+ uint64_t bits = 0;
+ for (const auto bit : ndk::enum_range<AidlBufferUsage>{}) {
+ bits |= static_cast<int64_t>(bit);
+ }
+ return bits;
+ }();
+ return validUsageBits;
+}
+
static inline IMapper::Rect sGralloc4Rect(const Rect& rect) {
IMapper::Rect outRect{};
outRect.left = rect.left;
@@ -81,6 +104,21 @@
outDescriptorInfo->reservedSize = 0;
}
+// See if gralloc "4.1" is available.
+static bool hasIAllocatorAidl() {
+ // Avoid re-querying repeatedly for this information;
+ static bool sHasIAllocatorAidl = []() -> bool {
+ // TODO: Enable after landing sepolicy changes
+ if constexpr ((true)) return false;
+
+ if (__builtin_available(android 31, *)) {
+ return AServiceManager_isDeclared(kAidlAllocatorServiceName.c_str());
+ }
+ return false;
+ }();
+ return sHasIAllocatorAidl;
+}
+
} // anonymous namespace
void Gralloc4Mapper::preload() {
@@ -105,6 +143,9 @@
status_t Gralloc4Mapper::validateBufferDescriptorInfo(
IMapper::BufferDescriptorInfo* descriptorInfo) const {
uint64_t validUsageBits = getValidUsageBits();
+ if (hasIAllocatorAidl()) {
+ validUsageBits |= getValidUsageBits41();
+ }
if (descriptorInfo->usage & ~validUsageBits) {
ALOGE("buffer descriptor contains invalid usage bits 0x%" PRIx64,
@@ -1070,6 +1111,13 @@
ALOGW("allocator 4.x is not supported");
return;
}
+ if (__builtin_available(android 31, *)) {
+ if (hasIAllocatorAidl()) {
+ mAidlAllocator = AidlIAllocator::fromBinder(ndk::SpAIBinder(
+ AServiceManager_waitForService(kAidlAllocatorServiceName.c_str())));
+ ALOGE_IF(!mAidlAllocator, "AIDL IAllocator declared but failed to get service");
+ }
+ }
}
bool Gralloc4Allocator::isLoaded() const {
@@ -1094,6 +1142,52 @@
return error;
}
+ if (mAidlAllocator) {
+ AllocationResult result;
+ auto status = mAidlAllocator->allocate(descriptor, bufferCount, &result);
+ if (!status.isOk()) {
+ error = status.getExceptionCode();
+ if (error == EX_SERVICE_SPECIFIC) {
+ error = status.getServiceSpecificError();
+ }
+ if (error == OK) {
+ error = UNKNOWN_ERROR;
+ }
+ } else {
+ if (importBuffers) {
+ for (uint32_t i = 0; i < bufferCount; i++) {
+ error = mMapper.importBuffer(makeFromAidl(result.buffers[i]),
+ &outBufferHandles[i]);
+ if (error != NO_ERROR) {
+ for (uint32_t j = 0; j < i; j++) {
+ mMapper.freeBuffer(outBufferHandles[j]);
+ outBufferHandles[j] = nullptr;
+ }
+ break;
+ }
+ }
+ } else {
+ for (uint32_t i = 0; i < bufferCount; i++) {
+ outBufferHandles[i] = dupFromAidl(result.buffers[i]);
+ if (!outBufferHandles[i]) {
+ for (uint32_t j = 0; j < i; j++) {
+ auto buffer = const_cast<native_handle_t*>(outBufferHandles[j]);
+ native_handle_close(buffer);
+ native_handle_delete(buffer);
+ outBufferHandles[j] = nullptr;
+ }
+ }
+ }
+ }
+ }
+ *outStride = result.stride;
+ // Release all the resources held by AllocationResult (specifically any remaining FDs)
+ result = {};
+ // make sure the kernel driver sees BC_FREE_BUFFER and closes the fds now
+ hardware::IPCThreadState::self()->flushCommands();
+ return error;
+ }
+
auto ret = mAllocator->allocate(descriptor, bufferCount,
[&](const auto& tmpError, const auto& tmpStride,
const auto& tmpBuffers) {
diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.h b/libs/ui/include/ui/DisplayIdentification.h
similarity index 93%
rename from services/surfaceflinger/DisplayHardware/DisplayIdentification.h
rename to libs/ui/include/ui/DisplayIdentification.h
index fbea4e5..fc9c0f4 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
+++ b/libs/ui/include/ui/DisplayIdentification.h
@@ -31,7 +31,6 @@
namespace android {
-
using DisplayIdentificationData = std::vector<uint8_t>;
struct DisplayIdentificationInfo {
@@ -81,5 +80,7 @@
PhysicalDisplayId getVirtualDisplayId(uint32_t id);
-} // namespace android
+// CityHash64 implementation that only hashes at most the first 16 characters of the given string.
+uint64_t cityHash64Len0To16(std::string_view sv);
+} // namespace android
diff --git a/libs/ui/include/ui/Gralloc4.h b/libs/ui/include/ui/Gralloc4.h
index 62f9e4a..6bafcd6 100644
--- a/libs/ui/include/ui/Gralloc4.h
+++ b/libs/ui/include/ui/Gralloc4.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_UI_GRALLOC4_H
#define ANDROID_UI_GRALLOC4_H
+#include <aidl/android/hardware/graphics/allocator/IAllocator.h>
#include <android/hardware/graphics/allocator/4.0/IAllocator.h>
#include <android/hardware/graphics/common/1.1/types.h>
#include <android/hardware/graphics/mapper/4.0/IMapper.h>
@@ -204,6 +205,8 @@
private:
const Gralloc4Mapper& mMapper;
sp<hardware::graphics::allocator::V4_0::IAllocator> mAllocator;
+ // Optional "4.1" allocator
+ std::shared_ptr<aidl::android::hardware::graphics::allocator::IAllocator> mAidlAllocator;
};
} // namespace android
diff --git a/libs/ui/include/ui/fuzzer/FuzzableDataspaces.h b/libs/ui/include/ui/fuzzer/FuzzableDataspaces.h
new file mode 100644
index 0000000..4200d6a
--- /dev/null
+++ b/libs/ui/include/ui/fuzzer/FuzzableDataspaces.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2021 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 <ui/GraphicTypes.h>
+using namespace android;
+
+constexpr ui::Dataspace kDataspaces[] = {
+ ui::Dataspace::UNKNOWN,
+ ui::Dataspace::ARBITRARY,
+ ui::Dataspace::STANDARD_UNSPECIFIED,
+ ui::Dataspace::STANDARD_BT709,
+ ui::Dataspace::STANDARD_BT601_625,
+ ui::Dataspace::STANDARD_BT601_625_UNADJUSTED,
+ ui::Dataspace::STANDARD_BT601_525,
+ ui::Dataspace::STANDARD_BT601_525_UNADJUSTED,
+ ui::Dataspace::STANDARD_BT2020,
+ ui::Dataspace::STANDARD_BT2020_CONSTANT_LUMINANCE,
+ ui::Dataspace::STANDARD_BT470M,
+ ui::Dataspace::STANDARD_FILM,
+ ui::Dataspace::STANDARD_DCI_P3,
+ ui::Dataspace::STANDARD_ADOBE_RGB,
+ ui::Dataspace::TRANSFER_UNSPECIFIED,
+ ui::Dataspace::TRANSFER_LINEAR,
+ ui::Dataspace::TRANSFER_SRGB,
+ ui::Dataspace::TRANSFER_SMPTE_170M,
+ ui::Dataspace::TRANSFER_GAMMA2_2,
+ ui::Dataspace::TRANSFER_GAMMA2_6,
+ ui::Dataspace::TRANSFER_GAMMA2_8,
+ ui::Dataspace::TRANSFER_ST2084,
+ ui::Dataspace::TRANSFER_HLG,
+ ui::Dataspace::RANGE_UNSPECIFIED,
+ ui::Dataspace::RANGE_FULL,
+ ui::Dataspace::RANGE_LIMITED,
+ ui::Dataspace::RANGE_EXTENDED,
+ ui::Dataspace::SRGB_LINEAR,
+ ui::Dataspace::V0_SRGB_LINEAR,
+ ui::Dataspace::V0_SCRGB_LINEAR,
+ ui::Dataspace::SRGB,
+ ui::Dataspace::V0_SRGB,
+ ui::Dataspace::V0_SCRGB,
+ ui::Dataspace::JFIF,
+ ui::Dataspace::V0_JFIF,
+ ui::Dataspace::BT601_625,
+ ui::Dataspace::V0_BT601_625,
+ ui::Dataspace::BT601_525,
+ ui::Dataspace::V0_BT601_525,
+ ui::Dataspace::BT709,
+ ui::Dataspace::V0_BT709,
+ ui::Dataspace::DCI_P3_LINEAR,
+ ui::Dataspace::DCI_P3,
+ ui::Dataspace::DISPLAY_P3_LINEAR,
+ ui::Dataspace::DISPLAY_P3,
+ ui::Dataspace::ADOBE_RGB,
+ ui::Dataspace::BT2020_LINEAR,
+ ui::Dataspace::BT2020,
+ ui::Dataspace::BT2020_PQ,
+ ui::Dataspace::DEPTH,
+ ui::Dataspace::SENSOR,
+ ui::Dataspace::BT2020_ITU,
+ ui::Dataspace::BT2020_ITU_PQ,
+ ui::Dataspace::BT2020_ITU_HLG,
+ ui::Dataspace::BT2020_HLG,
+ ui::Dataspace::DISPLAY_BT2020,
+ ui::Dataspace::DYNAMIC_DEPTH,
+ ui::Dataspace::JPEG_APP_SEGMENTS,
+ ui::Dataspace::HEIF,
+};
diff --git a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp b/libs/ui/tests/DisplayIdentification_test.cpp
similarity index 98%
rename from services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
rename to libs/ui/tests/DisplayIdentification_test.cpp
index cd4a5c9..736979a 100644
--- a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
+++ b/libs/ui/tests/DisplayIdentification_test.cpp
@@ -24,12 +24,12 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
-#include "DisplayHardware/DisplayIdentification.h"
-#include "DisplayHardware/Hash.h"
+#include <ui/DisplayIdentification.h>
using ::testing::ElementsAre;
namespace android {
+
namespace {
const unsigned char kInternalEdid[] =
diff --git a/services/audiomanager/Android.bp b/services/audiomanager/Android.bp
index e6fb2c3..d11631b 100644
--- a/services/audiomanager/Android.bp
+++ b/services/audiomanager/Android.bp
@@ -7,7 +7,7 @@
default_applicable_licenses: ["frameworks_native_license"],
}
-cc_library_shared {
+cc_library {
name: "libaudiomanager",
srcs: [
diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp
index b9b6a19..5b4ee21 100644
--- a/services/gpuservice/Android.bp
+++ b/services/gpuservice/Android.bp
@@ -31,6 +31,7 @@
"libcutils",
"libgfxstats",
"libgpumem",
+ "libgpuwork",
"libgpumemtracer",
"libgraphicsenv",
"liblog",
diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp
index 52d5d4f..7b9782f 100644
--- a/services/gpuservice/GpuService.cpp
+++ b/services/gpuservice/GpuService.cpp
@@ -25,6 +25,7 @@
#include <binder/PermissionCache.h>
#include <cutils/properties.h>
#include <gpumem/GpuMem.h>
+#include <gpuwork/GpuWork.h>
#include <gpustats/GpuStats.h>
#include <private/android_filesystem_config.h>
#include <tracing/GpuMemTracer.h>
@@ -50,13 +51,20 @@
GpuService::GpuService()
: mGpuMem(std::make_shared<GpuMem>()),
+ mGpuWork(std::make_shared<gpuwork::GpuWork>()),
mGpuStats(std::make_unique<GpuStats>()),
mGpuMemTracer(std::make_unique<GpuMemTracer>()) {
- std::thread asyncInitThread([this]() {
+
+ std::thread gpuMemAsyncInitThread([this]() {
mGpuMem->initialize();
mGpuMemTracer->initialize(mGpuMem);
});
- asyncInitThread.detach();
+ gpuMemAsyncInitThread.detach();
+
+ std::thread gpuWorkAsyncInitThread([this]() {
+ mGpuWork->initialize();
+ });
+ gpuWorkAsyncInitThread.detach();
};
void GpuService::setGpuStats(const std::string& driverPackageName,
@@ -124,6 +132,7 @@
bool dumpDriverInfo = false;
bool dumpMem = false;
bool dumpStats = false;
+ bool dumpWork = false;
size_t numArgs = args.size();
if (numArgs) {
@@ -134,9 +143,11 @@
dumpDriverInfo = true;
} else if (args[index] == String16("--gpumem")) {
dumpMem = true;
+ } else if (args[index] == String16("--gpuwork")) {
+ dumpWork = true;
}
}
- dumpAll = !(dumpDriverInfo || dumpMem || dumpStats);
+ dumpAll = !(dumpDriverInfo || dumpMem || dumpStats || dumpWork);
}
if (dumpAll || dumpDriverInfo) {
@@ -151,6 +162,10 @@
mGpuStats->dump(args, &result);
result.append("\n");
}
+ if (dumpAll || dumpWork) {
+ mGpuWork->dump(args, &result);
+ result.append("\n");
+ }
}
write(fd, result.c_str(), result.size());
diff --git a/services/gpuservice/GpuService.h b/services/gpuservice/GpuService.h
index 409084b..d7313d1 100644
--- a/services/gpuservice/GpuService.h
+++ b/services/gpuservice/GpuService.h
@@ -28,6 +28,10 @@
namespace android {
+namespace gpuwork {
+class GpuWork;
+}
+
class GpuMem;
class GpuStats;
class GpuMemTracer;
@@ -77,6 +81,7 @@
* Attributes
*/
std::shared_ptr<GpuMem> mGpuMem;
+ std::shared_ptr<gpuwork::GpuWork> mGpuWork;
std::unique_ptr<GpuStats> mGpuStats;
std::unique_ptr<GpuMemTracer> mGpuMemTracer;
std::mutex mLock;
diff --git a/services/gpuservice/gpuwork/Android.bp b/services/gpuservice/gpuwork/Android.bp
new file mode 100644
index 0000000..89b31a6
--- /dev/null
+++ b/services/gpuservice/gpuwork/Android.bp
@@ -0,0 +1,61 @@
+// Copyright 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_library_shared {
+ name: "libgpuwork",
+ srcs: [
+ "GpuWork.cpp",
+ ],
+ header_libs: [
+ "gpu_work_structs",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libbpf_bcc",
+ "libbpf_android",
+ "libcutils",
+ "liblog",
+ "libstatslog",
+ "libstatspull",
+ "libutils",
+ ],
+ export_include_dirs: [
+ "include",
+ ],
+ export_header_lib_headers: [
+ "gpu_work_structs",
+ ],
+ export_shared_lib_headers: [
+ "libbase",
+ "libbpf_android",
+ "libstatspull",
+ ],
+ cppflags: [
+ "-Wall",
+ "-Werror",
+ "-Wformat",
+ "-Wthread-safety",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+ required: [
+ "bpfloader",
+ "gpu_work.o",
+ ],
+}
diff --git a/services/gpuservice/gpuwork/GpuWork.cpp b/services/gpuservice/gpuwork/GpuWork.cpp
new file mode 100644
index 0000000..e7b1cd4
--- /dev/null
+++ b/services/gpuservice/gpuwork/GpuWork.cpp
@@ -0,0 +1,504 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "GpuWork"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "gpuwork/GpuWork.h"
+
+#include <android-base/stringprintf.h>
+#include <binder/PermissionCache.h>
+#include <bpf/WaitForProgsLoaded.h>
+#include <libbpf.h>
+#include <libbpf_android.h>
+#include <log/log.h>
+#include <random>
+#include <stats_event.h>
+#include <statslog.h>
+#include <unistd.h>
+#include <utils/Timers.h>
+#include <utils/Trace.h>
+
+#include <bit>
+#include <chrono>
+#include <cstdint>
+#include <limits>
+#include <map>
+#include <mutex>
+#include <unordered_map>
+#include <vector>
+
+#include "gpuwork/gpu_work.h"
+
+#define MS_IN_NS (1000000)
+
+namespace android {
+namespace gpuwork {
+
+namespace {
+
+// Gets a BPF map from |mapPath|.
+template <class Key, class Value>
+bool getBpfMap(const char* mapPath, bpf::BpfMap<Key, Value>* out) {
+ errno = 0;
+ auto map = bpf::BpfMap<Key, Value>(mapPath);
+ if (!map.isValid()) {
+ ALOGW("Failed to create bpf map from %s [%d(%s)]", mapPath, errno, strerror(errno));
+ return false;
+ }
+ *out = std::move(map);
+ return true;
+}
+
+template <typename SourceType>
+inline int32_t cast_int32(SourceType) = delete;
+
+template <typename SourceType>
+inline int32_t bitcast_int32(SourceType) = delete;
+
+template <>
+inline int32_t bitcast_int32<uint32_t>(uint32_t source) {
+ int32_t result;
+ memcpy(&result, &source, sizeof(result));
+ return result;
+}
+
+template <>
+inline int32_t cast_int32<uint64_t>(uint64_t source) {
+ if (source > std::numeric_limits<int32_t>::max()) {
+ return std::numeric_limits<int32_t>::max();
+ }
+ return static_cast<int32_t>(source);
+}
+
+template <>
+inline int32_t cast_int32<long long>(long long source) {
+ if (source > std::numeric_limits<int32_t>::max()) {
+ return std::numeric_limits<int32_t>::max();
+ } else if (source < std::numeric_limits<int32_t>::min()) {
+ return std::numeric_limits<int32_t>::min();
+ }
+ return static_cast<int32_t>(source);
+}
+
+} // namespace
+
+using base::StringAppendF;
+
+GpuWork::~GpuWork() {
+ // If we created our clearer thread, then we must stop it and join it.
+ if (mMapClearerThread.joinable()) {
+ // Tell the thread to terminate.
+ {
+ std::scoped_lock<std::mutex> lock(mMutex);
+ mIsTerminating = true;
+ mIsTerminatingConditionVariable.notify_all();
+ }
+
+ // Now, we can join it.
+ mMapClearerThread.join();
+ }
+
+ {
+ std::scoped_lock<std::mutex> lock(mMutex);
+ if (mStatsdRegistered) {
+ AStatsManager_clearPullAtomCallback(android::util::GPU_FREQ_TIME_IN_STATE_PER_UID);
+ }
+ }
+
+ bpf_detach_tracepoint("power", "gpu_work_period");
+}
+
+void GpuWork::initialize() {
+ // Make sure BPF programs are loaded.
+ bpf::waitForProgsLoaded();
+
+ waitForPermissions();
+
+ // Get the BPF maps before trying to attach the BPF program; if we can't get
+ // the maps then there is no point in attaching the BPF program.
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ if (!getBpfMap("/sys/fs/bpf/map_gpu_work_gpu_work_map", &mGpuWorkMap)) {
+ return;
+ }
+
+ if (!getBpfMap("/sys/fs/bpf/map_gpu_work_gpu_work_global_data", &mGpuWorkGlobalDataMap)) {
+ return;
+ }
+
+ mPreviousMapClearTimePoint = std::chrono::steady_clock::now();
+ }
+
+ // Attach the tracepoint ONLY if we got the map above.
+ if (!attachTracepoint("/sys/fs/bpf/prog_gpu_work_tracepoint_power_gpu_work_period", "power",
+ "gpu_work_period")) {
+ return;
+ }
+
+ // Create the map clearer thread, and store it to |mMapClearerThread|.
+ std::thread thread([this]() { periodicallyClearMap(); });
+
+ mMapClearerThread.swap(thread);
+
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ AStatsManager_setPullAtomCallback(int32_t{android::util::GPU_FREQ_TIME_IN_STATE_PER_UID},
+ nullptr, GpuWork::pullAtomCallback, this);
+ mStatsdRegistered = true;
+ }
+
+ ALOGI("Initialized!");
+
+ mInitialized.store(true);
+}
+
+void GpuWork::dump(const Vector<String16>& /* args */, std::string* result) {
+ if (!mInitialized.load()) {
+ result->append("GPU time in state information is not available.\n");
+ return;
+ }
+
+ // Ordered map ensures output data is sorted by UID.
+ std::map<Uid, UidTrackingInfo> dumpMap;
+
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ if (!mGpuWorkMap.isValid()) {
+ result->append("GPU time in state map is not available.\n");
+ return;
+ }
+
+ // Iteration of BPF hash maps can be unreliable (no data races, but elements
+ // may be repeated), as the map is typically being modified by other
+ // threads. The buckets are all preallocated. Our eBPF program only updates
+ // entries (in-place) or adds entries. |GpuWork| only iterates or clears the
+ // map while holding |mMutex|. Given this, we should be able to iterate over
+ // all elements reliably. In the worst case, we might see elements more than
+ // once.
+
+ // Note that userspace reads of BPF maps make a copy of the value, and
+ // thus the returned value is not being concurrently accessed by the BPF
+ // program (no atomic reads needed below).
+
+ mGpuWorkMap.iterateWithValue([&dumpMap](const Uid& key, const UidTrackingInfo& value,
+ const android::bpf::BpfMap<Uid, UidTrackingInfo>&)
+ -> base::Result<void> {
+ dumpMap[key] = value;
+ return {};
+ });
+ }
+
+ // Find the largest frequency where some UID has spent time in that frequency.
+ size_t largestFrequencyWithTime = 0;
+ for (const auto& uidToUidInfo : dumpMap) {
+ for (size_t i = largestFrequencyWithTime + 1; i < kNumTrackedFrequencies; ++i) {
+ if (uidToUidInfo.second.frequency_times_ns[i] > 0) {
+ largestFrequencyWithTime = i;
+ }
+ }
+ }
+
+ // Dump time in state information.
+ // E.g.
+ // uid/freq: 0MHz 50MHz 100MHz ...
+ // 1000: 0 0 0 0 ...
+ // 1003: 0 0 3456 0 ...
+ // [errors:3]1006: 0 0 3456 0 ...
+
+ // Header.
+ result->append("GPU time in frequency state in ms.\n");
+ result->append("uid/freq: 0MHz");
+ for (size_t i = 1; i <= largestFrequencyWithTime; ++i) {
+ StringAppendF(result, " %zuMHz", i * 50);
+ }
+ result->append("\n");
+
+ for (const auto& uidToUidInfo : dumpMap) {
+ if (uidToUidInfo.second.error_count) {
+ StringAppendF(result, "[errors:%" PRIu32 "]", uidToUidInfo.second.error_count);
+ }
+ StringAppendF(result, "%" PRIu32 ":", uidToUidInfo.first);
+ for (size_t i = 0; i <= largestFrequencyWithTime; ++i) {
+ StringAppendF(result, " %" PRIu64,
+ uidToUidInfo.second.frequency_times_ns[i] / MS_IN_NS);
+ }
+ result->append("\n");
+ }
+}
+
+bool GpuWork::attachTracepoint(const char* programPath, const char* tracepointGroup,
+ const char* tracepointName) {
+ errno = 0;
+ base::unique_fd fd(bpf::retrieveProgram(programPath));
+ if (fd < 0) {
+ ALOGW("Failed to retrieve pinned program from %s [%d(%s)]", programPath, errno,
+ strerror(errno));
+ return false;
+ }
+
+ // Attach the program to the tracepoint. The tracepoint is automatically enabled.
+ errno = 0;
+ int count = 0;
+ while (bpf_attach_tracepoint(fd.get(), tracepointGroup, tracepointName) < 0) {
+ if (++count > kGpuWaitTimeoutSeconds) {
+ ALOGW("Failed to attach bpf program to %s/%s tracepoint [%d(%s)]", tracepointGroup,
+ tracepointName, errno, strerror(errno));
+ return false;
+ }
+ // Retry until GPU driver loaded or timeout.
+ sleep(1);
+ errno = 0;
+ }
+
+ return true;
+}
+
+AStatsManager_PullAtomCallbackReturn GpuWork::pullAtomCallback(int32_t atomTag,
+ AStatsEventList* data,
+ void* cookie) {
+ ATRACE_CALL();
+
+ GpuWork* gpuWork = reinterpret_cast<GpuWork*>(cookie);
+ if (atomTag == android::util::GPU_FREQ_TIME_IN_STATE_PER_UID) {
+ return gpuWork->pullFrequencyAtoms(data);
+ }
+
+ return AStatsManager_PULL_SKIP;
+}
+
+AStatsManager_PullAtomCallbackReturn GpuWork::pullFrequencyAtoms(AStatsEventList* data) {
+ ATRACE_CALL();
+
+ if (!data || !mInitialized.load()) {
+ return AStatsManager_PULL_SKIP;
+ }
+
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ if (!mGpuWorkMap.isValid()) {
+ return AStatsManager_PULL_SKIP;
+ }
+
+ std::unordered_map<Uid, UidTrackingInfo> uidInfos;
+
+ // Iteration of BPF hash maps can be unreliable (no data races, but elements
+ // may be repeated), as the map is typically being modified by other
+ // threads. The buckets are all preallocated. Our eBPF program only updates
+ // entries (in-place) or adds entries. |GpuWork| only iterates or clears the
+ // map while holding |mMutex|. Given this, we should be able to iterate over
+ // all elements reliably. In the worst case, we might see elements more than
+ // once.
+
+ // Note that userspace reads of BPF maps make a copy of the value, and thus
+ // the returned value is not being concurrently accessed by the BPF program
+ // (no atomic reads needed below).
+
+ mGpuWorkMap.iterateWithValue(
+ [&uidInfos](const Uid& key, const UidTrackingInfo& value,
+ const android::bpf::BpfMap<Uid, UidTrackingInfo>&) -> base::Result<void> {
+ uidInfos[key] = value;
+ return {};
+ });
+
+ ALOGI("pullFrequencyAtoms: uidInfos.size() == %zu", uidInfos.size());
+
+ // Get a list of just the UIDs; the order does not matter.
+ std::vector<Uid> uids;
+ for (const auto& pair : uidInfos) {
+ uids.push_back(pair.first);
+ }
+
+ std::random_device device;
+ std::default_random_engine random_engine(device());
+
+ // If we have more than |kNumSampledUids| UIDs, choose |kNumSampledUids|
+ // random UIDs. We swap them to the front of the list. Given the list
+ // indices 0..i..n-1, we have the following inclusive-inclusive ranges:
+ // - [0, i-1] == the randomly chosen elements.
+ // - [i, n-1] == the remaining unchosen elements.
+ if (uids.size() > kNumSampledUids) {
+ for (size_t i = 0; i < kNumSampledUids; ++i) {
+ std::uniform_int_distribution<size_t> uniform_dist(i, uids.size() - 1);
+ size_t random_index = uniform_dist(random_engine);
+ std::swap(uids[i], uids[random_index]);
+ }
+ // Only keep the front |kNumSampledUids| elements.
+ uids.resize(kNumSampledUids);
+ }
+
+ ALOGI("pullFrequencyAtoms: uids.size() == %zu", uids.size());
+
+ auto now = std::chrono::steady_clock::now();
+
+ int32_t duration = cast_int32(
+ std::chrono::duration_cast<std::chrono::seconds>(now - mPreviousMapClearTimePoint)
+ .count());
+
+ for (const Uid uid : uids) {
+ const UidTrackingInfo& info = uidInfos[uid];
+ ALOGI("pullFrequencyAtoms: adding stats for UID %" PRIu32, uid);
+ android::util::addAStatsEvent(data, int32_t{android::util::GPU_FREQ_TIME_IN_STATE_PER_UID},
+ // uid
+ bitcast_int32(uid),
+ // time_duration_seconds
+ int32_t{duration},
+ // max_freq_mhz
+ int32_t{1000},
+ // freq_0_mhz_time_millis
+ cast_int32(info.frequency_times_ns[0] / 1000000),
+ // freq_50_mhz_time_millis
+ cast_int32(info.frequency_times_ns[1] / 1000000),
+ // ... etc. ...
+ cast_int32(info.frequency_times_ns[2] / 1000000),
+ cast_int32(info.frequency_times_ns[3] / 1000000),
+ cast_int32(info.frequency_times_ns[4] / 1000000),
+ cast_int32(info.frequency_times_ns[5] / 1000000),
+ cast_int32(info.frequency_times_ns[6] / 1000000),
+ cast_int32(info.frequency_times_ns[7] / 1000000),
+ cast_int32(info.frequency_times_ns[8] / 1000000),
+ cast_int32(info.frequency_times_ns[9] / 1000000),
+ cast_int32(info.frequency_times_ns[10] / 1000000),
+ cast_int32(info.frequency_times_ns[11] / 1000000),
+ cast_int32(info.frequency_times_ns[12] / 1000000),
+ cast_int32(info.frequency_times_ns[13] / 1000000),
+ cast_int32(info.frequency_times_ns[14] / 1000000),
+ cast_int32(info.frequency_times_ns[15] / 1000000),
+ cast_int32(info.frequency_times_ns[16] / 1000000),
+ cast_int32(info.frequency_times_ns[17] / 1000000),
+ cast_int32(info.frequency_times_ns[18] / 1000000),
+ cast_int32(info.frequency_times_ns[19] / 1000000),
+ // freq_1000_mhz_time_millis
+ cast_int32(info.frequency_times_ns[20] / 1000000));
+ }
+ clearMap();
+ return AStatsManager_PULL_SUCCESS;
+}
+
+void GpuWork::periodicallyClearMap() {
+ std::unique_lock<std::mutex> lock(mMutex);
+
+ auto previousTime = std::chrono::steady_clock::now();
+
+ while (true) {
+ if (mIsTerminating) {
+ break;
+ }
+ auto nextTime = std::chrono::steady_clock::now();
+ auto differenceSeconds =
+ std::chrono::duration_cast<std::chrono::seconds>(nextTime - previousTime);
+ if (differenceSeconds.count() > kMapClearerWaitDurationSeconds) {
+ // It has been >1 hour, so clear the map, if needed.
+ clearMapIfNeeded();
+ // We only update |previousTime| if we actually checked the map.
+ previousTime = nextTime;
+ }
+ // Sleep for ~1 hour. It does not matter if we don't check the map for 2
+ // hours.
+ mIsTerminatingConditionVariable.wait_for(lock,
+ std::chrono::seconds{
+ kMapClearerWaitDurationSeconds});
+ }
+}
+
+void GpuWork::clearMapIfNeeded() {
+ if (!mInitialized.load() || !mGpuWorkMap.isValid() || !mGpuWorkGlobalDataMap.isValid()) {
+ ALOGW("Map clearing could not occur because we are not initialized properly");
+ return;
+ }
+
+ base::Result<GlobalData> globalData = mGpuWorkGlobalDataMap.readValue(0);
+ if (!globalData.ok()) {
+ ALOGW("Could not read BPF global data map entry");
+ return;
+ }
+
+ // Note that userspace reads of BPF maps make a copy of the value, and thus
+ // the return value is not being concurrently accessed by the BPF program
+ // (no atomic reads needed below).
+
+ uint64_t numEntries = globalData.value().num_map_entries;
+
+ // If the map is <=75% full, we do nothing.
+ if (numEntries <= (kMaxTrackedUids / 4) * 3) {
+ return;
+ }
+
+ clearMap();
+}
+
+void GpuWork::clearMap() {
+ if (!mInitialized.load() || !mGpuWorkMap.isValid() || !mGpuWorkGlobalDataMap.isValid()) {
+ ALOGW("Map clearing could not occur because we are not initialized properly");
+ return;
+ }
+
+ base::Result<GlobalData> globalData = mGpuWorkGlobalDataMap.readValue(0);
+ if (!globalData.ok()) {
+ ALOGW("Could not read BPF global data map entry");
+ return;
+ }
+
+ // Iterating BPF maps to delete keys is tricky. If we just repeatedly call
+ // |getFirstKey()| and delete that, we may loop forever (or for a long time)
+ // because our BPF program might be repeatedly re-adding UID keys. Also,
+ // even if we limit the number of elements we try to delete, we might only
+ // delete new entries, leaving old entries in the map. If we delete a key A
+ // and then call |getNextKey(A)|, the first key in the map is returned, so
+ // we have the same issue.
+ //
+ // Thus, we instead get the next key and then delete the previous key. We
+ // also limit the number of deletions we try, just in case.
+
+ base::Result<Uid> key = mGpuWorkMap.getFirstKey();
+
+ for (size_t i = 0; i < kMaxTrackedUids; ++i) {
+ if (!key.ok()) {
+ break;
+ }
+ base::Result<Uid> previousKey = key;
+ key = mGpuWorkMap.getNextKey(previousKey.value());
+ mGpuWorkMap.deleteValue(previousKey.value());
+ }
+
+ // Reset our counter; |globalData| is a copy of the data, so we have to use
+ // |writeValue|.
+ globalData.value().num_map_entries = 0;
+ mGpuWorkGlobalDataMap.writeValue(0, globalData.value(), BPF_ANY);
+
+ // Update |mPreviousMapClearTimePoint| so we know when we started collecting
+ // the stats.
+ mPreviousMapClearTimePoint = std::chrono::steady_clock::now();
+}
+
+void GpuWork::waitForPermissions() {
+ const String16 permissionRegisterStatsPullAtom(kPermissionRegisterStatsPullAtom);
+ int count = 0;
+ while (!PermissionCache::checkPermission(permissionRegisterStatsPullAtom, getpid(), getuid())) {
+ if (++count > kPermissionsWaitTimeoutSeconds) {
+ ALOGW("Timed out waiting for android.permission.REGISTER_STATS_PULL_ATOM");
+ return;
+ }
+ // Retry.
+ sleep(1);
+ }
+}
+
+} // namespace gpuwork
+} // namespace android
diff --git a/services/gpuservice/gpuwork/bpfprogs/Android.bp b/services/gpuservice/gpuwork/bpfprogs/Android.bp
new file mode 100644
index 0000000..b3c4eff
--- /dev/null
+++ b/services/gpuservice/gpuwork/bpfprogs/Android.bp
@@ -0,0 +1,35 @@
+// Copyright 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+bpf {
+ name: "gpu_work.o",
+ srcs: ["gpu_work.c"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wformat",
+ "-Wthread-safety",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+}
+
+cc_library_headers {
+ name: "gpu_work_structs",
+ export_include_dirs: ["include"],
+}
diff --git a/services/gpuservice/gpuwork/bpfprogs/gpu_work.c b/services/gpuservice/gpuwork/bpfprogs/gpu_work.c
new file mode 100644
index 0000000..a0e1b22
--- /dev/null
+++ b/services/gpuservice/gpuwork/bpfprogs/gpu_work.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2022 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 "include/gpuwork/gpu_work.h"
+
+#include <linux/bpf.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef MOCK_BPF
+#include <test/mock_bpf_helpers.h>
+#else
+#include <bpf_helpers.h>
+#endif
+
+#define S_IN_NS (1000000000)
+#define MHZ_IN_KHZS (1000)
+
+typedef uint32_t Uid;
+
+// A map from UID to |UidTrackingInfo|.
+DEFINE_BPF_MAP_GRW(gpu_work_map, HASH, Uid, UidTrackingInfo, kMaxTrackedUids, AID_GRAPHICS);
+
+// A map containing a single entry of |GlobalData|.
+DEFINE_BPF_MAP_GRW(gpu_work_global_data, ARRAY, uint32_t, GlobalData, 1, AID_GRAPHICS);
+
+// GpuUidWorkPeriodEvent defines the structure of a kernel tracepoint under the
+// tracepoint system (also referred to as the group) "power" and name
+// "gpu_work_period". In summary, the kernel tracepoint should be
+// "power/gpu_work_period", available at:
+//
+// /sys/kernel/tracing/events/power/gpu_work_period/
+//
+// GpuUidWorkPeriodEvent defines a non-overlapping, non-zero period of time when
+// work was running on the GPU for a given application (identified by its UID;
+// the persistent, unique ID of the application) from |start_time_ns| to
+// |end_time_ns|. Drivers should issue this tracepoint as soon as possible
+// (within 1 second) after |end_time_ns|. For a given UID, periods must not
+// overlap, but periods from different UIDs can overlap (and should overlap, if
+// and only if that is how the work was executed). The period includes
+// information such as |frequency_khz|, the frequency that the GPU was running
+// at during the period, and |includes_compute_work|, whether the work included
+// compute shader work (not just graphics work). GPUs may have multiple
+// frequencies that can be adjusted, but the driver should report the frequency
+// that most closely estimates power usage (e.g. the frequency of shader cores,
+// not a scheduling unit).
+//
+// If any information changes while work from the UID is running on the GPU
+// (e.g. the GPU frequency changes, or the work starts/stops including compute
+// work) then the driver must conceptually end the period, issue the tracepoint,
+// start tracking a new period, and eventually issue a second tracepoint when
+// the work completes or when the information changes again. In this case, the
+// |end_time_ns| of the first period must equal the |start_time_ns| of the
+// second period. The driver may also end and start a new period (without a
+// gap), even if no information changes. For example, this might be convenient
+// if there is a collection of work from a UID running on the GPU for a long
+// time; ending and starting a period as individual parts of the work complete
+// allows the consumer of the tracepoint to be updated about the ongoing work.
+//
+// For a given UID, the tracepoints must be emitted in order; that is, the
+// |start_time_ns| of each subsequent tracepoint for a given UID must be
+// monotonically increasing.
+typedef struct {
+ // Actual fields start at offset 8.
+ uint64_t reserved;
+
+ // The UID of the process (i.e. persistent, unique ID of the Android app)
+ // that submitted work to the GPU.
+ uint32_t uid;
+
+ // The GPU frequency during the period. GPUs may have multiple frequencies
+ // that can be adjusted, but the driver should report the frequency that
+ // would most closely estimate power usage (e.g. the frequency of shader
+ // cores, not a scheduling unit).
+ uint32_t frequency_khz;
+
+ // The start time of the period in nanoseconds. The clock must be
+ // CLOCK_MONOTONIC, as returned by the ktime_get_ns(void) function.
+ uint64_t start_time_ns;
+
+ // The end time of the period in nanoseconds. The clock must be
+ // CLOCK_MONOTONIC, as returned by the ktime_get_ns(void) function.
+ uint64_t end_time_ns;
+
+ // Flags about the work. Reserved for future use.
+ uint64_t flags;
+
+ // The maximum GPU frequency allowed during the period according to the
+ // thermal throttling policy. Must be 0 if no thermal throttling was
+ // enforced during the period. The value must relate to |frequency_khz|; in
+ // other words, if |frequency_khz| is the frequency of the GPU shader cores,
+ // then |thermally_throttled_max_frequency_khz| must be the maximum allowed
+ // frequency of the GPU shader cores (not the maximum allowed frequency of
+ // some other part of the GPU).
+ //
+ // Note: Unlike with other fields of this struct, if the
+ // |thermally_throttled_max_frequency_khz| value conceptually changes while
+ // work from a UID is running on the GPU then the GPU driver does NOT need
+ // to accurately report this by ending the period and starting to track a
+ // new period; instead, the GPU driver may report any one of the
+ // |thermally_throttled_max_frequency_khz| values that was enforced during
+ // the period. The motivation for this relaxation is that we assume the
+ // maximum frequency will not be changing rapidly, and so capturing the
+ // exact point at which the change occurs is unnecessary.
+ uint32_t thermally_throttled_max_frequency_khz;
+
+} GpuUidWorkPeriodEvent;
+
+_Static_assert(offsetof(GpuUidWorkPeriodEvent, uid) == 8 &&
+ offsetof(GpuUidWorkPeriodEvent, frequency_khz) == 12 &&
+ offsetof(GpuUidWorkPeriodEvent, start_time_ns) == 16 &&
+ offsetof(GpuUidWorkPeriodEvent, end_time_ns) == 24 &&
+ offsetof(GpuUidWorkPeriodEvent, flags) == 32 &&
+ offsetof(GpuUidWorkPeriodEvent, thermally_throttled_max_frequency_khz) == 40,
+ "Field offsets of struct GpuUidWorkPeriodEvent must not be changed because they "
+ "must match the tracepoint field offsets found via adb shell cat "
+ "/sys/kernel/tracing/events/power/gpu_work_period/format");
+
+DEFINE_BPF_PROG("tracepoint/power/gpu_work_period", AID_ROOT, AID_GRAPHICS, tp_gpu_work_period)
+(GpuUidWorkPeriodEvent* const args) {
+ // Note: In BPF programs, |__sync_fetch_and_add| is translated to an atomic
+ // add.
+
+ const uint32_t uid = args->uid;
+
+ // Get |UidTrackingInfo| for |uid|.
+ UidTrackingInfo* uid_tracking_info = bpf_gpu_work_map_lookup_elem(&uid);
+ if (!uid_tracking_info) {
+ // There was no existing entry, so we add a new one.
+ UidTrackingInfo initial_info;
+ __builtin_memset(&initial_info, 0, sizeof(initial_info));
+ if (0 == bpf_gpu_work_map_update_elem(&uid, &initial_info, BPF_NOEXIST)) {
+ // We added an entry to the map, so we increment our entry counter in
+ // |GlobalData|.
+ const uint32_t zero = 0;
+ // Get the |GlobalData|.
+ GlobalData* global_data = bpf_gpu_work_global_data_lookup_elem(&zero);
+ // Getting the global data never fails because it is an |ARRAY| map,
+ // but we need to keep the verifier happy.
+ if (global_data) {
+ __sync_fetch_and_add(&global_data->num_map_entries, 1);
+ }
+ }
+ uid_tracking_info = bpf_gpu_work_map_lookup_elem(&uid);
+ if (!uid_tracking_info) {
+ // This should never happen, unless entries are getting deleted at
+ // this moment. If so, we just give up.
+ return 0;
+ }
+ }
+
+ // The period duration must be non-zero.
+ if (args->start_time_ns >= args->end_time_ns) {
+ __sync_fetch_and_add(&uid_tracking_info->error_count, 1);
+ return 0;
+ }
+
+ // The frequency must not be 0.
+ if (args->frequency_khz == 0) {
+ __sync_fetch_and_add(&uid_tracking_info->error_count, 1);
+ return 0;
+ }
+
+ // Calculate the frequency index: see |UidTrackingInfo.frequency_times_ns|.
+ // Round to the nearest 50MHz bucket.
+ uint32_t frequency_index =
+ ((args->frequency_khz / MHZ_IN_KHZS) + (kFrequencyGranularityMhz / 2)) /
+ kFrequencyGranularityMhz;
+ if (frequency_index >= kNumTrackedFrequencies) {
+ frequency_index = kNumTrackedFrequencies - 1;
+ }
+
+ // Never round down to 0MHz, as this is a special bucket (see below) and not
+ // an actual operating point.
+ if (frequency_index == 0) {
+ frequency_index = 1;
+ }
+
+ // Update time in state.
+ __sync_fetch_and_add(&uid_tracking_info->frequency_times_ns[frequency_index],
+ args->end_time_ns - args->start_time_ns);
+
+ if (uid_tracking_info->previous_end_time_ns > args->start_time_ns) {
+ // This must not happen because per-UID periods must not overlap and
+ // must be emitted in order.
+ __sync_fetch_and_add(&uid_tracking_info->error_count, 1);
+ } else {
+ // The period appears to have been emitted after the previous, as
+ // expected, so we can calculate the gap between this and the previous
+ // period.
+ const uint64_t gap_time = args->start_time_ns - uid_tracking_info->previous_end_time_ns;
+
+ // Update |previous_end_time_ns|.
+ uid_tracking_info->previous_end_time_ns = args->end_time_ns;
+
+ // Update the special 0MHz frequency time, which stores the gaps between
+ // periods, but only if the gap is < 1 second.
+ if (gap_time > 0 && gap_time < S_IN_NS) {
+ __sync_fetch_and_add(&uid_tracking_info->frequency_times_ns[0], gap_time);
+ }
+ }
+
+ return 0;
+}
+
+LICENSE("Apache 2.0");
diff --git a/services/gpuservice/gpuwork/bpfprogs/include/gpuwork/gpu_work.h b/services/gpuservice/gpuwork/bpfprogs/include/gpuwork/gpu_work.h
new file mode 100644
index 0000000..4fe8464
--- /dev/null
+++ b/services/gpuservice/gpuwork/bpfprogs/include/gpuwork/gpu_work.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2022 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 <stdint.h>
+
+#ifdef __cplusplus
+#include <type_traits>
+
+namespace android {
+namespace gpuwork {
+#endif
+
+typedef struct {
+ // The end time of the previous period from this UID in nanoseconds.
+ uint64_t previous_end_time_ns;
+
+ // The time spent at each GPU frequency while running GPU work from the UID,
+ // in nanoseconds. Array index i stores the time for frequency i*50 MHz. So
+ // index 0 is 0Mhz, index 1 is 50MHz, index 2 is 100MHz, etc., up to index
+ // |kNumTrackedFrequencies|.
+ uint64_t frequency_times_ns[21];
+
+ // The number of times we received |GpuUidWorkPeriodEvent| events in an
+ // unexpected order. See |GpuUidWorkPeriodEvent|.
+ uint32_t error_count;
+
+} UidTrackingInfo;
+
+typedef struct {
+ // We cannot query the number of entries in BPF map |gpu_work_map|. We track
+ // the number of entries (approximately) using a counter so we can check if
+ // the map is nearly full.
+ uint64_t num_map_entries;
+} GlobalData;
+
+static const uint32_t kMaxTrackedUids = 512;
+static const uint32_t kFrequencyGranularityMhz = 50;
+static const uint32_t kNumTrackedFrequencies = 21;
+
+#ifdef __cplusplus
+static_assert(kNumTrackedFrequencies ==
+ std::extent<decltype(UidTrackingInfo::frequency_times_ns)>::value);
+
+} // namespace gpuwork
+} // namespace android
+#endif
diff --git a/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h b/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h
new file mode 100644
index 0000000..b6f493d
--- /dev/null
+++ b/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2022 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 <bpf/BpfMap.h>
+#include <stats_pull_atom_callback.h>
+#include <utils/Mutex.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+#include <condition_variable>
+#include <cstdint>
+#include <functional>
+#include <thread>
+
+#include "gpuwork/gpu_work.h"
+
+namespace android {
+namespace gpuwork {
+
+class GpuWork {
+public:
+ using Uid = uint32_t;
+
+ GpuWork() = default;
+ ~GpuWork();
+
+ void initialize();
+
+ // Dumps the GPU time in frequency state information.
+ void dump(const Vector<String16>& args, std::string* result);
+
+private:
+ // Attaches tracepoint |tracepoint_group|/|tracepoint_name| to BPF program at path
+ // |program_path|. The tracepoint is also enabled.
+ static bool attachTracepoint(const char* program_path, const char* tracepoint_group,
+ const char* tracepoint_name);
+
+ // Native atom puller callback registered in statsd.
+ static AStatsManager_PullAtomCallbackReturn pullAtomCallback(int32_t atomTag,
+ AStatsEventList* data,
+ void* cookie);
+
+ AStatsManager_PullAtomCallbackReturn pullFrequencyAtoms(AStatsEventList* data);
+
+ // Periodically calls |clearMapIfNeeded| to clear the |mGpuWorkMap| map, if
+ // needed.
+ //
+ // Thread safety analysis is skipped because we need to use
+ // |std::unique_lock|, which is not currently supported by thread safety
+ // analysis.
+ void periodicallyClearMap() NO_THREAD_SAFETY_ANALYSIS;
+
+ // Checks whether the |mGpuWorkMap| map is nearly full and, if so, clears
+ // it.
+ void clearMapIfNeeded() REQUIRES(mMutex);
+
+ // Clears the |mGpuWorkMap| map.
+ void clearMap() REQUIRES(mMutex);
+
+ // Waits for required permissions to become set. This seems to be needed
+ // because platform service permissions might not be set when a service
+ // first starts. See b/214085769.
+ void waitForPermissions();
+
+ // Indicates whether our eBPF components have been initialized.
+ std::atomic<bool> mInitialized = false;
+
+ // A thread that periodically checks whether |mGpuWorkMap| is nearly full
+ // and, if so, clears it.
+ std::thread mMapClearerThread;
+
+ // Mutex for |mGpuWorkMap| and a few other fields.
+ std::mutex mMutex;
+
+ // BPF map for per-UID GPU work.
+ bpf::BpfMap<Uid, UidTrackingInfo> mGpuWorkMap GUARDED_BY(mMutex);
+
+ // BPF map containing a single element for global data.
+ bpf::BpfMap<uint32_t, GlobalData> mGpuWorkGlobalDataMap GUARDED_BY(mMutex);
+
+ // When true, we are being destructed, so |mMapClearerThread| should stop.
+ bool mIsTerminating GUARDED_BY(mMutex);
+
+ // A condition variable for |mIsTerminating|.
+ std::condition_variable mIsTerminatingConditionVariable GUARDED_BY(mMutex);
+
+ // 30 second timeout for trying to attach a BPF program to a tracepoint.
+ static constexpr int kGpuWaitTimeoutSeconds = 30;
+
+ // The wait duration for the map clearer thread; the thread checks the map
+ // every ~1 hour.
+ static constexpr uint32_t kMapClearerWaitDurationSeconds = 60 * 60;
+
+ // Whether our |pullAtomCallback| function is registered.
+ bool mStatsdRegistered GUARDED_BY(mMutex) = false;
+
+ // The number of randomly chosen (i.e. sampled) UIDs to log stats for.
+ static constexpr int kNumSampledUids = 10;
+
+ // The previous time point at which |mGpuWorkMap| was cleared.
+ std::chrono::steady_clock::time_point mPreviousMapClearTimePoint GUARDED_BY(mMutex);
+
+ // Permission to register a statsd puller.
+ static constexpr char16_t kPermissionRegisterStatsPullAtom[] =
+ u"android.permission.REGISTER_STATS_PULL_ATOM";
+
+ // Time limit for waiting for permissions.
+ static constexpr int kPermissionsWaitTimeoutSeconds = 30;
+};
+
+} // namespace gpuwork
+} // namespace android
diff --git a/services/gpuservice/vts/Android.bp b/services/gpuservice/vts/Android.bp
new file mode 100644
index 0000000..83a40e7
--- /dev/null
+++ b/services/gpuservice/vts/Android.bp
@@ -0,0 +1,30 @@
+// Copyright 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+java_test_host {
+ name: "GpuServiceVendorTests",
+ srcs: ["src/**/*.java"],
+ libs: [
+ "tradefed",
+ "vts-core-tradefed-harness",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+}
diff --git a/services/gpuservice/vts/AndroidTest.xml b/services/gpuservice/vts/AndroidTest.xml
new file mode 100644
index 0000000..02ca07f
--- /dev/null
+++ b/services/gpuservice/vts/AndroidTest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2022 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.
+-->
+<configuration description="Runs GpuServiceVendorTests">
+ <test class="com.android.tradefed.testtype.HostTest" >
+ <option name="jar" value="GpuServiceVendorTests.jar" />
+ </test>
+</configuration>
diff --git a/services/gpuservice/vts/OWNERS b/services/gpuservice/vts/OWNERS
new file mode 100644
index 0000000..e789052
--- /dev/null
+++ b/services/gpuservice/vts/OWNERS
@@ -0,0 +1,7 @@
+# Bug component: 653544
+paulthomson@google.com
+pbaiget@google.com
+lfy@google.com
+chrisforbes@google.com
+lpy@google.com
+alecmouri@google.com
diff --git a/services/gpuservice/vts/TEST_MAPPING b/services/gpuservice/vts/TEST_MAPPING
new file mode 100644
index 0000000..b33e962
--- /dev/null
+++ b/services/gpuservice/vts/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "GpuServiceVendorTests"
+ }
+ ]
+}
diff --git a/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java b/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java
new file mode 100644
index 0000000..4a77d9a
--- /dev/null
+++ b/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tests.gpuservice;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.util.CommandResult;
+import com.android.tradefed.util.CommandStatus;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class GpuWorkTracepointTest extends BaseHostJUnit4Test {
+
+ private static final String CPU_FREQUENCY_TRACEPOINT_FORMAT_PATH =
+ "/sys/kernel/tracing/events/power/cpu_frequency/format";
+ private static final String GPU_WORK_PERIOD_TRACEPOINT_FORMAT_PATH =
+ "/sys/kernel/tracing/events/power/gpu_work_period/format";
+
+ @Test
+ public void testReadTracingEvents() throws Exception {
+ // Test |testGpuWorkPeriodTracepointFormat| is dependent on whether certain tracepoint
+ // paths exist. This means the test will vacuously pass if the tracepoint file system is
+ // inaccessible. Thus, as a basic check, we make sure the CPU frequency tracepoint format
+ // is accessible. If not, something is probably fundamentally broken about the tracing
+ // file system.
+ CommandResult commandResult = getDevice().executeShellV2Command(
+ String.format("cat %s", CPU_FREQUENCY_TRACEPOINT_FORMAT_PATH));
+
+ assertEquals(String.format(
+ "Failed to read \"%s\". This probably means that the tracing file system "
+ + "is fundamentally broken in some way, possibly due to bad "
+ + "permissions.",
+ CPU_FREQUENCY_TRACEPOINT_FORMAT_PATH),
+ commandResult.getStatus(), CommandStatus.SUCCESS);
+ }
+
+ @Test
+ public void testGpuWorkPeriodTracepointFormat() throws Exception {
+ CommandResult commandResult = getDevice().executeShellV2Command(
+ String.format("cat %s", GPU_WORK_PERIOD_TRACEPOINT_FORMAT_PATH));
+
+ // If we failed to cat the tracepoint format then the test ends here.
+ assumeTrue(String.format("Failed to cat the gpu_work_period tracepoint format at %s",
+ GPU_WORK_PERIOD_TRACEPOINT_FORMAT_PATH),
+ commandResult.getStatus().equals(CommandStatus.SUCCESS));
+
+ // Otherwise, we check that the fields match the expected fields.
+ String actualFields = Arrays.stream(
+ commandResult.getStdout().trim().split("\n")).filter(
+ s -> s.startsWith("\tfield:")).collect(
+ Collectors.joining("\n"));
+
+ String expectedFields = String.join("\n",
+ "\tfield:unsigned short common_type;\toffset:0;\tsize:2;\tsigned:0;",
+ "\tfield:unsigned char common_flags;\toffset:2;\tsize:1;\tsigned:0;",
+ "\tfield:unsigned char common_preempt_count;\toffset:3;\tsize:1;\tsigned:0;",
+ "\tfield:int common_pid;\toffset:4;\tsize:4;\tsigned:1;",
+ "\tfield:u32 uid;\toffset:8;\tsize:4;\tsigned:0;",
+ "\tfield:u32 frequency;\toffset:12;\tsize:4;\tsigned:0;",
+ "\tfield:u64 start_time;\toffset:16;\tsize:8;\tsigned:0;",
+ "\tfield:u64 end_time;\toffset:24;\tsize:8;\tsigned:0;",
+ "\tfield:u64 flags;\toffset:32;\tsize:8;\tsigned:0;",
+ "\tfield:u32 thermally_throttled_max_frequency_khz;\toffset:40;\tsize:4;\tsigned:0;"
+ );
+
+ // We use |fail| rather than |assertEquals| because it allows us to give a clearer message.
+ if (!expectedFields.equals(actualFields)) {
+ String message = String.format(
+ "Tracepoint format given by \"%s\" does not match the expected format.\n"
+ + "Expected fields:\n%s\n\nActual fields:\n%s\n\n",
+ GPU_WORK_PERIOD_TRACEPOINT_FORMAT_PATH, expectedFields, actualFields);
+ fail(message);
+ }
+ }
+}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 3f3c0db..441a1de 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -5645,6 +5645,15 @@
return;
}
+ if (enabled) {
+ if (std::find(mIneligibleDisplaysForPointerCapture.begin(),
+ mIneligibleDisplaysForPointerCapture.end(),
+ mFocusedDisplayId) != mIneligibleDisplaysForPointerCapture.end()) {
+ ALOGW("Ignoring request to enable Pointer Capture: display is not eligible");
+ return;
+ }
+ }
+
setPointerCaptureLocked(enabled);
} // release lock
@@ -5652,6 +5661,16 @@
mLooper->wake();
}
+void InputDispatcher::setDisplayEligibilityForPointerCapture(int32_t displayId, bool isEligible) {
+ { // acquire lock
+ std::scoped_lock _l(mLock);
+ std::erase(mIneligibleDisplaysForPointerCapture, displayId);
+ if (!isEligible) {
+ mIneligibleDisplaysForPointerCapture.push_back(displayId);
+ }
+ } // release lock
+}
+
std::optional<int32_t> InputDispatcher::findGestureMonitorDisplayByTokenLocked(
const sp<IBinder>& token) {
for (const auto& it : mGestureMonitorsByDisplay) {
@@ -6311,6 +6330,8 @@
// Call focus resolver to clean up stale requests. This must be called after input windows
// have been removed for the removed display.
mFocusResolver.displayRemoved(displayId);
+ // Reset pointer capture eligibility, regardless of previous state.
+ std::erase(mIneligibleDisplaysForPointerCapture, displayId);
} // release lock
// Wake up poll loop since it may need to make new input dispatching choices.
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index cbb7bad..7564839 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -136,6 +136,7 @@
status_t pilferPointers(const sp<IBinder>& token) override;
void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled) override;
bool flushSensor(int deviceId, InputDeviceSensorType sensorType) override;
+ void setDisplayEligibilityForPointerCapture(int displayId, bool isEligible) override;
std::array<uint8_t, 32> sign(const VerifiedInputEvent& event) const;
@@ -420,6 +421,10 @@
// This should be in sync with PointerCaptureChangedEvents dispatched to the input channel.
sp<IBinder> mWindowTokenWithPointerCapture GUARDED_BY(mLock);
+ // Displays that are ineligible for pointer capture.
+ // TODO(b/214621487): Remove or move to a display flag.
+ std::vector<int32_t> mIneligibleDisplaysForPointerCapture GUARDED_BY(mLock);
+
// Disable Pointer Capture as a result of loss of window focus.
void disablePointerCaptureForcedLocked() REQUIRES(mLock);
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index c469ec3..16a6f16 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -201,6 +201,13 @@
*/
virtual void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled) = 0;
+ /**
+ * Sets the eligibility of a given display to enable pointer capture. If a display is marked
+ * ineligible, all attempts to request pointer capture for windows on that display will fail.
+ * TODO(b/214621487): Remove or move to a display flag.
+ */
+ virtual void setDisplayEligibilityForPointerCapture(int displayId, bool isEligible) = 0;
+
/* Flush input device motion sensor.
*
* Returns true on success.
diff --git a/services/sensorservice/AidlSensorHalWrapper.cpp b/services/sensorservice/AidlSensorHalWrapper.cpp
index 049d06a..cdd95ca 100644
--- a/services/sensorservice/AidlSensorHalWrapper.cpp
+++ b/services/sensorservice/AidlSensorHalWrapper.cpp
@@ -238,6 +238,18 @@
break;
}
+ case SensorType::HEAD_TRACKER: {
+ const auto &ht = src.payload.get<Event::EventPayload::headTracker>();
+ dst->head_tracker.rx = ht.rx;
+ dst->head_tracker.ry = ht.ry;
+ dst->head_tracker.rz = ht.rz;
+ dst->head_tracker.vx = ht.vx;
+ dst->head_tracker.vy = ht.vy;
+ dst->head_tracker.vz = ht.vz;
+ dst->head_tracker.discontinuity_count = ht.discontinuityCount;
+ break;
+ }
+
default: {
CHECK_GE((int32_t)src.sensorType, (int32_t)SensorType::DEVICE_PRIVATE_BASE);
@@ -383,6 +395,20 @@
break;
}
+ case SensorType::HEAD_TRACKER: {
+ Event::EventPayload::HeadTracker headTracker;
+ headTracker.rx = src.head_tracker.rx;
+ headTracker.ry = src.head_tracker.ry;
+ headTracker.rz = src.head_tracker.rz;
+ headTracker.vx = src.head_tracker.vx;
+ headTracker.vy = src.head_tracker.vy;
+ headTracker.vz = src.head_tracker.vz;
+ headTracker.discontinuityCount = src.head_tracker.discontinuity_count;
+
+ dst->payload.set<Event::EventPayload::Tag::headTracker>(headTracker);
+ break;
+ }
+
default: {
CHECK_GE((int32_t)dst->sensorType, (int32_t)SensorType::DEVICE_PRIVATE_BASE);
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 26ac269..af0f524 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -155,9 +155,7 @@
"DisplayHardware/AidlComposerHal.cpp",
"DisplayHardware/HidlComposerHal.cpp",
"DisplayHardware/ComposerHal.cpp",
- "DisplayHardware/DisplayIdentification.cpp",
"DisplayHardware/FramebufferSurface.cpp",
- "DisplayHardware/Hash.cpp",
"DisplayHardware/HWC2.cpp",
"DisplayHardware/HWComposer.cpp",
"DisplayHardware/PowerAdvisor.cpp",
@@ -183,13 +181,13 @@
"RenderArea.cpp",
"Scheduler/DispSyncSource.cpp",
"Scheduler/EventThread.cpp",
+ "Scheduler/FrameRateOverrideMappings.cpp",
"Scheduler/OneShotTimer.cpp",
"Scheduler/LayerHistory.cpp",
"Scheduler/LayerInfo.cpp",
"Scheduler/MessageQueue.cpp",
"Scheduler/RefreshRateConfigs.cpp",
"Scheduler/Scheduler.cpp",
- "Scheduler/Timer.cpp",
"Scheduler/VSyncDispatchTimerQueue.cpp",
"Scheduler/VSyncPredictor.cpp",
"Scheduler/VSyncReactor.cpp",
@@ -227,7 +225,6 @@
"libcutils",
"libdisplayservicehidl",
"libhidlbase",
- "liblayers_proto",
"liblog",
"libprocessgroup",
"libsync",
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 649138a..e797b5d 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -820,6 +820,10 @@
return isFixedSize();
}
+const std::shared_ptr<renderengine::ExternalTexture>& BufferLayer::getExternalTexture() const {
+ return mBufferInfo.mBuffer;
+}
+
} // namespace android
#if defined(__gl_h_)
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 34d11ac..99267be 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -111,6 +111,7 @@
ui::Dataspace getDataSpace() const override;
sp<GraphicBuffer> getBuffer() const override;
+ const std::shared_ptr<renderengine::ExternalTexture>& getExternalTexture() const override;
ui::Transform::RotationFlags getTransformHint() const override { return mTransformHint; }
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 6cecd89..0c93872 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -452,8 +452,8 @@
setFrameTimelineVsyncForBufferTransaction(info, postTime);
- if (buffer && dequeueTime && *dequeueTime != 0) {
- const uint64_t bufferId = buffer->getId();
+ if (dequeueTime && *dequeueTime != 0) {
+ const uint64_t bufferId = mDrawingState.buffer->getId();
mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str());
mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, *dequeueTime,
FrameTracer::FrameEvent::DEQUEUE);
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
index 01dd534..16cb41b 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
@@ -19,7 +19,7 @@
#include <cstdint>
#include <optional>
-#include "DisplayHardware/DisplayIdentification.h"
+#include <ui/DisplayIdentification.h>
#include <compositionengine/Output.h>
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 6cb12dd..d8644a4 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -34,7 +34,7 @@
#include <utils/StrongPointer.h>
#include <utils/Vector.h>
-#include "DisplayHardware/DisplayIdentification.h"
+#include <ui/DisplayIdentification.h>
namespace android {
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index a2824f5..bf5184e 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -28,8 +28,8 @@
#pragma clang diagnostic ignored "-Wconversion"
#pragma clang diagnostic ignored "-Wextra"
+#include <ui/DisplayIdentification.h>
#include "DisplayHardware/ComposerHal.h"
-#include "DisplayHardware/DisplayIdentification.h"
#include "LayerFE.h"
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index 3571e11..b777241 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -26,7 +26,8 @@
#include <ui/PixelFormat.h>
#include <ui/Size.h>
-#include "DisplayHardware/DisplayIdentification.h"
+#include <ui/DisplayIdentification.h>
+
#include "DisplayHardware/HWComposer.h"
#include "DisplayHardware/PowerAdvisor.h"
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index 0082dac..d64d676 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -25,7 +25,7 @@
#include <ui/FloatRect.h>
#include <ui/Rect.h>
-#include "DisplayHardware/DisplayIdentification.h"
+#include <ui/DisplayIdentification.h>
#include <aidl/android/hardware/graphics/composer3/Composition.h>
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
index 08a8b84..6fb3e08 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
@@ -22,8 +22,7 @@
#include <compositionengine/mock/Output.h>
#include <gmock/gmock.h>
#include <system/window.h>
-
-#include "DisplayHardware/DisplayIdentification.h"
+#include <ui/DisplayIdentification.h>
namespace android::compositionengine::mock {
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index d2accaa..0c9063d 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -41,7 +41,7 @@
#include "MainThreadGuard.h"
-#include "DisplayHardware/DisplayIdentification.h"
+#include <ui/DisplayIdentification.h>
#include "DisplayHardware/DisplayMode.h"
#include "DisplayHardware/Hal.h"
#include "DisplayHardware/PowerAdvisor.h"
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
index 3123351..d41a856 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -26,7 +26,7 @@
#include <ui/DisplayId.h>
#include <ui/Size.h>
-#include "DisplayIdentification.h"
+#include <ui/DisplayIdentification.h>
// ---------------------------------------------------------------------------
namespace android {
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 9e57602..93773fa 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -26,6 +26,7 @@
#include <vector>
#include <android-base/thread_annotations.h>
+#include <ui/DisplayIdentification.h>
#include <ui/FenceTime.h>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
@@ -38,7 +39,6 @@
#include <utils/StrongPointer.h>
#include <utils/Timers.h>
-#include "DisplayIdentification.h"
#include "DisplayMode.h"
#include "HWC2.h"
#include "Hal.h"
diff --git a/services/surfaceflinger/DisplayHardware/Hash.cpp b/services/surfaceflinger/DisplayHardware/Hash.cpp
deleted file mode 100644
index 6056c8d..0000000
--- a/services/surfaceflinger/DisplayHardware/Hash.cpp
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright 2021 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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "DisplayIdentification"
-
-#include <cstring>
-#include <type_traits>
-
-#include <log/log.h>
-
-#include "Hash.h"
-
-namespace android {
-namespace {
-
-template <class T>
-inline T load(const void* p) {
- static_assert(std::is_integral<T>::value, "T must be integral");
-
- T r;
- std::memcpy(&r, p, sizeof(r));
- return r;
-}
-
-uint64_t rotateByAtLeast1(uint64_t val, uint8_t shift) {
- return (val >> shift) | (val << (64 - shift));
-}
-
-uint64_t shiftMix(uint64_t val) {
- return val ^ (val >> 47);
-}
-
-uint64_t hash64Len16(uint64_t u, uint64_t v) {
- constexpr uint64_t kMul = 0x9ddfea08eb382d69;
- uint64_t a = (u ^ v) * kMul;
- a ^= (a >> 47);
- uint64_t b = (v ^ a) * kMul;
- b ^= (b >> 47);
- b *= kMul;
- return b;
-}
-
-uint64_t hash64Len0To16(const char* s, uint64_t len) {
- constexpr uint64_t k2 = 0x9ae16a3b2f90404f;
- constexpr uint64_t k3 = 0xc949d7c7509e6557;
-
- if (len > 8) {
- const uint64_t a = load<uint64_t>(s);
- const uint64_t b = load<uint64_t>(s + len - 8);
- return hash64Len16(a, rotateByAtLeast1(b + len, static_cast<uint8_t>(len))) ^ b;
- }
- if (len >= 4) {
- const uint32_t a = load<uint32_t>(s);
- const uint32_t b = load<uint32_t>(s + len - 4);
- return hash64Len16(len + (a << 3), b);
- }
- if (len > 0) {
- const unsigned char a = static_cast<unsigned char>(s[0]);
- const unsigned char b = static_cast<unsigned char>(s[len >> 1]);
- const unsigned char c = static_cast<unsigned char>(s[len - 1]);
- const uint32_t y = static_cast<uint32_t>(a) + (static_cast<uint32_t>(b) << 8);
- const uint32_t z = static_cast<uint32_t>(len) + (static_cast<uint32_t>(c) << 2);
- return shiftMix(y * k2 ^ z * k3) * k2;
- }
- return k2;
-}
-
-} // namespace
-
-uint64_t cityHash64Len0To16(std::string_view sv) {
- auto len = sv.length();
- if (len > 16) {
- ALOGE("%s called with length %zu. Only hashing the first 16 chars", __FUNCTION__, len);
- len = 16;
- }
- return hash64Len0To16(sv.data(), len);
-}
-
-} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/DisplayHardware/Hash.h b/services/surfaceflinger/DisplayHardware/Hash.h
deleted file mode 100644
index a7b6c71..0000000
--- a/services/surfaceflinger/DisplayHardware/Hash.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2021 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 <cstdint>
-#include <string_view>
-
-namespace android {
-
-// CityHash64 implementation that only hashes at most the first 16 characters of the given string.
-uint64_t cityHash64Len0To16(std::string_view sv);
-
-} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index 28d28f4..0db56aa 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -22,8 +22,8 @@
#include <utils/Mutex.h>
+#include <ui/DisplayIdentification.h>
#include "../Scheduler/OneShotTimer.h"
-#include "DisplayIdentification.h"
using namespace std::chrono_literals;
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index 7720713..307da41 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -25,7 +25,7 @@
#include <gui/IGraphicBufferProducer.h>
#include <ui/DisplayId.h>
-#include "DisplayIdentification.h"
+#include <ui/DisplayIdentification.h>
namespace android {
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 5948a78..fa2c92d 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -101,7 +101,9 @@
if (args.flags & ISurfaceComposerClient::eSecure) layerFlags |= layer_state_t::eLayerSecure;
if (args.flags & ISurfaceComposerClient::eSkipScreenshot)
layerFlags |= layer_state_t::eLayerSkipScreenshot;
-
+ if (args.sequence) {
+ sSequence = *args.sequence + 1;
+ }
mDrawingState.flags = layerFlags;
mDrawingState.active_legacy.transform.set(0, 0);
mDrawingState.crop.makeInvalid();
@@ -2022,9 +2024,9 @@
void Layer::writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags,
const DisplayDevice* display) {
const ui::Transform transform = getTransform();
- auto buffer = getBuffer();
+ auto buffer = getExternalTexture();
if (buffer != nullptr) {
- LayerProtoHelper::writeToProto(buffer,
+ LayerProtoHelper::writeToProto(*buffer,
[&]() { return layerInfo->mutable_active_buffer(); });
LayerProtoHelper::writeToProtoDeprecated(ui::Transform(getBufferTransform()),
layerInfo->mutable_buffer_transform());
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 0d2618a..4cdd8fa 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -565,6 +565,9 @@
virtual uint32_t getBufferTransform() const { return 0; }
virtual sp<GraphicBuffer> getBuffer() const { return nullptr; }
+ virtual const std::shared_ptr<renderengine::ExternalTexture>& getExternalTexture() const {
+ return mDrawingState.buffer;
+ };
virtual ui::Transform::RotationFlags getTransformHint() const { return ui::Transform::ROT_0; }
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index ee23561..015caa6 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -154,16 +154,16 @@
}
}
-void LayerProtoHelper::writeToProto(const sp<GraphicBuffer>& buffer,
+void LayerProtoHelper::writeToProto(const renderengine::ExternalTexture& buffer,
std::function<ActiveBufferProto*()> getActiveBufferProto) {
- if (buffer->getWidth() != 0 || buffer->getHeight() != 0 || buffer->getStride() != 0 ||
- buffer->format != 0) {
+ if (buffer.getBuffer()->getWidth() != 0 || buffer.getBuffer()->getHeight() != 0 ||
+ buffer.getBuffer()->getUsage() != 0 || buffer.getBuffer()->getPixelFormat() != 0) {
// Use a lambda do avoid writing the object header when the object is empty
ActiveBufferProto* activeBufferProto = getActiveBufferProto();
- activeBufferProto->set_width(buffer->getWidth());
- activeBufferProto->set_height(buffer->getHeight());
- activeBufferProto->set_stride(buffer->getStride());
- activeBufferProto->set_format(buffer->format);
+ activeBufferProto->set_width(buffer.getBuffer()->getWidth());
+ activeBufferProto->set_height(buffer.getBuffer()->getHeight());
+ activeBufferProto->set_format(buffer.getBuffer()->getPixelFormat());
+ activeBufferProto->set_usage(buffer.getBuffer()->getUsage());
}
}
diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h
index 249ec42..6ade143 100644
--- a/services/surfaceflinger/LayerProtoHelper.h
+++ b/services/surfaceflinger/LayerProtoHelper.h
@@ -15,6 +15,7 @@
*/
#include <layerproto/LayerProtoHeader.h>
+#include <renderengine/ExternalTexture.h>
#include <Layer.h>
#include <gui/WindowInfo.h>
@@ -48,7 +49,7 @@
TransformProto* transformProto);
static void writeTransformToProto(const ui::Transform& transform,
TransformProto* transformProto);
- static void writeToProto(const sp<GraphicBuffer>& buffer,
+ static void writeToProto(const renderengine::ExternalTexture& buffer,
std::function<ActiveBufferProto*()> getActiveBufferProto);
static void writeToProto(const gui::WindowInfo& inputInfo,
const wp<Layer>& touchableRegionBounds,
diff --git a/services/surfaceflinger/Scheduler/Android.bp b/services/surfaceflinger/Scheduler/Android.bp
index 409d098..5de796d 100644
--- a/services/surfaceflinger/Scheduler/Android.bp
+++ b/services/surfaceflinger/Scheduler/Android.bp
@@ -16,6 +16,8 @@
],
shared_libs: [
"libbase",
+ "libcutils",
+ "liblog",
"libutils",
],
}
@@ -26,10 +28,36 @@
export_include_dirs: ["include"],
}
+// TODO(b/185535769): Remove libsurfaceflinger_unittest's dependency on AsyncCallRecorder.
+cc_library_headers {
+ name: "libscheduler_test_headers",
+ defaults: ["libscheduler_defaults"],
+ export_include_dirs: ["tests"],
+}
+
cc_library_static {
name: "libscheduler",
defaults: ["libscheduler_defaults"],
- srcs: [],
+ srcs: [
+ "src/Timer.cpp",
+ ],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
}
+
+cc_test {
+ name: "libscheduler_test",
+ test_suites: ["device-tests"],
+ defaults: ["libscheduler_defaults"],
+ srcs: [
+ "tests/TimerTest.cpp",
+ ],
+ static_libs: [
+ "libgmock",
+ "libgtest",
+ "libscheduler",
+ ],
+ sanitize: {
+ address: true,
+ },
+}
diff --git a/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp b/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp
new file mode 100644
index 0000000..d9d64ae
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2022 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 "FrameRateOverrideMappings.h"
+
+namespace android::scheduler {
+using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
+
+std::optional<Fps> FrameRateOverrideMappings::getFrameRateOverrideForUid(
+ uid_t uid, bool supportsFrameRateOverrideByContent) const {
+ std::lock_guard lock(mFrameRateOverridesLock);
+
+ {
+ const auto iter = mFrameRateOverridesFromBackdoor.find(uid);
+ if (iter != mFrameRateOverridesFromBackdoor.end()) {
+ return iter->second;
+ }
+ }
+
+ {
+ const auto iter = mFrameRateOverridesFromGameManager.find(uid);
+ if (iter != mFrameRateOverridesFromGameManager.end()) {
+ return iter->second;
+ }
+ }
+
+ if (!supportsFrameRateOverrideByContent) {
+ return std::nullopt;
+ }
+
+ {
+ const auto iter = mFrameRateOverridesByContent.find(uid);
+ if (iter != mFrameRateOverridesByContent.end()) {
+ return iter->second;
+ }
+ }
+
+ return std::nullopt;
+}
+
+std::vector<FrameRateOverride> FrameRateOverrideMappings::getAllFrameRateOverrides() {
+ std::lock_guard lock(mFrameRateOverridesLock);
+ std::vector<FrameRateOverride> overrides;
+ overrides.reserve(std::max({mFrameRateOverridesFromGameManager.size(),
+ mFrameRateOverridesFromBackdoor.size(),
+ mFrameRateOverridesByContent.size()}));
+
+ for (const auto& [uid, frameRate] : mFrameRateOverridesFromBackdoor) {
+ overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
+ }
+ for (const auto& [uid, frameRate] : mFrameRateOverridesFromGameManager) {
+ if (std::find_if(overrides.begin(), overrides.end(),
+ [uid = uid](auto i) { return i.uid == uid; }) == overrides.end()) {
+ overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
+ }
+ }
+ for (const auto& [uid, frameRate] : mFrameRateOverridesByContent) {
+ if (std::find_if(overrides.begin(), overrides.end(),
+ [uid = uid](auto i) { return i.uid == uid; }) == overrides.end()) {
+ overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
+ }
+ }
+
+ return overrides;
+}
+
+void FrameRateOverrideMappings::dump(std::string& result) const {
+ using base::StringAppendF;
+
+ std::lock_guard lock(mFrameRateOverridesLock);
+
+ StringAppendF(&result, "Frame Rate Overrides (backdoor): {");
+ for (const auto& [uid, frameRate] : mFrameRateOverridesFromBackdoor) {
+ StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str());
+ }
+ StringAppendF(&result, "}\n");
+
+ StringAppendF(&result, "Frame Rate Overrides (GameManager): {");
+ for (const auto& [uid, frameRate] : mFrameRateOverridesFromGameManager) {
+ StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str());
+ }
+ StringAppendF(&result, "}\n");
+
+ StringAppendF(&result, "Frame Rate Overrides (setFrameRate): {");
+ for (const auto& [uid, frameRate] : mFrameRateOverridesByContent) {
+ StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str());
+ }
+ StringAppendF(&result, "}\n");
+}
+
+bool FrameRateOverrideMappings::updateFrameRateOverridesByContent(
+ const UidToFrameRateOverride& frameRateOverrides) {
+ std::lock_guard lock(mFrameRateOverridesLock);
+ if (!std::equal(mFrameRateOverridesByContent.begin(), mFrameRateOverridesByContent.end(),
+ frameRateOverrides.begin(), frameRateOverrides.end(),
+ [](const auto& lhs, const auto& rhs) {
+ return lhs.first == rhs.first && isApproxEqual(lhs.second, rhs.second);
+ })) {
+ mFrameRateOverridesByContent = frameRateOverrides;
+ return true;
+ }
+ return false;
+}
+
+void FrameRateOverrideMappings::setGameModeRefreshRateForUid(FrameRateOverride frameRateOverride) {
+ std::lock_guard lock(mFrameRateOverridesLock);
+ if (frameRateOverride.frameRateHz != 0.f) {
+ mFrameRateOverridesFromGameManager[frameRateOverride.uid] =
+ Fps::fromValue(frameRateOverride.frameRateHz);
+ } else {
+ mFrameRateOverridesFromGameManager.erase(frameRateOverride.uid);
+ }
+}
+
+void FrameRateOverrideMappings::setPreferredRefreshRateForUid(FrameRateOverride frameRateOverride) {
+ std::lock_guard lock(mFrameRateOverridesLock);
+ if (frameRateOverride.frameRateHz != 0.f) {
+ mFrameRateOverridesFromBackdoor[frameRateOverride.uid] =
+ Fps::fromValue(frameRateOverride.frameRateHz);
+ } else {
+ mFrameRateOverridesFromBackdoor.erase(frameRateOverride.uid);
+ }
+}
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.h b/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.h
new file mode 100644
index 0000000..278f87c
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2022 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 <android-base/thread_annotations.h>
+#include <gui/DisplayEventReceiver.h>
+#include <scheduler/Fps.h>
+#include <sys/types.h>
+#include <map>
+#include <optional>
+
+namespace android::scheduler {
+class FrameRateOverrideMappings {
+ using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
+ using UidToFrameRateOverride = std::map<uid_t, Fps>;
+
+public:
+ std::optional<Fps> getFrameRateOverrideForUid(uid_t uid,
+ bool supportsFrameRateOverrideByContent) const
+ EXCLUDES(mFrameRateOverridesLock);
+ std::vector<FrameRateOverride> getAllFrameRateOverrides() EXCLUDES(mFrameRateOverridesLock);
+ void dump(std::string& result) const;
+ bool updateFrameRateOverridesByContent(const UidToFrameRateOverride& frameRateOverrides)
+ EXCLUDES(mFrameRateOverridesLock);
+ void setGameModeRefreshRateForUid(FrameRateOverride frameRateOverride)
+ EXCLUDES(mFrameRateOverridesLock);
+ void setPreferredRefreshRateForUid(FrameRateOverride frameRateOverride)
+ EXCLUDES(mFrameRateOverridesLock);
+
+private:
+ // The frame rate override lists need their own mutex as they are being read
+ // by SurfaceFlinger, Scheduler and EventThread (as a callback) to prevent deadlocks
+ mutable std::mutex mFrameRateOverridesLock;
+
+ // mappings between a UID and a preferred refresh rate that this app would
+ // run at.
+ UidToFrameRateOverride mFrameRateOverridesByContent GUARDED_BY(mFrameRateOverridesLock);
+ UidToFrameRateOverride mFrameRateOverridesFromBackdoor GUARDED_BY(mFrameRateOverridesLock);
+ UidToFrameRateOverride mFrameRateOverridesFromGameManager GUARDED_BY(mFrameRateOverridesLock);
+};
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 42dc539..3becb5c 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -206,7 +206,7 @@
if (layer.vote == LayerVoteType::ExplicitExact) {
const int divider = getFrameRateDivider(refreshRate.getFps(), layer.desiredRefreshRate);
- if (mSupportsFrameRateOverride) {
+ if (mSupportsFrameRateOverrideByContent) {
// Since we support frame rate override, allow refresh rates which are
// multiples of the layer's request, as those apps would be throttled
// down to run at the desired refresh rate.
@@ -225,7 +225,7 @@
// The layer frame rate is not a divider of the refresh rate,
// there is a small penalty attached to the score to favor the frame rates
// the exactly matches the display refresh rate or a multiple.
- constexpr float kNonExactMatchingPenalty = 0.99f;
+ constexpr float kNonExactMatchingPenalty = 0.95f;
return calculateNonExactMatchingLayerScoreLocked(layer, refreshRate) * seamlessness *
kNonExactMatchingPenalty;
}
@@ -479,7 +479,7 @@
const RefreshRate& touchRefreshRate = getMaxRefreshRateByPolicyLocked(anchorGroup);
const bool touchBoostForExplicitExact = [&] {
- if (mSupportsFrameRateOverride) {
+ if (mSupportsFrameRateOverrideByContent) {
// Enable touch boost if there are other layers besides exact
return explicitExact + noVoteLayers != layers.size();
} else {
@@ -547,7 +547,6 @@
const std::vector<LayerRequirement>& layers, Fps displayFrameRate,
GlobalSignals globalSignals) const {
ATRACE_CALL();
- if (!mSupportsFrameRateOverride) return {};
ALOGV("getFrameRateOverrides %zu layers", layers.size());
std::lock_guard lock(mLock);
@@ -762,12 +761,12 @@
mMinSupportedRefreshRate = sortedModes.front();
mMaxSupportedRefreshRate = sortedModes.back();
- mSupportsFrameRateOverride = false;
+ mSupportsFrameRateOverrideByContent = false;
if (mConfig.enableFrameRateOverride) {
for (const auto& mode1 : sortedModes) {
for (const auto& mode2 : sortedModes) {
if (getFrameRateDivider(mode1->getFps(), mode2->getFps()) >= 2) {
- mSupportsFrameRateOverride = true;
+ mSupportsFrameRateOverrideByContent = true;
break;
}
}
@@ -1006,8 +1005,8 @@
base::StringAppendF(&result, "\t%s\n", refreshRate->toString().c_str());
}
- base::StringAppendF(&result, "Supports Frame Rate Override: %s\n",
- mSupportsFrameRateOverride ? "yes" : "no");
+ base::StringAppendF(&result, "Supports Frame Rate Override By Content: %s\n",
+ mSupportsFrameRateOverrideByContent ? "yes" : "no");
base::StringAppendF(&result, "Idle timer: (%s) %s\n",
mConfig.supportKernelIdleTimer ? "kernel" : "platform",
mIdleTimer ? mIdleTimer->dump().c_str() : "off");
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index de5afb7..849d297 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -327,7 +327,7 @@
// refresh rates.
KernelIdleTimerAction getIdleTimerAction() const;
- bool supportsFrameRateOverride() const { return mSupportsFrameRateOverride; }
+ bool supportsFrameRateOverrideByContent() const { return mSupportsFrameRateOverrideByContent; }
// Return the display refresh rate divider to match the layer
// frame rate, or 0 if the display refresh rate is not a multiple of the
@@ -495,7 +495,7 @@
const std::vector<Fps> mKnownFrameRates;
const Config mConfig;
- bool mSupportsFrameRateOverride;
+ bool mSupportsFrameRateOverrideByContent;
struct GetBestRefreshRateInvocation {
std::vector<LayerRequirement> layerRequirements;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index d4b3917..a85e748 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -42,6 +42,7 @@
#include "../Layer.h"
#include "DispSyncSource.h"
#include "EventThread.h"
+#include "FrameRateOverrideMappings.h"
#include "InjectVSyncSource.h"
#include "OneShotTimer.h"
#include "SurfaceFlingerProperties.h"
@@ -138,29 +139,11 @@
}
std::optional<Fps> Scheduler::getFrameRateOverride(uid_t uid) const {
- {
- std::scoped_lock lock(mRefreshRateConfigsLock);
- if (!mRefreshRateConfigs->supportsFrameRateOverride()) {
- return std::nullopt;
- }
- }
-
- std::lock_guard lock(mFrameRateOverridesLock);
- {
- const auto iter = mFrameRateOverridesFromBackdoor.find(uid);
- if (iter != mFrameRateOverridesFromBackdoor.end()) {
- return iter->second;
- }
- }
-
- {
- const auto iter = mFrameRateOverridesByContent.find(uid);
- if (iter != mFrameRateOverridesByContent.end()) {
- return iter->second;
- }
- }
-
- return std::nullopt;
+ const auto refreshRateConfigs = holdRefreshRateConfigs();
+ const bool supportsFrameRateOverrideByContent =
+ refreshRateConfigs->supportsFrameRateOverrideByContent();
+ return mFrameRateOverrideMappings
+ .getFrameRateOverrideForUid(uid, supportsFrameRateOverrideByContent);
}
bool Scheduler::isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const {
@@ -174,9 +157,6 @@
impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() const {
std::scoped_lock lock(mRefreshRateConfigsLock);
- if (!mRefreshRateConfigs->supportsFrameRateOverride()) {
- return {};
- }
return [this](nsecs_t expectedVsyncTimestamp, uid_t uid) {
return !isVsyncValid(expectedVsyncTimestamp, uid);
@@ -282,18 +262,9 @@
}
void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId) {
- std::vector<FrameRateOverride> overrides;
- {
- std::lock_guard lock(mFrameRateOverridesLock);
- for (const auto& [uid, frameRate] : mFrameRateOverridesFromBackdoor) {
- overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
- }
- for (const auto& [uid, frameRate] : mFrameRateOverridesByContent) {
- if (mFrameRateOverridesFromBackdoor.count(uid) == 0) {
- overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
- }
- }
- }
+ std::vector<FrameRateOverride> overrides =
+ mFrameRateOverrideMappings.getAllFrameRateOverrides();
+
android::EventThread* thread;
{
std::lock_guard lock(mConnectionsLock);
@@ -693,20 +664,7 @@
mFeatures.test(Feature::kContentDetection) ? "on" : "off",
mLayerHistory.dump().c_str());
- {
- std::lock_guard lock(mFrameRateOverridesLock);
- StringAppendF(&result, "Frame Rate Overrides (backdoor): {");
- for (const auto& [uid, frameRate] : mFrameRateOverridesFromBackdoor) {
- StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str());
- }
- StringAppendF(&result, "}\n");
-
- StringAppendF(&result, "Frame Rate Overrides (setFrameRate): {");
- for (const auto& [uid, frameRate] : mFrameRateOverridesByContent) {
- StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str());
- }
- StringAppendF(&result, "}\n");
- }
+ mFrameRateOverrideMappings.dump(result);
{
std::lock_guard lock(mHWVsyncLock);
@@ -723,7 +681,7 @@
bool Scheduler::updateFrameRateOverrides(
scheduler::RefreshRateConfigs::GlobalSignals consideredSignals, Fps displayRefreshRate) {
const auto refreshRateConfigs = holdRefreshRateConfigs();
- if (!refreshRateConfigs->supportsFrameRateOverride()) {
+ if (!refreshRateConfigs->supportsFrameRateOverrideByContent()) {
return false;
}
@@ -731,15 +689,7 @@
const auto frameRateOverrides =
refreshRateConfigs->getFrameRateOverrides(mPolicy.contentRequirements,
displayRefreshRate, consideredSignals);
- std::lock_guard lock(mFrameRateOverridesLock);
- if (!std::equal(mFrameRateOverridesByContent.begin(), mFrameRateOverridesByContent.end(),
- frameRateOverrides.begin(), frameRateOverrides.end(),
- [](const auto& lhs, const auto& rhs) {
- return lhs.first == rhs.first && isApproxEqual(lhs.second, rhs.second);
- })) {
- mFrameRateOverridesByContent = frameRateOverrides;
- return true;
- }
+ return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides);
}
return false;
}
@@ -851,18 +801,20 @@
mLayerHistory.setDisplayArea(displayArea);
}
+void Scheduler::setGameModeRefreshRateForUid(FrameRateOverride frameRateOverride) {
+ if (frameRateOverride.frameRateHz > 0.f && frameRateOverride.frameRateHz < 1.f) {
+ return;
+ }
+
+ mFrameRateOverrideMappings.setGameModeRefreshRateForUid(frameRateOverride);
+}
+
void Scheduler::setPreferredRefreshRateForUid(FrameRateOverride frameRateOverride) {
if (frameRateOverride.frameRateHz > 0.f && frameRateOverride.frameRateHz < 1.f) {
return;
}
- std::lock_guard lock(mFrameRateOverridesLock);
- if (frameRateOverride.frameRateHz != 0.f) {
- mFrameRateOverridesFromBackdoor[frameRateOverride.uid] =
- Fps::fromValue(frameRateOverride.frameRateHz);
- } else {
- mFrameRateOverridesFromBackdoor.erase(frameRateOverride.uid);
- }
+ mFrameRateOverrideMappings.setPreferredRefreshRateForUid(frameRateOverride);
}
std::chrono::steady_clock::time_point Scheduler::getPreviousVsyncFrom(
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 2f3d952..bc9024a 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -35,6 +35,7 @@
#include <scheduler/Features.h>
#include "EventThread.h"
+#include "FrameRateOverrideMappings.h"
#include "LayerHistory.h"
#include "MessageQueue.h"
#include "OneShotTimer.h"
@@ -146,7 +147,7 @@
void onScreenReleased(ConnectionHandle);
void onFrameRateOverridesChanged(ConnectionHandle, PhysicalDisplayId)
- EXCLUDES(mFrameRateOverridesLock) EXCLUDES(mConnectionsLock);
+ EXCLUDES(mConnectionsLock);
// Modifies work duration in the event thread.
void setDuration(ConnectionHandle, std::chrono::nanoseconds workDuration,
@@ -196,8 +197,7 @@
// Returns true if a given vsync timestamp is considered valid vsync
// for a given uid
- bool isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const
- EXCLUDES(mFrameRateOverridesLock);
+ bool isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const;
std::chrono::steady_clock::time_point getPreviousVsyncFrom(nsecs_t expectedPresentTime) const;
@@ -226,10 +226,12 @@
// Stores the preferred refresh rate that an app should run at.
// FrameRateOverride.refreshRateHz == 0 means no preference.
- void setPreferredRefreshRateForUid(FrameRateOverride) EXCLUDES(mFrameRateOverridesLock);
+ void setPreferredRefreshRateForUid(FrameRateOverride);
+
+ void setGameModeRefreshRateForUid(FrameRateOverride);
+
// Retrieves the overridden refresh rate for a given uid.
- std::optional<Fps> getFrameRateOverride(uid_t uid) const
- EXCLUDES(mRefreshRateConfigsLock, mFrameRateOverridesLock);
+ std::optional<Fps> getFrameRateOverride(uid_t uid) const EXCLUDES(mRefreshRateConfigsLock);
nsecs_t getVsyncPeriodFromRefreshRateConfigs() const EXCLUDES(mRefreshRateConfigsLock) {
std::scoped_lock lock(mRefreshRateConfigsLock);
@@ -275,7 +277,7 @@
void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mRefreshRateConfigsLock);
bool updateFrameRateOverrides(RefreshRateConfigs::GlobalSignals, Fps displayRefreshRate)
- REQUIRES(mPolicyLock) EXCLUDES(mFrameRateOverridesLock);
+ REQUIRES(mPolicyLock);
impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const
EXCLUDES(mRefreshRateConfigsLock);
@@ -350,16 +352,7 @@
GUARDED_BY(mVsyncTimelineLock);
static constexpr std::chrono::nanoseconds MAX_VSYNC_APPLIED_TIME = 200ms;
- // The frame rate override lists need their own mutex as they are being read
- // by SurfaceFlinger, Scheduler and EventThread (as a callback) to prevent deadlocks
- mutable std::mutex mFrameRateOverridesLock;
-
- // mappings between a UID and a preferred refresh rate that this app would
- // run at.
- RefreshRateConfigs::UidToFrameRateOverride mFrameRateOverridesByContent
- GUARDED_BY(mFrameRateOverridesLock);
- RefreshRateConfigs::UidToFrameRateOverride mFrameRateOverridesFromBackdoor
- GUARDED_BY(mFrameRateOverridesLock);
+ FrameRateOverrideMappings mFrameRateOverrideMappings;
// Keeps track of whether the screen is acquired for debug
std::atomic<bool> mScreenAcquired = false;
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index b52706f..2bfe204 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -16,17 +16,15 @@
#pragma once
-#include <utils/Log.h>
-#include <utils/Timers.h>
#include <functional>
#include <optional>
#include <string>
+#include <utils/Timers.h>
+
#include "StrongTyping.h"
namespace android::scheduler {
-class TimeKeeper;
-class VSyncTracker;
using ScheduleResult = std::optional<nsecs_t>;
@@ -64,8 +62,7 @@
* invocation of callbackFn.
*
*/
- virtual CallbackToken registerCallback(Callback const& callbackFn,
- std::string callbackName) = 0;
+ virtual CallbackToken registerCallback(Callback, std::string callbackName) = 0;
/*
* Unregisters a callback.
@@ -142,8 +139,9 @@
protected:
VSyncDispatch() = default;
- VSyncDispatch(VSyncDispatch const&) = delete;
- VSyncDispatch& operator=(VSyncDispatch const&) = delete;
+
+ VSyncDispatch(const VSyncDispatch&) = delete;
+ VSyncDispatch& operator=(const VSyncDispatch&) = delete;
};
/*
@@ -152,11 +150,11 @@
*/
class VSyncCallbackRegistration {
public:
- VSyncCallbackRegistration(VSyncDispatch&, VSyncDispatch::Callback const& callbackFn,
- std::string const& callbackName);
+ VSyncCallbackRegistration(VSyncDispatch&, VSyncDispatch::Callback, std::string callbackName);
+ ~VSyncCallbackRegistration();
+
VSyncCallbackRegistration(VSyncCallbackRegistration&&);
VSyncCallbackRegistration& operator=(VSyncCallbackRegistration&&);
- ~VSyncCallbackRegistration();
// See documentation for VSyncDispatch::schedule.
ScheduleResult schedule(VSyncDispatch::ScheduleTiming scheduleTiming);
@@ -165,9 +163,6 @@
CancelResult cancel();
private:
- VSyncCallbackRegistration(VSyncCallbackRegistration const&) = delete;
- VSyncCallbackRegistration& operator=(VSyncCallbackRegistration const&) = delete;
-
std::reference_wrapper<VSyncDispatch> mDispatch;
VSyncDispatch::CallbackToken mToken;
bool mValidToken;
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index c0646ad..27f4311 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -22,7 +22,8 @@
#include <ftl/concat.h>
#include <utils/Trace.h>
-#include "TimeKeeper.h"
+#include <scheduler/TimeKeeper.h>
+
#include "VSyncDispatchTimerQueue.h"
#include "VSyncTracker.h"
@@ -31,6 +32,7 @@
using base::StringAppendF;
namespace {
+
nsecs_t getExpectedCallbackTime(nsecs_t nextVsyncTime,
const VSyncDispatch::ScheduleTiming& timing) {
return nextVsyncTime - timing.readyDuration - timing.workDuration;
@@ -42,17 +44,17 @@
std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration));
return getExpectedCallbackTime(nextVsyncTime, timing);
}
+
} // namespace
VSyncDispatch::~VSyncDispatch() = default;
VSyncTracker::~VSyncTracker() = default;
-TimeKeeper::~TimeKeeper() = default;
-VSyncDispatchTimerQueueEntry::VSyncDispatchTimerQueueEntry(std::string const& name,
- VSyncDispatch::Callback const& cb,
+VSyncDispatchTimerQueueEntry::VSyncDispatchTimerQueueEntry(std::string name,
+ VSyncDispatch::Callback callback,
nsecs_t minVsyncDistance)
- : mName(name),
- mCallback(cb),
+ : mName(std::move(name)),
+ mCallback(std::move(callback)),
mMinVsyncDistance(minVsyncDistance) {}
std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::lastExecutedVsyncTarget() const {
@@ -301,13 +303,13 @@
}
VSyncDispatchTimerQueue::CallbackToken VSyncDispatchTimerQueue::registerCallback(
- Callback const& callbackFn, std::string callbackName) {
+ Callback callback, std::string callbackName) {
std::lock_guard lock(mMutex);
return CallbackToken{
mCallbacks
.emplace(++mCallbackToken,
- std::make_shared<VSyncDispatchTimerQueueEntry>(callbackName,
- callbackFn,
+ std::make_shared<VSyncDispatchTimerQueueEntry>(std::move(callbackName),
+ std::move(callback),
mMinVsyncDistance))
.first->first};
}
@@ -402,10 +404,10 @@
}
VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncDispatch& dispatch,
- VSyncDispatch::Callback const& callbackFn,
- std::string const& callbackName)
+ VSyncDispatch::Callback callback,
+ std::string callbackName)
: mDispatch(dispatch),
- mToken(dispatch.registerCallback(callbackFn, callbackName)),
+ mToken(dispatch.registerCallback(std::move(callback), std::move(callbackName))),
mValidToken(true) {}
VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncCallbackRegistration&& other)
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
index 76e1caf..3186d6d 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -16,7 +16,6 @@
#pragma once
-#include <android-base/thread_annotations.h>
#include <functional>
#include <memory>
#include <mutex>
@@ -24,10 +23,14 @@
#include <string_view>
#include <unordered_map>
+#include <android-base/thread_annotations.h>
+
#include "VSyncDispatch.h"
namespace android::scheduler {
+class VSyncTracker;
+
// VSyncDispatchTimerQueueEntry is a helper class representing internal state for each entry in
// VSyncDispatchTimerQueue hoisted to public for unit testing.
class VSyncDispatchTimerQueueEntry {
@@ -36,7 +39,7 @@
// Valid transition: disarmed -> armed ( when scheduled )
// Valid transition: armed -> running -> disarmed ( when timer is called)
// Valid transition: armed -> disarmed ( when cancelled )
- VSyncDispatchTimerQueueEntry(std::string const& name, VSyncDispatch::Callback const& fn,
+ VSyncDispatchTimerQueueEntry(std::string name, VSyncDispatch::Callback,
nsecs_t minVsyncDistance);
std::string_view name() const;
@@ -45,10 +48,9 @@
std::optional<nsecs_t> lastExecutedVsyncTarget() const;
// This moves the state from disarmed->armed and will calculate the wakeupTime.
- ScheduleResult schedule(VSyncDispatch::ScheduleTiming timing, VSyncTracker& tracker,
- nsecs_t now);
+ ScheduleResult schedule(VSyncDispatch::ScheduleTiming, VSyncTracker&, nsecs_t now);
// This will update armed entries with the latest vsync information. Entry remains armed.
- void update(VSyncTracker& tracker, nsecs_t now);
+ void update(VSyncTracker&, nsecs_t now);
// This will return empty if not armed, or the next calculated wakeup time if armed.
// It will not update the wakeupTime.
@@ -81,11 +83,11 @@
void dump(std::string& result) const;
private:
- std::string const mName;
- VSyncDispatch::Callback const mCallback;
+ const std::string mName;
+ const VSyncDispatch::Callback mCallback;
VSyncDispatch::ScheduleTiming mScheduleTiming;
- nsecs_t const mMinVsyncDistance;
+ const nsecs_t mMinVsyncDistance;
struct ArmingInfo {
nsecs_t mActualWakeupTime;
@@ -115,19 +117,19 @@
// should be grouped into one wakeup.
// \param[in] minVsyncDistance The minimum distance between two vsync estimates before the
// vsyncs are considered the same vsync event.
- explicit VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk, VSyncTracker& tracker,
- nsecs_t timerSlack, nsecs_t minVsyncDistance);
+ VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper>, VSyncTracker&, nsecs_t timerSlack,
+ nsecs_t minVsyncDistance);
~VSyncDispatchTimerQueue();
- CallbackToken registerCallback(Callback const& callbackFn, std::string callbackName) final;
- void unregisterCallback(CallbackToken token) final;
- ScheduleResult schedule(CallbackToken token, ScheduleTiming scheduleTiming) final;
- CancelResult cancel(CallbackToken token) final;
- void dump(std::string& result) const final;
+ CallbackToken registerCallback(Callback, std::string callbackName) final;
+ void unregisterCallback(CallbackToken) final;
+ ScheduleResult schedule(CallbackToken, ScheduleTiming) final;
+ CancelResult cancel(CallbackToken) final;
+ void dump(std::string&) const final;
private:
- VSyncDispatchTimerQueue(VSyncDispatchTimerQueue const&) = delete;
- VSyncDispatchTimerQueue& operator=(VSyncDispatchTimerQueue const&) = delete;
+ VSyncDispatchTimerQueue(const VSyncDispatchTimerQueue&) = delete;
+ VSyncDispatchTimerQueue& operator=(const VSyncDispatchTimerQueue&) = delete;
using CallbackMap =
std::unordered_map<CallbackToken, std::shared_ptr<VSyncDispatchTimerQueueEntry>>;
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 1c9de1c..bdcab51 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -18,21 +18,22 @@
#undef LOG_TAG
#define LOG_TAG "VSyncReactor"
//#define LOG_NDEBUG 0
-#include "VSyncReactor.h"
+
#include <cutils/properties.h>
#include <log/log.h>
#include <utils/Trace.h>
+
#include "../TracedOrdinal.h"
-#include "TimeKeeper.h"
#include "VSyncDispatch.h"
+#include "VSyncReactor.h"
#include "VSyncTracker.h"
namespace android::scheduler {
+
using base::StringAppendF;
VsyncController::~VsyncController() = default;
-Clock::~Clock() = default;
nsecs_t SystemClock::now() const {
return systemTime(SYSTEM_TIME_MONOTONIC);
}
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index a9d536b..6a1950a 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -16,14 +16,18 @@
#pragma once
-#include <android-base/thread_annotations.h>
-#include <ui/FenceTime.h>
#include <memory>
#include <mutex>
#include <unordered_map>
#include <vector>
-#include "TimeKeeper.h"
+
+#include <android-base/thread_annotations.h>
+#include <ui/FenceTime.h>
+
+#include <scheduler/TimeKeeper.h>
+
#include "VsyncController.h"
+
namespace android::scheduler {
class Clock;
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
index 77d1223..e611658 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
@@ -15,10 +15,10 @@
*/
#include <scheduler/Fps.h>
+#include <scheduler/Timer.h>
#include "VsyncSchedule.h"
-#include "Timer.h"
#include "VSyncDispatchTimerQueue.h"
#include "VSyncPredictor.h"
#include "VSyncReactor.h"
diff --git a/services/surfaceflinger/Scheduler/TimeKeeper.h b/services/surfaceflinger/Scheduler/include/scheduler/TimeKeeper.h
similarity index 81%
rename from services/surfaceflinger/Scheduler/TimeKeeper.h
rename to services/surfaceflinger/Scheduler/include/scheduler/TimeKeeper.h
index 40dd841..319390b 100644
--- a/services/surfaceflinger/Scheduler/TimeKeeper.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/TimeKeeper.h
@@ -16,14 +16,17 @@
#pragma once
-#include <utils/Timers.h>
#include <functional>
+#include <string>
+
+#include <utils/Timers.h>
namespace android::scheduler {
class Clock {
public:
virtual ~Clock();
+
/*
* Returns the SYSTEM_TIME_MONOTONIC, used by testing infra to stub time.
*/
@@ -31,8 +34,9 @@
protected:
Clock() = default;
- Clock(Clock const&) = delete;
- Clock& operator=(Clock const&) = delete;
+
+ Clock(const Clock&) = delete;
+ Clock& operator=(const Clock&) = delete;
};
/*
@@ -46,19 +50,20 @@
* Arms callback to fired when time is current based on CLOCK_MONOTONIC
* There is only one timer, and subsequent calls will reset the callback function and the time.
*/
- virtual void alarmAt(std::function<void()> const& callback, nsecs_t time) = 0;
+ virtual void alarmAt(std::function<void()>, nsecs_t time) = 0;
/*
* Cancels an existing pending callback
*/
virtual void alarmCancel() = 0;
- virtual void dump(std::string& result) const = 0;
+ virtual void dump(std::string&) const = 0;
protected:
- TimeKeeper(TimeKeeper const&) = delete;
- TimeKeeper& operator=(TimeKeeper const&) = delete;
TimeKeeper() = default;
+
+ TimeKeeper(const TimeKeeper&) = delete;
+ TimeKeeper& operator=(const TimeKeeper&) = delete;
};
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/Timer.h b/services/surfaceflinger/Scheduler/include/scheduler/Timer.h
similarity index 88%
rename from services/surfaceflinger/Scheduler/Timer.h
rename to services/surfaceflinger/Scheduler/include/scheduler/Timer.h
index eb65954..58ad6cb 100644
--- a/services/surfaceflinger/Scheduler/Timer.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Timer.h
@@ -16,11 +16,14 @@
#pragma once
-#include "TimeKeeper.h"
+#include <array>
+#include <functional>
+#include <mutex>
+#include <thread>
#include <android-base/thread_annotations.h>
-#include <array>
-#include <thread>
+
+#include <scheduler/TimeKeeper.h>
namespace android::scheduler {
@@ -28,13 +31,15 @@
public:
Timer();
~Timer();
+
nsecs_t now() const final;
// NB: alarmAt and alarmCancel are threadsafe; with the last-returning function being effectual
// Most users will want to serialize thes calls so as to be aware of the timer state.
- void alarmAt(std::function<void()> const& cb, nsecs_t time) final;
+ void alarmAt(std::function<void()>, nsecs_t time) final;
void alarmCancel() final;
- void dump(std::string& result) const final;
+
+ void dump(std::string&) const final;
protected:
// For unit testing
@@ -54,7 +59,7 @@
void reset() EXCLUDES(mMutex);
void cleanup() REQUIRES(mMutex);
- void setDebugState(DebugState state) EXCLUDES(mMutex);
+ void setDebugState(DebugState) EXCLUDES(mMutex);
int mTimerFd = -1;
diff --git a/services/surfaceflinger/Scheduler/Timer.cpp b/services/surfaceflinger/Scheduler/src/Timer.cpp
similarity index 95%
rename from services/surfaceflinger/Scheduler/Timer.cpp
rename to services/surfaceflinger/Scheduler/src/Timer.cpp
index 38e277c..a4cf57f 100644
--- a/services/surfaceflinger/Scheduler/Timer.cpp
+++ b/services/surfaceflinger/Scheduler/src/Timer.cpp
@@ -16,7 +16,6 @@
#undef LOG_TAG
#define LOG_TAG "SchedulerTimer"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <chrono>
#include <cstdint>
@@ -30,12 +29,15 @@
#include <log/log.h>
#include <utils/Trace.h>
-#include "Timer.h"
+#include <scheduler/Timer.h>
namespace android::scheduler {
-static constexpr size_t kReadPipe = 0;
-static constexpr size_t kWritePipe = 1;
+constexpr size_t kReadPipe = 0;
+constexpr size_t kWritePipe = 1;
+
+Clock::~Clock() = default;
+TimeKeeper::~TimeKeeper() = default;
Timer::Timer() {
reset();
@@ -104,13 +106,13 @@
return systemTime(SYSTEM_TIME_MONOTONIC);
}
-void Timer::alarmAt(std::function<void()> const& cb, nsecs_t time) {
+void Timer::alarmAt(std::function<void()> callback, nsecs_t time) {
std::lock_guard lock(mMutex);
using namespace std::literals;
static constexpr int ns_per_s =
std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
- mCallback = cb;
+ mCallback = std::move(callback);
mExpectingCallback = true;
struct itimerspec old_timer;
diff --git a/services/surfaceflinger/tests/unittests/AsyncCallRecorder.h b/services/surfaceflinger/Scheduler/tests/AsyncCallRecorder.h
similarity index 98%
rename from services/surfaceflinger/tests/unittests/AsyncCallRecorder.h
rename to services/surfaceflinger/Scheduler/tests/AsyncCallRecorder.h
index 8bed766..57f0dab 100644
--- a/services/surfaceflinger/tests/unittests/AsyncCallRecorder.h
+++ b/services/surfaceflinger/Scheduler/tests/AsyncCallRecorder.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2018 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.
diff --git a/services/surfaceflinger/tests/unittests/TimerTest.cpp b/services/surfaceflinger/Scheduler/tests/TimerTest.cpp
similarity index 84%
rename from services/surfaceflinger/tests/unittests/TimerTest.cpp
rename to services/surfaceflinger/Scheduler/tests/TimerTest.cpp
index 0a3639d..47d968c 100644
--- a/services/surfaceflinger/tests/unittests/TimerTest.cpp
+++ b/services/surfaceflinger/Scheduler/tests/TimerTest.cpp
@@ -14,15 +14,13 @@
* limitations under the License.
*/
-#include "AsyncCallRecorder.h"
-#include "Scheduler/TimeKeeper.h"
-#include "Scheduler/Timer.h"
-
#include <gmock/gmock.h>
#include <gtest/gtest.h>
-using namespace testing;
-using namespace std::literals;
+#include <scheduler/TimeKeeper.h>
+#include <scheduler/Timer.h>
+
+#include "AsyncCallRecorder.h"
namespace android::scheduler {
@@ -35,7 +33,7 @@
};
struct TimerTest : testing::Test {
- static constexpr int mIterations = 20;
+ static constexpr int kIterations = 20;
AsyncCallRecorder<void (*)()> mCallbackRecorder;
TestableTimer mTimer;
@@ -44,17 +42,17 @@
};
TEST_F(TimerTest, callsCallbackIfScheduledInPast) {
- for (int i = 0; i < mIterations; i++) {
- mTimer.alarmAt(std::bind(&TimerTest::timerCallback, this), systemTime() - 10'000'00);
+ for (int i = 0; i < kIterations; i++) {
+ mTimer.alarmAt(std::bind(&TimerTest::timerCallback, this), systemTime() - 1'000'000);
EXPECT_TRUE(mCallbackRecorder.waitForCall().has_value());
EXPECT_FALSE(mCallbackRecorder.waitForUnexpectedCall().has_value());
}
}
TEST_F(TimerTest, recoversAfterEpollError) {
- for (int i = 0; i < mIterations; i++) {
+ for (int i = 0; i < kIterations; i++) {
mTimer.makeEpollError();
- mTimer.alarmAt(std::bind(&TimerTest::timerCallback, this), systemTime() - 10'000'00);
+ mTimer.alarmAt(std::bind(&TimerTest::timerCallback, this), systemTime() - 1'000'000);
EXPECT_TRUE(mCallbackRecorder.waitForCall().has_value());
EXPECT_FALSE(mCallbackRecorder.waitForUnexpectedCall().has_value());
}
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index e07d02f..0a8f9ec 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -95,6 +95,7 @@
#include <type_traits>
#include <unordered_map>
+#include <ui/DisplayIdentification.h>
#include "BackgroundExecutor.h"
#include "BufferLayer.h"
#include "BufferQueueLayer.h"
@@ -104,7 +105,6 @@
#include "ContainerLayer.h"
#include "DisplayDevice.h"
#include "DisplayHardware/ComposerHal.h"
-#include "DisplayHardware/DisplayIdentification.h"
#include "DisplayHardware/FramebufferSurface.h"
#include "DisplayHardware/HWComposer.h"
#include "DisplayHardware/Hal.h"
@@ -505,10 +505,8 @@
enableLatchUnsignaledConfig = getLatchUnsignaledConfig();
- mTransactionTracingEnabled =
- !mIsUserBuild && property_get_bool("debug.sf.enable_transaction_tracing", true);
- if (mTransactionTracingEnabled) {
- mTransactionTracing.enable();
+ if (!mIsUserBuild && base::GetBoolProperty("debug.sf.enable_transaction_tracing"s, true)) {
+ mTransactionTracing.emplace();
}
}
@@ -3773,8 +3771,8 @@
}
}
- if (mTransactionTracingEnabled) {
- mTransactionTracing.addCommittedTransactions(transactions, vsyncId);
+ if (mTransactionTracing) {
+ mTransactionTracing->addCommittedTransactions(transactions, vsyncId);
}
return needsTraversal;
}
@@ -4034,8 +4032,8 @@
mBufferCountTracker.increment(state.surface->localBinder());
});
- if (mTransactionTracingEnabled) {
- mTransactionTracing.addQueuedTransaction(state);
+ if (mTransactionTracing) {
+ mTransactionTracing->addQueuedTransaction(state);
}
queueTransaction(state);
@@ -4571,9 +4569,9 @@
}
*outLayerId = mirrorLayer->sequence;
- if (mTransactionTracingEnabled) {
- mTransactionTracing.onMirrorLayerAdded((*outHandle)->localBinder(), mirrorLayer->sequence,
- args.name, mirrorFrom->sequence);
+ if (mTransactionTracing) {
+ mTransactionTracing->onMirrorLayerAdded((*outHandle)->localBinder(), mirrorLayer->sequence,
+ args.name, mirrorFrom->sequence);
}
return addClientLayer(args.client, *outHandle, mirrorLayer /* layer */, nullptr /* parent */,
false /* addAsRoot */, nullptr /* outTransformHint */);
@@ -4633,9 +4631,9 @@
if (parentSp != nullptr) {
parentId = parentSp->getSequence();
}
- if (mTransactionTracingEnabled) {
- mTransactionTracing.onLayerAdded((*outHandle)->localBinder(), layer->sequence, args.name,
- args.flags, parentId);
+ if (mTransactionTracing) {
+ mTransactionTracing->onLayerAdded((*outHandle)->localBinder(), layer->sequence, args.name,
+ args.flags, parentId);
}
result = addClientLayer(args.client, *outHandle, layer, parent, addToRoot, outTransformHint);
@@ -4725,8 +4723,8 @@
markLayerPendingRemovalLocked(layer);
mBufferCountTracker.remove(handle);
layer.clear();
- if (mTransactionTracingEnabled) {
- mTransactionTracing.onHandleRemoved(handle);
+ if (mTransactionTracing) {
+ mTransactionTracing->onHandleRemoved(handle);
}
}
@@ -4961,7 +4959,9 @@
status_t SurfaceFlinger::dumpCritical(int fd, const DumpArgs&, bool asProto) {
if (asProto) {
mLayerTracing.writeToFile();
- mTransactionTracing.writeToFile();
+ if (mTransactionTracing) {
+ mTransactionTracing->writeToFile();
+ }
}
return doDump(fd, DumpArgs(), asProto);
@@ -5358,9 +5358,15 @@
* Tracing state
*/
mLayerTracing.dump(result);
- result.append("\n");
- mTransactionTracing.dump(result);
- result.append("\n");
+
+ result.append("\nTransaction tracing: ");
+ if (mTransactionTracing) {
+ result.append("enabled\n");
+ mTransactionTracing->dump(result);
+ } else {
+ result.append("disabled\n");
+ }
+ result.push_back('\n');
/*
* HWC layer minidump
@@ -5573,6 +5579,7 @@
}
return PERMISSION_DENIED;
}
+ case SET_OVERRIDE_FRAME_RATE:
case ON_PULL_ATOM: {
const int uid = IPCThreadState::self()->getCallingUid();
if (uid == AID_SYSTEM) {
@@ -6040,15 +6047,17 @@
return NO_ERROR;
}
case 1041: { // Transaction tracing
- if (data.readInt32()) {
- // Transaction tracing is always running but allow the user to temporarily
- // increase the buffer when actively debugging.
- mTransactionTracing.setBufferSize(
- TransactionTracing::ACTIVE_TRACING_BUFFER_SIZE);
- } else {
- mTransactionTracing.setBufferSize(
- TransactionTracing::CONTINUOUS_TRACING_BUFFER_SIZE);
- mTransactionTracing.writeToFile();
+ if (mTransactionTracing) {
+ if (data.readInt32()) {
+ // Transaction tracing is always running but allow the user to temporarily
+ // increase the buffer when actively debugging.
+ mTransactionTracing->setBufferSize(
+ TransactionTracing::ACTIVE_TRACING_BUFFER_SIZE);
+ } else {
+ mTransactionTracing->setBufferSize(
+ TransactionTracing::CONTINUOUS_TRACING_BUFFER_SIZE);
+ mTransactionTracing->writeToFile();
+ }
}
reply->writeInt32(NO_ERROR);
return NO_ERROR;
@@ -6890,8 +6899,8 @@
if (!layer->isRemovedFromCurrentState()) {
mScheduler->deregisterLayer(layer);
}
- if (mTransactionTracingEnabled) {
- mTransactionTracing.onLayerRemoved(layer->getSequence());
+ if (mTransactionTracing) {
+ mTransactionTracing->onLayerRemoved(layer->getSequence());
}
}
@@ -6978,6 +6987,17 @@
return NO_ERROR;
}
+status_t SurfaceFlinger::setOverrideFrameRate(uid_t uid, float frameRate) {
+ PhysicalDisplayId displayId = [&]() {
+ Mutex::Autolock lock(mStateLock);
+ return getDefaultDisplayDeviceLocked()->getPhysicalId();
+ }();
+
+ mScheduler->setGameModeRefreshRateForUid(FrameRateOverride{static_cast<uid_t>(uid), frameRate});
+ mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId);
+ return NO_ERROR;
+}
+
status_t SurfaceFlinger::setFrameTimelineInfo(const sp<IGraphicBufferProducer>& surface,
const FrameTimelineInfo& frameTimelineInfo) {
Mutex::Autolock lock(mStateLock);
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 7349238..8ca9982 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -606,6 +606,8 @@
status_t setFrameTimelineInfo(const sp<IGraphicBufferProducer>& surface,
const FrameTimelineInfo& frameTimelineInfo) override;
+ status_t setOverrideFrameRate(uid_t uid, float frameRate) override;
+
status_t addTransactionTraceListener(
const sp<gui::ITransactionTraceListener>& listener) override;
@@ -1197,8 +1199,7 @@
LayerTracing mLayerTracing{*this};
bool mLayerTracingEnabled = false;
- TransactionTracing mTransactionTracing;
- bool mTransactionTracingEnabled = false;
+ std::optional<TransactionTracing> mTransactionTracing;
std::atomic<bool> mTracingEnabledChanged = false;
const std::shared_ptr<TimeStats> mTimeStats;
diff --git a/services/surfaceflinger/TracedOrdinal.h b/services/surfaceflinger/TracedOrdinal.h
index eee4bec..558b3be 100644
--- a/services/surfaceflinger/TracedOrdinal.h
+++ b/services/surfaceflinger/TracedOrdinal.h
@@ -15,11 +15,14 @@
*/
#pragma once
-#include <android-base/stringprintf.h>
+
+#include <chrono>
+#include <cmath>
+#include <functional>
+#include <string>
+
#include <cutils/compiler.h>
#include <utils/Trace.h>
-#include <cmath>
-#include <string>
namespace std {
template <class Rep, class Period>
@@ -75,7 +78,7 @@
}
if (mNameNegative.empty()) {
- mNameNegative = base::StringPrintf("%sNegative", mName.c_str());
+ mNameNegative = mName + "Negative";
}
if (!std::signbit(mData)) {
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
index 849de22..a91698f 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -31,6 +31,7 @@
proto.set_vsync_id(t.frameTimelineInfo.vsyncId);
proto.set_input_event_id(t.frameTimelineInfo.inputEventId);
proto.set_post_time(t.postTime);
+ proto.set_transaction_id(t.id);
for (auto& layerState : t.states) {
proto.mutable_layer_changes()->Add(std::move(toProto(layerState.state, getLayerId)));
@@ -52,6 +53,9 @@
bufferProto->set_buffer_id(state.bufferId);
bufferProto->set_width(state.bufferWidth);
bufferProto->set_height(state.bufferHeight);
+ bufferProto->set_pixel_format(
+ static_cast<proto::LayerState_BufferData_PixelFormat>(state.pixelFormat));
+ bufferProto->set_usage(state.bufferUsage);
}
layerProto.set_has_sideband_stream(state.hasSidebandStream);
layerProto.set_layer_id(state.layerId);
@@ -136,6 +140,9 @@
bufferProto->set_buffer_id(layer.bufferData->getId());
bufferProto->set_width(layer.bufferData->getWidth());
bufferProto->set_height(layer.bufferData->getHeight());
+ bufferProto->set_pixel_format(static_cast<proto::LayerState_BufferData_PixelFormat>(
+ layer.bufferData->getPixelFormat()));
+ bufferProto->set_usage(layer.bufferData->getUsage());
}
bufferProto->set_frame_number(layer.bufferData->frameNumber);
bufferProto->set_flags(layer.bufferData->flags.get());
@@ -169,6 +176,7 @@
? getLayerId(layer.relativeLayerSurfaceControl->getHandle())
: -1;
proto.set_relative_parent_id(layerId);
+ proto.set_z(layer.z);
}
if (layer.what & layer_state_t::eInputInfoChanged) {
@@ -291,10 +299,13 @@
t.frameTimelineInfo.vsyncId = proto.vsync_id();
t.frameTimelineInfo.inputEventId = proto.input_event_id();
t.postTime = proto.post_time();
+ t.id = proto.transaction_id();
+
int32_t layerCount = proto.layer_changes_size();
t.states.reserve(static_cast<size_t>(layerCount));
for (int i = 0; i < layerCount; i++) {
ComposerState s;
+ s.state.what = 0;
fromProto(proto.layer_changes(i), getLayerHandle, s.state);
t.states.add(s);
}
@@ -316,27 +327,31 @@
outArgs.mirrorFromId = proto.mirror_from_id();
}
-void TransactionProtoParser::fromProto(const proto::LayerState& proto,
- LayerIdToHandleFn getLayerHandle,
- TracingLayerState& outState) {
- fromProto(proto, getLayerHandle, static_cast<layer_state_t&>(outState));
- if (proto.what() & layer_state_t::eReparent) {
+void TransactionProtoParser::mergeFromProto(const proto::LayerState& proto,
+ LayerIdToHandleFn getLayerHandle,
+ TracingLayerState& outState) {
+ layer_state_t state;
+ fromProto(proto, getLayerHandle, state);
+ outState.merge(state);
+
+ if (state.what & layer_state_t::eReparent) {
outState.parentId = proto.parent_id();
- outState.args.parentId = outState.parentId;
}
- if (proto.what() & layer_state_t::eRelativeLayerChanged) {
+ if (state.what & layer_state_t::eRelativeLayerChanged) {
outState.relativeParentId = proto.relative_parent_id();
}
- if (proto.what() & layer_state_t::eInputInfoChanged) {
+ if (state.what & layer_state_t::eInputInfoChanged) {
outState.inputCropId = proto.window_info_handle().crop_layer_id();
}
- if (proto.what() & layer_state_t::eBufferChanged) {
+ if (state.what & layer_state_t::eBufferChanged) {
const proto::LayerState_BufferData& bufferProto = proto.buffer_data();
outState.bufferId = bufferProto.buffer_id();
outState.bufferWidth = bufferProto.width();
outState.bufferHeight = bufferProto.height();
+ outState.pixelFormat = bufferProto.pixel_format();
+ outState.bufferUsage = bufferProto.usage();
}
- if (proto.what() & layer_state_t::eSidebandStreamChanged) {
+ if (state.what & layer_state_t::eSidebandStreamChanged) {
outState.hasSidebandStream = proto.has_sideband_stream();
}
}
@@ -432,15 +447,24 @@
if ((proto.what() & layer_state_t::eReparent) && (getLayerHandle != nullptr)) {
int32_t layerId = proto.parent_id();
- layer.parentSurfaceControlForChild =
- new SurfaceControl(SurfaceComposerClient::getDefault(), getLayerHandle(layerId),
- nullptr, layerId);
+ if (layerId == -1) {
+ layer.parentSurfaceControlForChild = nullptr;
+ } else {
+ layer.parentSurfaceControlForChild =
+ new SurfaceControl(SurfaceComposerClient::getDefault(), getLayerHandle(layerId),
+ nullptr, layerId);
+ }
}
- if ((proto.what() & layer_state_t::eRelativeLayerChanged) && (getLayerHandle != nullptr)) {
+ if (proto.what() & layer_state_t::eRelativeLayerChanged) {
int32_t layerId = proto.relative_parent_id();
- layer.relativeLayerSurfaceControl =
- new SurfaceControl(SurfaceComposerClient::getDefault(), getLayerHandle(layerId),
- nullptr, layerId);
+ if (layerId == -1) {
+ layer.relativeLayerSurfaceControl = nullptr;
+ } else if (getLayerHandle != nullptr) {
+ layer.relativeLayerSurfaceControl =
+ new SurfaceControl(SurfaceComposerClient::getDefault(), getLayerHandle(layerId),
+ nullptr, layerId);
+ }
+ layer.z = proto.z();
}
if ((proto.what() & layer_state_t::eInputInfoChanged) && proto.has_window_info_handle()) {
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.h b/services/surfaceflinger/Tracing/TransactionProtoParser.h
index b78d3d9..d589936 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.h
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.h
@@ -34,6 +34,8 @@
uint64_t bufferId;
uint32_t bufferHeight;
uint32_t bufferWidth;
+ int32_t pixelFormat;
+ uint64_t bufferUsage;
bool hasSidebandStream;
int32_t parentId;
int32_t relativeParentId;
@@ -58,8 +60,8 @@
static TransactionState fromProto(const proto::TransactionState&,
LayerIdToHandleFn getLayerHandleFn,
DisplayIdToHandleFn getDisplayHandleFn);
- static void fromProto(const proto::LayerState&, LayerIdToHandleFn getLayerHandleFn,
- TracingLayerState& outState);
+ static void mergeFromProto(const proto::LayerState&, LayerIdToHandleFn getLayerHandleFn,
+ TracingLayerState& outState);
static void fromProto(const proto::LayerCreationArgs&, TracingLayerCreationArgs& outArgs);
private:
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.cpp b/services/surfaceflinger/Tracing/TransactionTracing.cpp
index b5966d5..5136295 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.cpp
+++ b/services/surfaceflinger/Tracing/TransactionTracing.cpp
@@ -23,35 +23,22 @@
#include <utils/SystemClock.h>
#include <utils/Trace.h>
-#include "RingBuffer.h"
#include "TransactionTracing.h"
namespace android {
TransactionTracing::TransactionTracing() {
- mBuffer = std::make_unique<
- RingBuffer<proto::TransactionTraceFile, proto::TransactionTraceEntry>>();
-}
-
-TransactionTracing::~TransactionTracing() = default;
-
-bool TransactionTracing::enable() {
std::scoped_lock lock(mTraceLock);
- if (mEnabled) {
- return false;
- }
- mBuffer->setSize(mBufferSizeInBytes);
+
+ mBuffer.setSize(mBufferSizeInBytes);
mStartingTimestamp = systemTime();
- mEnabled = true;
{
std::scoped_lock lock(mMainThreadLock);
- mDone = false;
mThread = std::thread(&TransactionTracing::loop, this);
}
- return true;
}
-bool TransactionTracing::disable() {
+TransactionTracing::~TransactionTracing() {
std::thread thread;
{
std::scoped_lock lock(mMainThreadLock);
@@ -63,43 +50,20 @@
thread.join();
}
- std::scoped_lock lock(mTraceLock);
- if (!mEnabled) {
- return false;
- }
- mEnabled = false;
-
- writeToFileLocked();
- mBuffer->reset();
- mQueuedTransactions.clear();
- mStartingStates.clear();
- mLayerHandles.clear();
- return true;
-}
-
-bool TransactionTracing::isEnabled() const {
- std::scoped_lock lock(mTraceLock);
- return mEnabled;
+ writeToFile();
}
status_t TransactionTracing::writeToFile() {
std::scoped_lock lock(mTraceLock);
- if (!mEnabled) {
- return STATUS_OK;
- }
- return writeToFileLocked();
-}
-
-status_t TransactionTracing::writeToFileLocked() {
proto::TransactionTraceFile fileProto = createTraceFileProto();
addStartingStateToProtoLocked(fileProto);
- return mBuffer->writeToFile(fileProto, FILE_NAME);
+ return mBuffer.writeToFile(fileProto, FILE_NAME);
}
void TransactionTracing::setBufferSize(size_t bufferSizeInBytes) {
std::scoped_lock lock(mTraceLock);
mBufferSizeInBytes = bufferSizeInBytes;
- mBuffer->setSize(mBufferSizeInBytes);
+ mBuffer.setSize(mBufferSizeInBytes);
}
proto::TransactionTraceFile TransactionTracing::createTraceFileProto() const {
@@ -111,21 +75,16 @@
void TransactionTracing::dump(std::string& result) const {
std::scoped_lock lock(mTraceLock);
- base::StringAppendF(&result, "Transaction tracing state: %s\n",
- mEnabled ? "enabled" : "disabled");
base::StringAppendF(&result,
" queued transactions=%zu created layers=%zu handles=%zu states=%zu\n",
mQueuedTransactions.size(), mCreatedLayers.size(), mLayerHandles.size(),
mStartingStates.size());
- mBuffer->dump(result);
+ mBuffer.dump(result);
}
void TransactionTracing::addQueuedTransaction(const TransactionState& transaction) {
std::scoped_lock lock(mTraceLock);
ATRACE_CALL();
- if (!mEnabled) {
- return;
- }
mQueuedTransactions[transaction.id] =
TransactionProtoParser::toProto(transaction,
std::bind(&TransactionTracing::getLayerIdLocked, this,
@@ -206,10 +165,17 @@
std::string serializedProto;
entryProto.SerializeToString(&serializedProto);
entryProto.Clear();
- std::vector<std::string> entries = mBuffer->emplace(std::move(serializedProto));
+ std::vector<std::string> entries = mBuffer.emplace(std::move(serializedProto));
removedEntries.reserve(removedEntries.size() + entries.size());
removedEntries.insert(removedEntries.end(), std::make_move_iterator(entries.begin()),
std::make_move_iterator(entries.end()));
+
+ entryProto.mutable_removed_layer_handles()->Reserve(
+ static_cast<int32_t>(mRemovedLayerHandles.size()));
+ for (auto& handle : mRemovedLayerHandles) {
+ entryProto.mutable_removed_layer_handles()->Add(handle);
+ }
+ mRemovedLayerHandles.clear();
}
proto::TransactionTraceEntry removedEntryProto;
@@ -229,10 +195,10 @@
base::ScopedLockAssertion assumeLocked(mTraceLock);
mTransactionsAddedToBufferCv.wait(lock, [&]() REQUIRES(mTraceLock) {
proto::TransactionTraceEntry entry;
- if (mBuffer->used() > 0) {
- entry.ParseFromString(mBuffer->back());
+ if (mBuffer.used() > 0) {
+ entry.ParseFromString(mBuffer.back());
}
- return mBuffer->used() > 0 && entry.vsync_id() >= vsyncId;
+ return mBuffer.used() > 0 && entry.vsync_id() >= vsyncId;
});
}
@@ -267,7 +233,14 @@
void TransactionTracing::onHandleRemoved(BBinder* layerHandle) {
std::scoped_lock lock(mTraceLock);
- mLayerHandles.erase(layerHandle);
+ auto it = mLayerHandles.find(layerHandle);
+ if (it == mLayerHandles.end()) {
+ ALOGW("handle not found. %p", layerHandle);
+ return;
+ }
+
+ mRemovedLayerHandles.push_back(it->second);
+ mLayerHandles.erase(it);
}
void TransactionTracing::tryPushToTracingThread() {
@@ -318,7 +291,7 @@
ALOGW("Could not find layer id %d", layerState.layer_id());
continue;
}
- TransactionProtoParser::fromProto(layerState, nullptr, it->second);
+ TransactionProtoParser::mergeFromProto(layerState, nullptr, it->second);
}
}
@@ -352,7 +325,7 @@
std::scoped_lock<std::mutex> lock(mTraceLock);
proto::TransactionTraceFile proto = createTraceFileProto();
addStartingStateToProtoLocked(proto);
- mBuffer->writeToProto(proto);
+ mBuffer.writeToProto(proto);
return proto;
}
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.h b/services/surfaceflinger/Tracing/TransactionTracing.h
index 26a3758..d5d98ce 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.h
+++ b/services/surfaceflinger/Tracing/TransactionTracing.h
@@ -25,17 +25,16 @@
#include <mutex>
#include <thread>
+#include "RingBuffer.h"
#include "TransactionProtoParser.h"
using namespace android::surfaceflinger;
namespace android {
-template <typename FileProto, typename EntryProto>
-class RingBuffer;
-
class SurfaceFlinger;
class TransactionTracingTest;
+
/*
* Records all committed transactions into a ring bufffer.
*
@@ -54,10 +53,6 @@
TransactionTracing();
~TransactionTracing();
- bool enable();
- bool disable();
- bool isEnabled() const;
-
void addQueuedTransaction(const TransactionState&);
void addCommittedTransactions(std::vector<TransactionState>& transactions, int64_t vsyncId);
status_t writeToFile();
@@ -78,8 +73,7 @@
static constexpr auto FILE_NAME = "/data/misc/wmtrace/transactions_trace.winscope";
mutable std::mutex mTraceLock;
- bool mEnabled GUARDED_BY(mTraceLock) = false;
- std::unique_ptr<RingBuffer<proto::TransactionTraceFile, proto::TransactionTraceEntry>> mBuffer
+ RingBuffer<proto::TransactionTraceFile, proto::TransactionTraceEntry> mBuffer
GUARDED_BY(mTraceLock);
size_t mBufferSizeInBytes GUARDED_BY(mTraceLock) = CONTINUOUS_TRACING_BUFFER_SIZE;
std::unordered_map<uint64_t, proto::TransactionState> mQueuedTransactions
@@ -88,6 +82,7 @@
std::vector<proto::LayerCreationArgs> mCreatedLayers GUARDED_BY(mTraceLock);
std::unordered_map<BBinder* /* layerHandle */, int32_t /* layerId */> mLayerHandles
GUARDED_BY(mTraceLock);
+ std::vector<int32_t /* layerId */> mRemovedLayerHandles GUARDED_BY(mTraceLock);
std::map<int32_t /* layerId */, TracingLayerState> mStartingStates GUARDED_BY(mTraceLock);
// We do not want main thread to block so main thread will try to acquire mMainThreadLock,
@@ -116,7 +111,6 @@
void tryPushToTracingThread() EXCLUDES(mMainThreadLock);
void addStartingStateToProtoLocked(proto::TransactionTraceFile& proto) REQUIRES(mTraceLock);
void updateStartingStateLocked(const proto::TransactionTraceEntry& entry) REQUIRES(mTraceLock);
- status_t writeToFileLocked() REQUIRES(mTraceLock);
// TEST
// Wait until all the committed transactions for the specified vsync id are added to the buffer.
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
index 4529905..2e9e659 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -155,6 +155,7 @@
uint32 height = 2;
uint32 stride = 3;
int32 format = 4;
+ uint64 usage = 5;
}
message BarrierLayerProto {
diff --git a/services/surfaceflinger/layerproto/transactions.proto b/services/surfaceflinger/layerproto/transactions.proto
index 3cb1076..9b076bd 100644
--- a/services/surfaceflinger/layerproto/transactions.proto
+++ b/services/surfaceflinger/layerproto/transactions.proto
@@ -46,6 +46,7 @@
repeated int32 removed_layers = 5;
repeated DisplayState added_displays = 6;
repeated int32 removed_displays = 7;
+ repeated int32 removed_layer_handles = 8;
}
message LayerCreationArgs {
@@ -62,8 +63,9 @@
int64 vsync_id = 3;
int32 input_event_id = 4;
int64 post_time = 5;
- repeated LayerState layer_changes = 6;
- repeated DisplayState display_changes = 7;
+ uint64 transaction_id = 6;
+ repeated LayerState layer_changes = 7;
+ repeated DisplayState display_changes = 8;
}
// Keep insync with layer_state_t
@@ -78,28 +80,35 @@
eLayerChanged = 0x00000002;
eSizeChanged = 0x00000004;
eAlphaChanged = 0x00000008;
+
eMatrixChanged = 0x00000010;
eTransparentRegionChanged = 0x00000020;
eFlagsChanged = 0x00000040;
eLayerStackChanged = 0x00000080;
+
eReleaseBufferListenerChanged = 0x00000400;
eShadowRadiusChanged = 0x00000800;
+
eLayerCreated = 0x00001000;
eBufferCropChanged = 0x00002000;
eRelativeLayerChanged = 0x00004000;
eReparent = 0x00008000;
+
eColorChanged = 0x00010000;
eDestroySurface = 0x00020000;
eTransformChanged = 0x00040000;
eTransformToDisplayInverseChanged = 0x00080000;
+
eCropChanged = 0x00100000;
eBufferChanged = 0x00200000;
eAcquireFenceChanged = 0x00400000;
eDataspaceChanged = 0x00800000;
+
eHdrMetadataChanged = 0x01000000;
eSurfaceDamageRegionChanged = 0x02000000;
eApiChanged = 0x04000000;
eSidebandStreamChanged = 0x08000000;
+
eColorTransformChanged = 0x10000000;
eHasListenerCallbacksChanged = 0x20000000;
eInputInfoChanged = 0x40000000;
@@ -182,6 +191,26 @@
}
uint32 flags = 5;
uint64 cached_buffer_id = 6;
+
+ enum PixelFormat {
+ PIXEL_FORMAT_UNKNOWN = 0;
+ PIXEL_FORMAT_CUSTOM = -4;
+ PIXEL_FORMAT_TRANSLUCENT = -3;
+ PIXEL_FORMAT_TRANSPARENT = -2;
+ PIXEL_FORMAT_OPAQUE = -1;
+ PIXEL_FORMAT_RGBA_8888 = 1;
+ PIXEL_FORMAT_RGBX_8888 = 2;
+ PIXEL_FORMAT_RGB_888 = 3;
+ PIXEL_FORMAT_RGB_565 = 4;
+ PIXEL_FORMAT_BGRA_8888 = 5;
+ PIXEL_FORMAT_RGBA_5551 = 6;
+ PIXEL_FORMAT_RGBA_4444 = 7;
+ PIXEL_FORMAT_RGBA_FP16 = 22;
+ PIXEL_FORMAT_RGBA_1010102 = 43;
+ PIXEL_FORMAT_R_8 = 0x38;
+ }
+ PixelFormat pixel_format = 7;
+ uint64 usage = 8;
}
BufferData buffer_data = 22;
int32 api = 23;
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index c053d43..5af17ec 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -50,7 +50,6 @@
"CachingTest.cpp",
"CompositionTest.cpp",
"DispSyncSourceTest.cpp",
- "DisplayIdentificationTest.cpp",
"DisplayIdGeneratorTest.cpp",
"DisplayTransactionTest.cpp",
"DisplayDevice_GetBestColorModeTest.cpp",
@@ -89,7 +88,6 @@
"RegionSamplingTest.cpp",
"TimeStatsTest.cpp",
"FrameTracerTest.cpp",
- "TimerTest.cpp",
"TransactionApplicationTest.cpp",
"TransactionFrameTracerTest.cpp",
"TransactionProtoParserTest.cpp",
@@ -178,11 +176,12 @@
"server_configurable_flags",
],
header_libs: [
+ "android.hardware.graphics.composer3-command-buffer",
"android.hardware.graphics.composer@2.1-command-buffer",
"android.hardware.graphics.composer@2.2-command-buffer",
"android.hardware.graphics.composer@2.3-command-buffer",
"android.hardware.graphics.composer@2.4-command-buffer",
- "android.hardware.graphics.composer3-command-buffer",
+ "libscheduler_test_headers",
"libsurfaceflinger_headers",
],
}
diff --git a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
index a9ad249..f613e43 100644
--- a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
@@ -37,12 +37,11 @@
class MockVSyncDispatch : public scheduler::VSyncDispatch {
public:
- MOCK_METHOD2(registerCallback,
- CallbackToken(std::function<void(nsecs_t, nsecs_t, nsecs_t)> const&, std::string));
- MOCK_METHOD1(unregisterCallback, void(CallbackToken));
- MOCK_METHOD2(schedule, scheduler::ScheduleResult(CallbackToken, ScheduleTiming));
- MOCK_METHOD1(cancel, scheduler::CancelResult(CallbackToken token));
- MOCK_CONST_METHOD1(dump, void(std::string&));
+ MOCK_METHOD(CallbackToken, registerCallback, (Callback, std::string), (override));
+ MOCK_METHOD(void, unregisterCallback, (CallbackToken), (override));
+ MOCK_METHOD(scheduler::ScheduleResult, schedule, (CallbackToken, ScheduleTiming), (override));
+ MOCK_METHOD(scheduler::CancelResult, cancel, (CallbackToken), (override));
+ MOCK_METHOD(void, dump, (std::string&), (const, override));
MockVSyncDispatch() {
ON_CALL(*this, registerCallback)
diff --git a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.h b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.h
deleted file mode 100644
index 1c8e5cc..0000000
--- a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2018 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 "DisplayHardware/DisplayIdentification.h"
-
-namespace android {
-
-const DisplayIdentificationData& getInternalEdid();
-const DisplayIdentificationData& getExternalEdid();
-const DisplayIdentificationData& getExternalEedid();
-
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/DisplayIdentificationTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayIdentificationTestHelpers.h
new file mode 100644
index 0000000..975bc12
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/DisplayIdentificationTestHelpers.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2022 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 <ui/DisplayIdentification.h>
+
+namespace android {
+
+template <size_t N>
+DisplayIdentificationData asDisplayIdentificationData(const unsigned char (&bytes)[N]) {
+ return DisplayIdentificationData(bytes, bytes + N - 1);
+}
+
+inline const DisplayIdentificationData& getInternalEdid() {
+ static constexpr unsigned char kInternalEdid[] =
+ "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\xa3\x42\x31\x00\x00\x00\x00"
+ "\x00\x15\x01\x03\x80\x1a\x10\x78\x0a\xd3\xe5\x95\x5c\x60\x90\x27"
+ "\x19\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x9e\x1b\x00\xa0\x50\x20\x12\x30\x10\x30"
+ "\x13\x00\x05\xa3\x10\x00\x00\x19\x00\x00\x00\x0f\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x23\x87\x02\x64\x00\x00\x00\x00\xfe\x00\x53"
+ "\x41\x4d\x53\x55\x4e\x47\x0a\x20\x20\x20\x20\x20\x00\x00\x00\xfe"
+ "\x00\x31\x32\x31\x41\x54\x31\x31\x2d\x38\x30\x31\x0a\x20\x00\x45";
+ static const DisplayIdentificationData data = asDisplayIdentificationData(kInternalEdid);
+ return data;
+}
+
+inline const DisplayIdentificationData& getExternalEdid() {
+ static constexpr unsigned char kExternalEdid[] =
+ "\x00\xff\xff\xff\xff\xff\xff\x00\x22\xf0\x6c\x28\x01\x01\x01\x01"
+ "\x02\x16\x01\x04\xb5\x40\x28\x78\xe2\x8d\x85\xad\x4f\x35\xb1\x25"
+ "\x0e\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\xe2\x68\x00\xa0\xa0\x40\x2e\x60\x30\x20"
+ "\x36\x00\x81\x90\x21\x00\x00\x1a\xbc\x1b\x00\xa0\x50\x20\x17\x30"
+ "\x30\x20\x36\x00\x81\x90\x21\x00\x00\x1a\x00\x00\x00\xfc\x00\x48"
+ "\x50\x20\x5a\x52\x33\x30\x77\x0a\x20\x20\x20\x20\x00\x00\x00\xff"
+ "\x00\x43\x4e\x34\x32\x30\x32\x31\x33\x37\x51\x0a\x20\x20\x00\x71";
+ static const DisplayIdentificationData data = asDisplayIdentificationData(kExternalEdid);
+ return data;
+}
+
+inline const DisplayIdentificationData& getExternalEedid() {
+ // Extended EDID with timing extension.
+ static constexpr unsigned char kExternalEedid[] =
+ "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\x2d\xfe\x08\x00\x00\x00\x00"
+ "\x29\x15\x01\x03\x80\x10\x09\x78\x0a\xee\x91\xa3\x54\x4c\x99\x26"
+ "\x0f\x50\x54\xbd\xef\x80\x71\x4f\x81\xc0\x81\x00\x81\x80\x95\x00"
+ "\xa9\xc0\xb3\x00\x01\x01\x02\x3a\x80\x18\x71\x38\x2d\x40\x58\x2c"
+ "\x45\x00\xa0\x5a\x00\x00\x00\x1e\x66\x21\x56\xaa\x51\x00\x1e\x30"
+ "\x46\x8f\x33\x00\xa0\x5a\x00\x00\x00\x1e\x00\x00\x00\xfd\x00\x18"
+ "\x4b\x0f\x51\x17\x00\x0a\x20\x20\x20\x20\x20\x20\x00\x00\x00\xfc"
+ "\x00\x53\x41\x4d\x53\x55\x4e\x47\x0a\x20\x20\x20\x20\x20\x01\x1d"
+ "\x02\x03\x1f\xf1\x47\x90\x04\x05\x03\x20\x22\x07\x23\x09\x07\x07"
+ "\x83\x01\x00\x00\xe2\x00\x0f\x67\x03\x0c\x00\x20\x00\xb8\x2d\x01"
+ "\x1d\x80\x18\x71\x1c\x16\x20\x58\x2c\x25\x00\xa0\x5a\x00\x00\x00"
+ "\x9e\x01\x1d\x00\x72\x51\xd0\x1e\x20\x6e\x28\x55\x00\xa0\x5a\x00"
+ "\x00\x00\x1e\x8c\x0a\xd0\x8a\x20\xe0\x2d\x10\x10\x3e\x96\x00\xa0"
+ "\x5a\x00\x00\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc6";
+ static const DisplayIdentificationData data = asDisplayIdentificationData(kExternalEedid);
+ return data;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index 45eceff..54b8bcb 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -22,7 +22,7 @@
#pragma clang diagnostic ignored "-Wextra"
#include <type_traits>
-#include "DisplayIdentificationTest.h"
+#include "DisplayIdentificationTestHelpers.h"
#include <binder/IPCThreadState.h>
#include <compositionengine/Display.h>
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index 765dec3..2af0480 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -36,7 +36,7 @@
#include "DisplayHardware/DisplayMode.h"
#include "DisplayHardware/HWComposer.h"
#include "DisplayHardware/Hal.h"
-#include "DisplayIdentificationTest.h"
+#include "DisplayIdentificationTestHelpers.h"
#include "mock/DisplayHardware/MockComposer.h"
#include "mock/DisplayHardware/MockHWC2.h"
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index bd4dc59..1dd7dea 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -56,12 +56,11 @@
};
struct MockVSyncDispatch : scheduler::VSyncDispatch {
- MOCK_METHOD2(registerCallback,
- CallbackToken(const std::function<void(nsecs_t, nsecs_t, nsecs_t)>&, std::string));
- MOCK_METHOD1(unregisterCallback, void(CallbackToken));
- MOCK_METHOD2(schedule, scheduler::ScheduleResult(CallbackToken, ScheduleTiming));
- MOCK_METHOD1(cancel, scheduler::CancelResult(CallbackToken token));
- MOCK_CONST_METHOD1(dump, void(std::string&));
+ MOCK_METHOD(CallbackToken, registerCallback, (Callback, std::string), (override));
+ MOCK_METHOD(void, unregisterCallback, (CallbackToken), (override));
+ MOCK_METHOD(scheduler::ScheduleResult, schedule, (CallbackToken, ScheduleTiming), (override));
+ MOCK_METHOD(scheduler::CancelResult, cancel, (CallbackToken token), (override));
+ MOCK_METHOD(void, dump, (std::string&), (const, override));
};
struct MockTokenManager : frametimeline::TokenManager {
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 98746bc..2bafabd 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -1986,6 +1986,38 @@
}
}
+// b/190578904
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_conflictingVotes) {
+ const DisplayModes displayModes = {
+ createDisplayMode(DisplayModeId(0), 0, (43_Hz).getPeriodNsecs()),
+ createDisplayMode(DisplayModeId(1), 0, (53_Hz).getPeriodNsecs()),
+ createDisplayMode(DisplayModeId(2), 0, (55_Hz).getPeriodNsecs()),
+ createDisplayMode(DisplayModeId(3), 0, (60_Hz).getPeriodNsecs()),
+ };
+
+ const RefreshRateConfigs::GlobalSignals globalSignals = {.touch = false, .idle = false};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(displayModes,
+ /*currentConfigId=*/displayModes[0]->getId());
+
+ const auto layers = std::vector<LayerRequirement>{
+ LayerRequirement{
+ .vote = LayerVoteType::ExplicitDefault,
+ .desiredRefreshRate = 43_Hz,
+ .seamlessness = Seamlessness::SeamedAndSeamless,
+ .weight = 0.41f,
+ },
+ LayerRequirement{
+ .vote = LayerVoteType::ExplicitExactOrMultiple,
+ .desiredRefreshRate = 53_Hz,
+ .seamlessness = Seamlessness::SeamedAndSeamless,
+ .weight = 0.41f,
+ },
+ };
+
+ EXPECT_EQ(53_Hz, refreshRateConfigs->getBestRefreshRate(layers, globalSignals).getFps());
+}
+
TEST_F(RefreshRateConfigsTest, testComparisonOperator) {
EXPECT_TRUE(mExpected60Config < mExpected90Config);
EXPECT_FALSE(mExpected60Config < mExpected60Config);
diff --git a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
index 43b09fd..5ac5812 100644
--- a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
@@ -29,79 +29,32 @@
class TransactionTracingTest : public testing::Test {
protected:
static constexpr size_t SMALL_BUFFER_SIZE = 1024;
- std::unique_ptr<android::TransactionTracing> mTracing;
- void SetUp() override { mTracing = std::make_unique<android::TransactionTracing>(); }
+ TransactionTracing mTracing;
- void TearDown() override {
- mTracing->disable();
- mTracing.reset();
- }
+ void flush(int64_t vsyncId) { mTracing.flush(vsyncId); }
+ proto::TransactionTraceFile writeToProto() { return mTracing.writeToProto(); }
- auto getCommittedTransactions() {
- std::scoped_lock<std::mutex> lock(mTracing->mMainThreadLock);
- return mTracing->mCommittedTransactions;
- }
-
- auto getQueuedTransactions() {
- std::scoped_lock<std::mutex> lock(mTracing->mTraceLock);
- return mTracing->mQueuedTransactions;
- }
-
- auto getUsedBufferSize() {
- std::scoped_lock<std::mutex> lock(mTracing->mTraceLock);
- return mTracing->mBuffer->used();
- }
-
- auto flush(int64_t vsyncId) { return mTracing->flush(vsyncId); }
-
- auto bufferFront() {
- std::scoped_lock<std::mutex> lock(mTracing->mTraceLock);
+ proto::TransactionTraceEntry bufferFront() {
+ std::scoped_lock<std::mutex> lock(mTracing.mTraceLock);
proto::TransactionTraceEntry entry;
- entry.ParseFromString(mTracing->mBuffer->front());
+ entry.ParseFromString(mTracing.mBuffer.front());
return entry;
}
- bool threadIsJoinable() {
- std::scoped_lock lock(mTracing->mMainThreadLock);
- return mTracing->mThread.joinable();
- }
-
- proto::TransactionTraceFile writeToProto() { return mTracing->writeToProto(); }
-
- auto getCreatedLayers() {
- std::scoped_lock<std::mutex> lock(mTracing->mTraceLock);
- return mTracing->mCreatedLayers;
- }
-
- auto getStartingStates() {
- std::scoped_lock<std::mutex> lock(mTracing->mTraceLock);
- return mTracing->mStartingStates;
- }
-
void queueAndCommitTransaction(int64_t vsyncId) {
TransactionState transaction;
transaction.id = static_cast<uint64_t>(vsyncId * 3);
transaction.originUid = 1;
transaction.originPid = 2;
- mTracing->addQueuedTransaction(transaction);
+ mTracing.addQueuedTransaction(transaction);
std::vector<TransactionState> transactions;
transactions.emplace_back(transaction);
- mTracing->addCommittedTransactions(transactions, vsyncId);
+ mTracing.addCommittedTransactions(transactions, vsyncId);
flush(vsyncId);
}
- // Test that we clean up the tracing thread and free any memory allocated.
- void verifyDisabledTracingState() {
- EXPECT_FALSE(mTracing->isEnabled());
- EXPECT_FALSE(threadIsJoinable());
- EXPECT_EQ(getCommittedTransactions().size(), 0u);
- EXPECT_EQ(getQueuedTransactions().size(), 0u);
- EXPECT_EQ(getUsedBufferSize(), 0u);
- EXPECT_EQ(getStartingStates().size(), 0u);
- }
-
void verifyEntry(const proto::TransactionTraceEntry& actualProto,
- const std::vector<TransactionState> expectedTransactions,
+ const std::vector<TransactionState>& expectedTransactions,
int64_t expectedVsyncId) {
EXPECT_EQ(actualProto.vsync_id(), expectedVsyncId);
EXPECT_EQ(actualProto.transactions().size(),
@@ -113,16 +66,7 @@
}
};
-TEST_F(TransactionTracingTest, enable) {
- EXPECT_FALSE(mTracing->isEnabled());
- mTracing->enable();
- EXPECT_TRUE(mTracing->isEnabled());
- mTracing->disable();
- verifyDisabledTracingState();
-}
-
TEST_F(TransactionTracingTest, addTransactions) {
- mTracing->enable();
std::vector<TransactionState> transactions;
transactions.reserve(100);
for (uint64_t i = 0; i < 100; i++) {
@@ -130,7 +74,7 @@
transaction.id = i;
transaction.originPid = static_cast<int32_t>(i);
transactions.emplace_back(transaction);
- mTracing->addQueuedTransaction(transaction);
+ mTracing.addQueuedTransaction(transaction);
}
// Split incoming transactions into two and commit them in reverse order to test out of order
@@ -138,12 +82,12 @@
std::vector<TransactionState> firstTransactionSet =
std::vector<TransactionState>(transactions.begin() + 50, transactions.end());
int64_t firstTransactionSetVsyncId = 42;
- mTracing->addCommittedTransactions(firstTransactionSet, firstTransactionSetVsyncId);
+ mTracing.addCommittedTransactions(firstTransactionSet, firstTransactionSetVsyncId);
int64_t secondTransactionSetVsyncId = 43;
std::vector<TransactionState> secondTransactionSet =
std::vector<TransactionState>(transactions.begin(), transactions.begin() + 50);
- mTracing->addCommittedTransactions(secondTransactionSet, secondTransactionSetVsyncId);
+ mTracing.addCommittedTransactions(secondTransactionSet, secondTransactionSetVsyncId);
flush(secondTransactionSetVsyncId);
proto::TransactionTraceFile proto = writeToProto();
@@ -151,24 +95,19 @@
// skip starting entry
verifyEntry(proto.entry(1), firstTransactionSet, firstTransactionSetVsyncId);
verifyEntry(proto.entry(2), secondTransactionSet, secondTransactionSetVsyncId);
-
- mTracing->disable();
- verifyDisabledTracingState();
}
class TransactionTracingLayerHandlingTest : public TransactionTracingTest {
protected:
void SetUp() override {
- TransactionTracingTest::SetUp();
- mTracing->enable();
// add layers
- mTracing->setBufferSize(SMALL_BUFFER_SIZE);
+ mTracing.setBufferSize(SMALL_BUFFER_SIZE);
const sp<IBinder> fakeLayerHandle = new BBinder();
- mTracing->onLayerAdded(fakeLayerHandle->localBinder(), mParentLayerId, "parent",
- 123 /* flags */, -1 /* parentId */);
+ mTracing.onLayerAdded(fakeLayerHandle->localBinder(), mParentLayerId, "parent",
+ 123 /* flags */, -1 /* parentId */);
const sp<IBinder> fakeChildLayerHandle = new BBinder();
- mTracing->onLayerAdded(fakeChildLayerHandle->localBinder(), mChildLayerId, "child",
- 456 /* flags */, mParentLayerId);
+ mTracing.onLayerAdded(fakeChildLayerHandle->localBinder(), mChildLayerId, "child",
+ 456 /* flags */, mParentLayerId);
// add some layer transaction
{
@@ -184,12 +123,12 @@
childState.state.what = layer_state_t::eLayerChanged;
childState.state.z = 43;
transaction.states.add(childState);
- mTracing->addQueuedTransaction(transaction);
+ mTracing.addQueuedTransaction(transaction);
std::vector<TransactionState> transactions;
transactions.emplace_back(transaction);
VSYNC_ID_FIRST_LAYER_CHANGE = ++mVsyncId;
- mTracing->addCommittedTransactions(transactions, VSYNC_ID_FIRST_LAYER_CHANGE);
+ mTracing.addCommittedTransactions(transactions, VSYNC_ID_FIRST_LAYER_CHANGE);
flush(VSYNC_ID_FIRST_LAYER_CHANGE);
}
@@ -204,31 +143,25 @@
layerState.state.z = 41;
layerState.state.x = 22;
transaction.states.add(layerState);
- mTracing->addQueuedTransaction(transaction);
+ mTracing.addQueuedTransaction(transaction);
std::vector<TransactionState> transactions;
transactions.emplace_back(transaction);
VSYNC_ID_SECOND_LAYER_CHANGE = ++mVsyncId;
- mTracing->addCommittedTransactions(transactions, VSYNC_ID_SECOND_LAYER_CHANGE);
+ mTracing.addCommittedTransactions(transactions, VSYNC_ID_SECOND_LAYER_CHANGE);
flush(VSYNC_ID_SECOND_LAYER_CHANGE);
}
// remove child layer
- mTracing->onLayerRemoved(2);
+ mTracing.onLayerRemoved(2);
VSYNC_ID_CHILD_LAYER_REMOVED = ++mVsyncId;
queueAndCommitTransaction(VSYNC_ID_CHILD_LAYER_REMOVED);
// remove layer
- mTracing->onLayerRemoved(1);
+ mTracing.onLayerRemoved(1);
queueAndCommitTransaction(++mVsyncId);
}
- void TearDown() override {
- mTracing->disable();
- verifyDisabledTracingState();
- TransactionTracingTest::TearDown();
- }
-
int mParentLayerId = 1;
int mChildLayerId = 2;
int64_t mVsyncId = 0;
@@ -298,16 +231,14 @@
class TransactionTracingMirrorLayerTest : public TransactionTracingTest {
protected:
void SetUp() override {
- TransactionTracingTest::SetUp();
- mTracing->enable();
// add layers
- mTracing->setBufferSize(SMALL_BUFFER_SIZE);
+ mTracing.setBufferSize(SMALL_BUFFER_SIZE);
const sp<IBinder> fakeLayerHandle = new BBinder();
- mTracing->onLayerAdded(fakeLayerHandle->localBinder(), mLayerId, "Test Layer",
- 123 /* flags */, -1 /* parentId */);
+ mTracing.onLayerAdded(fakeLayerHandle->localBinder(), mLayerId, "Test Layer",
+ 123 /* flags */, -1 /* parentId */);
const sp<IBinder> fakeMirrorLayerHandle = new BBinder();
- mTracing->onMirrorLayerAdded(fakeMirrorLayerHandle->localBinder(), mMirrorLayerId, "Mirror",
- mLayerId);
+ mTracing.onMirrorLayerAdded(fakeMirrorLayerHandle->localBinder(), mMirrorLayerId, "Mirror",
+ mLayerId);
// add some layer transaction
{
@@ -323,21 +254,15 @@
mirrorState.state.what = layer_state_t::eLayerChanged;
mirrorState.state.z = 43;
transaction.states.add(mirrorState);
- mTracing->addQueuedTransaction(transaction);
+ mTracing.addQueuedTransaction(transaction);
std::vector<TransactionState> transactions;
transactions.emplace_back(transaction);
- mTracing->addCommittedTransactions(transactions, ++mVsyncId);
+ mTracing.addCommittedTransactions(transactions, ++mVsyncId);
flush(mVsyncId);
}
}
- void TearDown() override {
- mTracing->disable();
- verifyDisabledTracingState();
- TransactionTracingTest::TearDown();
- }
-
int mLayerId = 5;
int mMirrorLayerId = 55;
int64_t mVsyncId = 0;
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index 42b1993..2da266b 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -14,14 +14,15 @@
* limitations under the License.
*/
-#include "Scheduler/TimeKeeper.h"
-#include "Scheduler/Timer.h"
-#include "Scheduler/VSyncDispatchTimerQueue.h"
-#include "Scheduler/VSyncTracker.h"
+#include <thread>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
-#include <thread>
+
+#include <scheduler/Timer.h>
+
+#include "Scheduler/VSyncDispatchTimerQueue.h"
+#include "Scheduler/VSyncTracker.h"
using namespace testing;
using namespace std::literals;
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index ddc02bf..b7f968d 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -23,16 +23,19 @@
#define LOG_TAG "LibSurfaceFlingerUnittests"
#define LOG_NDEBUG 0
-#include "Scheduler/TimeKeeper.h"
-#include "Scheduler/VSyncDispatchTimerQueue.h"
-#include "Scheduler/VSyncTracker.h"
+#include <thread>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
-#include <thread>
+
+#include <scheduler/TimeKeeper.h>
+
+#include "Scheduler/VSyncDispatchTimerQueue.h"
+#include "Scheduler/VSyncTracker.h"
using namespace testing;
using namespace std::literals;
+
namespace android::scheduler {
class MockVSyncTracker : public VSyncTracker {
@@ -71,10 +74,10 @@
ON_CALL(*this, now()).WillByDefault(Invoke(this, &ControllableClock::fakeTime));
}
- MOCK_CONST_METHOD0(now, nsecs_t());
- MOCK_METHOD2(alarmAt, void(std::function<void()> const&, nsecs_t time));
- MOCK_METHOD0(alarmCancel, void());
- MOCK_CONST_METHOD1(dump, void(std::string&));
+ MOCK_METHOD(nsecs_t, now, (), (const));
+ MOCK_METHOD(void, alarmAt, (std::function<void()>, nsecs_t), (override));
+ MOCK_METHOD(void, alarmCancel, (), (override));
+ MOCK_METHOD(void, dump, (std::string&), (const, override));
void alarmAtDefaultBehavior(std::function<void()> const& callback, nsecs_t time) {
mCallback = callback;
@@ -196,11 +199,14 @@
class TimeKeeperWrapper : public TimeKeeper {
public:
TimeKeeperWrapper(TimeKeeper& control) : mControllableClock(control) {}
- void alarmAt(std::function<void()> const& callback, nsecs_t time) final {
- mControllableClock.alarmAt(callback, time);
- }
- void alarmCancel() final { mControllableClock.alarmCancel(); }
+
nsecs_t now() const final { return mControllableClock.now(); }
+
+ void alarmAt(std::function<void()> callback, nsecs_t time) final {
+ mControllableClock.alarmAt(std::move(callback), time);
+ }
+
+ void alarmCancel() final { mControllableClock.alarmCancel(); }
void dump(std::string&) const final {}
private:
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index 5826a9b..4eb9055 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -22,19 +22,22 @@
#define LOG_TAG "LibSurfaceFlingerUnittests"
#define LOG_NDEBUG 0
-#include "Scheduler/TimeKeeper.h"
-#include "Scheduler/VSyncDispatch.h"
-#include "Scheduler/VSyncReactor.h"
-#include "Scheduler/VSyncTracker.h"
+#include <array>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <ui/Fence.h>
#include <ui/FenceTime.h>
-#include <array>
+
+#include <scheduler/TimeKeeper.h>
+
+#include "Scheduler/VSyncDispatch.h"
+#include "Scheduler/VSyncReactor.h"
+#include "Scheduler/VSyncTracker.h"
using namespace testing;
using namespace std::literals;
+
namespace android::scheduler {
class MockVSyncTracker : public VSyncTracker {
@@ -65,14 +68,12 @@
std::shared_ptr<Clock> const mClock;
};
-class MockVSyncDispatch : public VSyncDispatch {
-public:
- MOCK_METHOD2(registerCallback,
- CallbackToken(std::function<void(nsecs_t, nsecs_t, nsecs_t)> const&, std::string));
- MOCK_METHOD1(unregisterCallback, void(CallbackToken));
- MOCK_METHOD2(schedule, ScheduleResult(CallbackToken, ScheduleTiming));
- MOCK_METHOD1(cancel, CancelResult(CallbackToken token));
- MOCK_CONST_METHOD1(dump, void(std::string&));
+struct MockVSyncDispatch : VSyncDispatch {
+ MOCK_METHOD(CallbackToken, registerCallback, (Callback, std::string), (override));
+ MOCK_METHOD(void, unregisterCallback, (CallbackToken), (override));
+ MOCK_METHOD(ScheduleResult, schedule, (CallbackToken, ScheduleTiming), (override));
+ MOCK_METHOD(CancelResult, cancel, (CallbackToken), (override));
+ MOCK_METHOD(void, dump, (std::string&), (const, override));
};
std::shared_ptr<android::FenceTime> generateInvalidFence() {
@@ -497,4 +498,4 @@
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
+#pragma clang diagnostic pop // ignored "-Wextra"