Merge "Add new methods to PowerHalWrapperAidlTest" into main
diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h
index 37d320a..fba063d 100644
--- a/include/android/performance_hint.h
+++ b/include/android/performance_hint.h
@@ -518,7 +518,7 @@
pipeline is reached.
*/
int ASessionCreationConfig_setGraphicsPipeline(
- ASessionCreationConfig* _Nonnull confi, bool enabled) __INTRODUCED_IN(36);
+ ASessionCreationConfig* _Nonnull config, bool enabled) __INTRODUCED_IN(36);
__END_DECLS
diff --git a/include/ftl/flags.h b/include/ftl/flags.h
index dbe3148..a2a22eb 100644
--- a/include/ftl/flags.h
+++ b/include/ftl/flags.h
@@ -22,6 +22,7 @@
#include <bitset>
#include <cstdint>
#include <iterator>
+#include <initializer_list>
#include <string>
#include <type_traits>
@@ -40,6 +41,7 @@
public:
constexpr Flags(F f) : mFlags(static_cast<U>(f)) {}
+ constexpr Flags(std::initializer_list<F> fs) : mFlags(combine(fs)) {}
constexpr Flags() : mFlags(0) {}
constexpr Flags(const Flags<F>& f) : mFlags(f.mFlags) {}
@@ -197,6 +199,14 @@
private:
U mFlags;
+ static constexpr U combine(std::initializer_list<F> fs) {
+ U result = 0;
+ for (const F f : fs) {
+ result |= static_cast<U>(f);
+ }
+ return result;
+ }
+
static void appendFlag(std::string& str, const std::string_view& flag, bool& first) {
if (first) {
first = false;
diff --git a/include/input/CoordinateFilter.h b/include/input/CoordinateFilter.h
index f36472d..8f2e605 100644
--- a/include/input/CoordinateFilter.h
+++ b/include/input/CoordinateFilter.h
@@ -44,7 +44,7 @@
* the previous call.
* @param coords Coordinates to be overwritten by the corresponding filtered coordinates.
*/
- void filter(std::chrono::duration<float> timestamp, PointerCoords& coords);
+ void filter(std::chrono::nanoseconds timestamp, PointerCoords& coords);
private:
OneEuroFilter mXFilter;
diff --git a/include/input/OneEuroFilter.h b/include/input/OneEuroFilter.h
index a0168e4..bdd82b2 100644
--- a/include/input/OneEuroFilter.h
+++ b/include/input/OneEuroFilter.h
@@ -56,7 +56,7 @@
* provided in the previous call.
* @param rawPosition Position to be filtered.
*/
- float filter(std::chrono::duration<float> timestamp, float rawPosition);
+ float filter(std::chrono::nanoseconds timestamp, float rawPosition);
private:
/**
@@ -67,7 +67,7 @@
/**
* Slope of the cutoff frequency criterion. This is the term scaling the absolute value of the
- * filtered signal's speed. The data member is dimensionless, that is, it does not have units.
+ * filtered signal's speed. Units are 1 / position.
*/
const float mBeta;
@@ -78,9 +78,9 @@
const float mSpeedCutoffFreq;
/**
- * The timestamp from the previous call. Units are seconds.
+ * The timestamp from the previous call.
*/
- std::optional<std::chrono::duration<float>> mPrevTimestamp;
+ std::optional<std::chrono::nanoseconds> mPrevTimestamp;
/**
* The raw position from the previous call.
@@ -88,7 +88,7 @@
std::optional<float> mPrevRawPosition;
/**
- * The filtered velocity from the previous call. Units are position per second.
+ * The filtered velocity from the previous call. Units are position per nanosecond.
*/
std::optional<float> mPrevFilteredVelocity;
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index ab44957..99a9c91 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -67,15 +67,7 @@
"name": "fuzz_service_test"
},
{
- "name": "CtsOsTestCases",
- "options": [
- {
- "include-filter": "android.os.cts.BinderTest"
- },
- {
- "include-filter": "android.os.cts.ParcelTest"
- }
- ]
+ "name": "CtsOsTestCases_ParcelAndBinderTests"
},
{
"name": "libbinder_rs-internal_test"
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 4d691f8..a637165 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -164,96 +164,53 @@
LIBBINDER_NDK35 { # introduced=VanillaIceCream
global:
APersistableBundle_readFromParcel;
- APersistableBundle_readFromParcel; # llndk=202404
APersistableBundle_writeToParcel;
- APersistableBundle_writeToParcel; # llndk=202404
APersistableBundle_new;
- APersistableBundle_new; # llndk=202404
APersistableBundle_dup;
- APersistableBundle_dup; # llndk=202404
APersistableBundle_delete;
- APersistableBundle_delete; # llndk=202404
APersistableBundle_isEqual;
- APersistableBundle_isEqual; # llndk=202404
APersistableBundle_size;
- APersistableBundle_size; # llndk=202404
APersistableBundle_erase;
- APersistableBundle_erase; # llndk=202404
APersistableBundle_putBoolean;
- APersistableBundle_putBoolean; # llndk=202404
APersistableBundle_putInt;
- APersistableBundle_putInt; # llndk=202404
APersistableBundle_putLong;
- APersistableBundle_putLong; # llndk=202404
APersistableBundle_putDouble;
- APersistableBundle_putDouble; # llndk=202404
APersistableBundle_putString;
- APersistableBundle_putString; # llndk=202404
APersistableBundle_putBooleanVector;
- APersistableBundle_putBooleanVector; # llndk=202404
APersistableBundle_putIntVector;
- APersistableBundle_putIntVector; # llndk=202404
APersistableBundle_putLongVector;
- APersistableBundle_putLongVector; # llndk=202404
APersistableBundle_putDoubleVector;
- APersistableBundle_putDoubleVector; # llndk=202404
APersistableBundle_putStringVector;
- APersistableBundle_putStringVector; # llndk=202404
APersistableBundle_putPersistableBundle;
- APersistableBundle_putPersistableBundle; # llndk=202404
APersistableBundle_getBoolean;
- APersistableBundle_getBoolean; # llndk=202404
APersistableBundle_getInt;
- APersistableBundle_getInt; # llndk=202404
APersistableBundle_getLong;
- APersistableBundle_getLong; # llndk=202404
APersistableBundle_getDouble;
- APersistableBundle_getDouble; # llndk=202404
APersistableBundle_getString;
- APersistableBundle_getString; # llndk=202404
APersistableBundle_getBooleanVector;
- APersistableBundle_getBooleanVector; # llndk=202404
APersistableBundle_getIntVector;
- APersistableBundle_getIntVector; # llndk=202404
APersistableBundle_getLongVector;
- APersistableBundle_getLongVector; # llndk=202404
APersistableBundle_getDoubleVector;
- APersistableBundle_getDoubleVector; # llndk=202404
APersistableBundle_getStringVector;
- APersistableBundle_getStringVector; # llndk=202404
APersistableBundle_getPersistableBundle;
- APersistableBundle_getPersistableBundle; # llndk=202404
APersistableBundle_getBooleanKeys;
- APersistableBundle_getBooleanKeys; # llndk=202404
APersistableBundle_getIntKeys;
- APersistableBundle_getIntKeys; # llndk=202404
APersistableBundle_getLongKeys;
- APersistableBundle_getLongKeys; # llndk=202404
APersistableBundle_getDoubleKeys;
- APersistableBundle_getDoubleKeys; # llndk=202404
APersistableBundle_getStringKeys;
- APersistableBundle_getStringKeys; # llndk=202404
APersistableBundle_getBooleanVectorKeys;
- APersistableBundle_getBooleanVectorKeys; # llndk=202404
APersistableBundle_getIntVectorKeys;
- APersistableBundle_getIntVectorKeys; # llndk=202404
APersistableBundle_getLongVectorKeys;
- APersistableBundle_getLongVectorKeys; # llndk=202404
APersistableBundle_getDoubleVectorKeys;
- APersistableBundle_getDoubleVectorKeys; # llndk=202404
APersistableBundle_getStringVectorKeys;
- APersistableBundle_getStringVectorKeys; # llndk=202404
APersistableBundle_getPersistableBundleKeys;
- APersistableBundle_getPersistableBundleKeys; # llndk=202404
- AServiceManager_openDeclaredPassthroughHal; # systemapi llndk=202404
+ AServiceManager_openDeclaredPassthroughHal; # systemapi llndk
};
LIBBINDER_NDK36 { # introduced=36
global:
AIBinder_Class_setTransactionCodeToFunctionNameMap;
- AIBinder_Class_setTransactionCodeToFunctionNameMap; # llndk=202504
AIBinder_Class_getFunctionName;
- AIBinder_Class_getFunctionName; # llndk=202504
ABinderRpc_registerAccessorProvider; # systemapi
ABinderRpc_unregisterAccessorProvider; # systemapi
ABinderRpc_Accessor_new; # systemapi
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 14493db..1b24b0a 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -126,7 +126,7 @@
#[cfg(not(any(trusty, android_ndk)))]
pub use state::{ProcessState, ThreadState};
#[cfg(not(any(android_vendor, android_vndk, android_ndk)))]
-pub use system_only::{delegate_accessor, Accessor, ConnectionInfo};
+pub use system_only::{delegate_accessor, Accessor, AccessorProvider, ConnectionInfo};
/// Binder result containing a [`Status`] on error.
pub type Result<T> = std::result::Result<T, Status>;
diff --git a/libs/binder/rust/src/system_only.rs b/libs/binder/rust/src/system_only.rs
index 9833cbe..3da59ab 100644
--- a/libs/binder/rust/src/system_only.rs
+++ b/libs/binder/rust/src/system_only.rs
@@ -100,17 +100,36 @@
unsafe { SpIBinder::from_raw(sys::ABinderRpc_Accessor_asBinder(self.accessor)) }
}
+ /// Release the underlying ABinderRpc_Accessor pointer for use with the ndk API
+ /// This gives up ownership of the ABinderRpc_Accessor and it is the responsibility of
+ /// the caller to delete it with ABinderRpc_Accessor_delete
+ ///
+ /// # Safety
+ ///
+ /// - The returned `ABinderRpc_Accessor` pointer is now owned by the caller, who must
+ /// call `ABinderRpc_Accessor_delete` to delete the object.
+ /// - This `Accessor` object is now useless after `release` so it can be dropped.
+ unsafe fn release(mut self) -> *mut sys::ABinderRpc_Accessor {
+ if self.accessor.is_null() {
+ log::error!("Attempting to release an Accessor that was already released");
+ return ptr::null_mut();
+ }
+ let ptr = self.accessor;
+ self.accessor = ptr::null_mut();
+ ptr
+ }
+
/// Callback invoked from C++ when the connection info is needed.
///
/// # Safety
///
- /// The `instance` parameter must be a non-null pointer to a valid C string for
- /// CStr::from_ptr. The memory must contain a valid null terminator at the end of
- /// the string within isize::MAX from the pointer. The memory must not be mutated for
- /// the duration of this function call and must be valid for reads from the pointer
- /// to the null terminator.
- /// The `cookie` parameter must be the cookie for an `Arc<F>` and
- /// the caller must hold a ref-count to it.
+ /// - The `instance` parameter must be a non-null pointer to a valid C string for
+ /// CStr::from_ptr. The memory must contain a valid null terminator at the end of
+ /// the string within isize::MAX from the pointer. The memory must not be mutated for
+ /// the duration of this function call and must be valid for reads from the pointer
+ /// to the null terminator.
+ /// - The `cookie` parameter must be the cookie for an `Arc<F>` and
+ /// the caller must hold a ref-count to it.
unsafe extern "C" fn connection_info<F>(
instance: *const c_char,
cookie: *mut c_void,
@@ -172,8 +191,8 @@
///
/// # Safety
///
- /// The `cookie` parameter must be the cookie for an `Arc<F>` and
- /// the owner must give up a ref-count to it.
+ /// - The `cookie` parameter must be the cookie for an `Arc<F>` and
+ /// the owner must give up a ref-count to it.
unsafe extern "C" fn cookie_decr_refcount<F>(cookie: *mut c_void)
where
F: Fn(&str) -> Option<ConnectionInfo> + Send + Sync + 'static,
@@ -185,6 +204,10 @@
impl Drop for Accessor {
fn drop(&mut self) {
+ if self.accessor.is_null() {
+ // This Accessor was already released.
+ return;
+ }
// Safety: `self.accessor` is always a valid, owned
// `ABinderRpc_Accessor` pointer returned by
// `ABinderRpc_Accessor_new` when `self` was created. This delete
@@ -218,3 +241,140 @@
// point, so can be safely passed to `SpIBinder::from_raw`.
Ok(unsafe { SpIBinder::from_raw(delegator).expect("Expected valid binder at this point") })
}
+
+/// Rust wrapper around ABinderRpc_AccessorProvider objects for RPC binder service management.
+///
+/// Dropping the `AccessorProvider` will drop/unregister the underlying object.
+#[derive(Debug)]
+pub struct AccessorProvider {
+ accessor_provider: *mut sys::ABinderRpc_AccessorProvider,
+}
+
+/// Safety: A `AccessorProvider` is a wrapper around `ABinderRpc_AccessorProvider` which is
+/// `Sync` and `Send`. As
+/// `ABinderRpc_AccessorProvider` is threadsafe, this structure is too.
+/// The Fn owned the AccessorProvider has `Sync` and `Send` properties
+unsafe impl Send for AccessorProvider {}
+
+/// Safety: A `AccessorProvider` is a wrapper around `ABinderRpc_AccessorProvider` which is
+/// `Sync` and `Send`. As `ABinderRpc_AccessorProvider` is threadsafe, this structure is too.
+/// The Fn owned the AccessorProvider has `Sync` and `Send` properties
+unsafe impl Sync for AccessorProvider {}
+
+impl AccessorProvider {
+ /// Create a new `AccessorProvider` that will give libbinder `Accessors` in order to
+ /// connect to binder services over sockets.
+ ///
+ /// `instances` is a list of all instances that this `AccessorProvider` is responsible for.
+ /// It is declaring these instances as available to this process and will return
+ /// `Accessor` objects for them when libbinder calls the `provider` callback.
+ /// `provider` is the callback that libbinder will call when a service is being requested.
+ /// The callback takes a `&str` argument representing the service that is being requested.
+ /// See the `ABinderRpc_AccessorProvider_getAccessorCallback` for the C++ equivalent.
+ pub fn new<F>(instances: &[String], provider: F) -> Option<AccessorProvider>
+ where
+ F: Fn(&str) -> Option<Accessor> + Send + Sync + 'static,
+ {
+ let callback: *mut c_void = Arc::into_raw(Arc::new(provider)) as *mut c_void;
+ let c_str_instances: Vec<CString> =
+ instances.iter().map(|s| CString::new(s.as_bytes()).unwrap()).collect();
+ let mut c_instances: Vec<*const c_char> =
+ c_str_instances.iter().map(|s| s.as_ptr()).collect();
+ let num_instances: usize = c_instances.len();
+ // Safety:
+ // - The function pointer for the first argument is a valid `get_accessor` callback.
+ // - This call returns an owned `ABinderRpc_AccessorProvider` pointer which
+ // must be destroyed via `ABinderRpc_unregisterAccessorProvider` when no longer
+ // needed.
+ // - When the underlying ABinderRpc_AccessorProvider is deleted, it will call
+ // the `cookie_decr_refcount` callback on the `callback` pointer to release its
+ // strong ref.
+ // - The `c_instances` vector is not modified by the function
+ let accessor_provider = unsafe {
+ sys::ABinderRpc_registerAccessorProvider(
+ Some(Self::get_accessor::<F>),
+ c_instances.as_mut_ptr(),
+ num_instances,
+ callback,
+ Some(Self::accessor_cookie_decr_refcount::<F>),
+ )
+ };
+
+ if accessor_provider.is_null() {
+ return None;
+ }
+ Some(AccessorProvider { accessor_provider })
+ }
+
+ /// Callback invoked from C++ when an Accessor is needed.
+ ///
+ /// # Safety
+ ///
+ /// - libbinder guarantees the `instance` argument is a valid C string if it's not null.
+ /// - The `cookie` pointer is same pointer that we pass to ABinderRpc_registerAccessorProvider
+ /// in AccessorProvider.new() which is the closure that we will delete with
+ /// self.accessor_cookie_decr_refcount when unregistering the AccessorProvider.
+ unsafe extern "C" fn get_accessor<F>(
+ instance: *const c_char,
+ cookie: *mut c_void,
+ ) -> *mut binder_ndk_sys::ABinderRpc_Accessor
+ where
+ F: Fn(&str) -> Option<Accessor> + Send + Sync + 'static,
+ {
+ if cookie.is_null() || instance.is_null() {
+ log::error!("Cookie({cookie:p}) or instance({instance:p}) is null!");
+ return ptr::null_mut();
+ }
+ // Safety: The caller promises that `cookie` is for an Arc<F>.
+ let callback = unsafe { (cookie as *const F).as_ref().unwrap() };
+
+ let inst = {
+ // Safety: The caller in libbinder_ndk will have already verified this is a valid
+ // C string
+ match unsafe { CStr::from_ptr(instance) }.to_str() {
+ Ok(s) => s,
+ Err(err) => {
+ log::error!("Failed to get a valid C string! {err:?}");
+ return ptr::null_mut();
+ }
+ }
+ };
+
+ match callback(inst) {
+ Some(a) => {
+ // Safety: This is giving up ownership of this ABinderRpc_Accessor
+ // to the caller of this function (libbinder) and it is responsible
+ // for deleting it.
+ unsafe { a.release() }
+ }
+ None => ptr::null_mut(),
+ }
+ }
+
+ /// Callback that decrements the ref-count.
+ /// This is invoked from C++ when the provider is unregistered.
+ ///
+ /// # Safety
+ ///
+ /// - The `cookie` parameter must be the cookie for an `Arc<F>` and
+ /// the owner must give up a ref-count to it.
+ unsafe extern "C" fn accessor_cookie_decr_refcount<F>(cookie: *mut c_void)
+ where
+ F: Fn(&str) -> Option<Accessor> + Send + Sync + 'static,
+ {
+ // Safety: The caller promises that `cookie` is for an Arc<F>.
+ unsafe { Arc::decrement_strong_count(cookie as *const F) };
+ }
+}
+
+impl Drop for AccessorProvider {
+ fn drop(&mut self) {
+ // Safety: `self.accessor_provider` is always a valid, owned
+ // `ABinderRpc_AccessorProvider` pointer returned by
+ // `ABinderRpc_registerAccessorProvider` when `self` was created. This delete
+ // method can only be called once when `self` is dropped.
+ unsafe {
+ sys::ABinderRpc_unregisterAccessorProvider(self.accessor_provider);
+ }
+ }
+}
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index 489fa0a..0e793e5 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -384,8 +384,8 @@
use std::time::Duration;
use binder::{
- Accessor, BinderFeatures, DeathRecipient, FromIBinder, IBinder, Interface, SpIBinder,
- StatusCode, Strong,
+ Accessor, AccessorProvider, 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.
@@ -982,6 +982,62 @@
assert_eq!(delegator_binder, Err(StatusCode::NAME_NOT_FOUND));
}
+ #[test]
+ fn test_accessor_provider_simple() {
+ let instances: Vec<String> = vec!["foo.service".to_owned(), "foo.other_service".to_owned()];
+ let accessor = AccessorProvider::new(&instances, move |_inst: &str| None);
+ assert!(accessor.is_some());
+ }
+
+ #[test]
+ fn test_accessor_provider_no_instance() {
+ let instances: Vec<String> = vec![];
+ let accessor = AccessorProvider::new(&instances, move |_inst: &str| None);
+ assert!(accessor.is_none());
+ }
+
+ #[test]
+ fn test_accessor_provider_double_register() {
+ let instances: Vec<String> = vec!["foo.service".to_owned(), "foo.other_service".to_owned()];
+ let accessor = AccessorProvider::new(&instances, move |_inst: &str| None);
+ assert!(accessor.is_some());
+ let accessor2 = AccessorProvider::new(&instances, move |_inst: &str| None);
+ assert!(accessor2.is_none());
+ }
+
+ #[test]
+ fn test_accessor_provider_register_drop_register() {
+ let instances: Vec<String> = vec!["foo.service".to_owned(), "foo.other_service".to_owned()];
+ {
+ let accessor = AccessorProvider::new(&instances, move |_inst: &str| None);
+ assert!(accessor.is_some());
+ // accessor drops and unregisters the provider
+ }
+ {
+ let accessor = AccessorProvider::new(&instances, move |_inst: &str| None);
+ assert!(accessor.is_some());
+ }
+ }
+
+ #[test]
+ fn test_accessor_provider_callback_destruction() {
+ let deleted: Arc<AtomicBool> = Arc::new(AtomicBool::new(false));
+ let instances: Vec<String> = vec!["foo.service".to_owned(), "foo.other_service".to_owned()];
+ {
+ let accessor: Option<AccessorProvider>;
+ {
+ let helper = ToBeDeleted { deleted: deleted.clone() };
+ accessor = AccessorProvider::new(&instances, move |_inst: &str| {
+ let _ = &helper;
+ None
+ });
+ }
+ assert!(accessor.is_some());
+ assert!(!deleted.load(Ordering::Relaxed));
+ }
+ assert!(deleted.load(Ordering::Relaxed));
+ }
+
#[tokio::test]
async fn reassociate_rust_binder_async() {
let service_name = "testing_service";
diff --git a/libs/ftl/flags_test.cpp b/libs/ftl/flags_test.cpp
index 1279d11..bb43e8d 100644
--- a/libs/ftl/flags_test.cpp
+++ b/libs/ftl/flags_test.cpp
@@ -17,7 +17,7 @@
#include <ftl/flags.h>
#include <gtest/gtest.h>
-#include <type_traits>
+#include <initializer_list>
namespace android::test {
@@ -59,6 +59,18 @@
ASSERT_FALSE(flags.all(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE));
}
+TEST(Flags, ImplicitConstructionAndAssignmentFromInitializerList) {
+ Flags<TestFlags> flags = {TestFlags::ONE, TestFlags::THREE};
+ ASSERT_TRUE(flags.test(TestFlags::ONE));
+ ASSERT_FALSE(flags.test(TestFlags::TWO));
+ ASSERT_TRUE(flags.test(TestFlags::THREE));
+
+ flags = {};
+ ASSERT_FALSE(flags.test(TestFlags::ONE));
+ ASSERT_FALSE(flags.test(TestFlags::TWO));
+ ASSERT_FALSE(flags.test(TestFlags::THREE));
+}
+
TEST(Flags, DefaultConstructor_hasNoFlagsSet) {
Flags<TestFlags> flags;
ASSERT_FALSE(flags.any(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE));
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 7933776..be88b11 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -2873,6 +2873,11 @@
outInfo->hasArrSupport = ginfo.hasArrSupport;
outInfo->frameRateCategoryRate = ui::FrameRateCategoryRate(ginfo.frameRateCategoryRate.normal,
ginfo.frameRateCategoryRate.high);
+ outInfo->supportedRefreshRates.clear();
+ outInfo->supportedRefreshRates.reserve(ginfo.supportedRefreshRates.size());
+ for (const auto rate : ginfo.supportedRefreshRates) {
+ outInfo->supportedRefreshRates.push_back(static_cast<float>(rate));
+ }
}
status_t SurfaceComposerClient::getDynamicDisplayInfoFromId(int64_t displayId,
diff --git a/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl b/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl
index 67cc273..26c12c5 100644
--- a/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl
+++ b/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl
@@ -50,4 +50,7 @@
// Represents frame rate for FrameRateCategory Normal and High.
FrameRateCategoryRate frameRateCategoryRate;
+
+ // All the refresh rates supported for the default display mode.
+ float[] supportedRefreshRates;
}
diff --git a/libs/gui/aidl/android/gui/LutProperties.aidl b/libs/gui/aidl/android/gui/LutProperties.aidl
index 87b878c..84c7013 100644
--- a/libs/gui/aidl/android/gui/LutProperties.aidl
+++ b/libs/gui/aidl/android/gui/LutProperties.aidl
@@ -27,6 +27,6 @@
int size;
@Backing(type="int")
- enum SamplingKey { RGB, MAX_RGB }
+ enum SamplingKey { RGB, MAX_RGB, CIE_Y }
SamplingKey[] samplingKeys;
}
\ No newline at end of file
diff --git a/libs/input/CoordinateFilter.cpp b/libs/input/CoordinateFilter.cpp
index d231474..a32685b 100644
--- a/libs/input/CoordinateFilter.cpp
+++ b/libs/input/CoordinateFilter.cpp
@@ -23,7 +23,7 @@
CoordinateFilter::CoordinateFilter(float minCutoffFreq, float beta)
: mXFilter{minCutoffFreq, beta}, mYFilter{minCutoffFreq, beta} {}
-void CoordinateFilter::filter(std::chrono::duration<float> timestamp, PointerCoords& coords) {
+void CoordinateFilter::filter(std::chrono::nanoseconds timestamp, PointerCoords& coords) {
coords.setAxisValue(AMOTION_EVENT_AXIS_X, mXFilter.filter(timestamp, coords.getX()));
coords.setAxisValue(AMOTION_EVENT_AXIS_Y, mYFilter.filter(timestamp, coords.getY()));
}
diff --git a/libs/input/OneEuroFilter.cpp b/libs/input/OneEuroFilter.cpp
index 400d7c9..7b0d104 100644
--- a/libs/input/OneEuroFilter.cpp
+++ b/libs/input/OneEuroFilter.cpp
@@ -25,16 +25,24 @@
namespace android {
namespace {
+using namespace std::literals::chrono_literals;
+
+const float kHertzPerGigahertz = 1E9f;
+const float kGigahertzPerHertz = 1E-9f;
+
+// filteredSpeed's units are position per nanosecond. beta's units are 1 / position.
inline float cutoffFreq(float minCutoffFreq, float beta, float filteredSpeed) {
- return minCutoffFreq + beta * std::abs(filteredSpeed);
+ return kHertzPerGigahertz *
+ ((minCutoffFreq * kGigahertzPerHertz) + beta * std::abs(filteredSpeed));
}
-inline float smoothingFactor(std::chrono::duration<float> samplingPeriod, float cutoffFreq) {
- return samplingPeriod.count() / (samplingPeriod.count() + (1.0 / (2.0 * M_PI * cutoffFreq)));
+inline float smoothingFactor(std::chrono::nanoseconds samplingPeriod, float cutoffFreq) {
+ const float constant = 2.0f * M_PI * samplingPeriod.count() * (cutoffFreq * kGigahertzPerHertz);
+ return constant / (constant + 1);
}
-inline float lowPassFilter(float rawPosition, float prevFilteredPosition, float smoothingFactor) {
- return smoothingFactor * rawPosition + (1 - smoothingFactor) * prevFilteredPosition;
+inline float lowPassFilter(float rawValue, float prevFilteredValue, float smoothingFactor) {
+ return smoothingFactor * rawValue + (1 - smoothingFactor) * prevFilteredValue;
}
} // namespace
@@ -42,17 +50,17 @@
OneEuroFilter::OneEuroFilter(float minCutoffFreq, float beta, float speedCutoffFreq)
: mMinCutoffFreq{minCutoffFreq}, mBeta{beta}, mSpeedCutoffFreq{speedCutoffFreq} {}
-float OneEuroFilter::filter(std::chrono::duration<float> timestamp, float rawPosition) {
- LOG_IF(FATAL, mPrevFilteredPosition.has_value() && (timestamp <= *mPrevTimestamp))
- << "Timestamp must be greater than mPrevTimestamp";
+float OneEuroFilter::filter(std::chrono::nanoseconds timestamp, float rawPosition) {
+ LOG_IF(FATAL, mPrevTimestamp.has_value() && (*mPrevTimestamp >= timestamp))
+ << "Timestamp must be greater than mPrevTimestamp. Timestamp: " << timestamp.count()
+ << "ns. mPrevTimestamp: " << mPrevTimestamp->count() << "ns";
- const std::chrono::duration<float> samplingPeriod = (mPrevTimestamp.has_value())
- ? (timestamp - *mPrevTimestamp)
- : std::chrono::duration<float>{1.0};
+ const std::chrono::nanoseconds samplingPeriod =
+ (mPrevTimestamp.has_value()) ? (timestamp - *mPrevTimestamp) : 1s;
const float rawVelocity = (mPrevFilteredPosition.has_value())
- ? ((rawPosition - *mPrevFilteredPosition) / samplingPeriod.count())
- : 0.0;
+ ? ((rawPosition - *mPrevFilteredPosition) / (samplingPeriod.count()))
+ : 0.0f;
const float speedSmoothingFactor = smoothingFactor(samplingPeriod, mSpeedCutoffFreq);
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 46e8190..d1c564d 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -17,6 +17,7 @@
"IdGenerator_test.cpp",
"InputChannel_test.cpp",
"InputConsumer_test.cpp",
+ "InputConsumerFilteredResampling_test.cpp",
"InputConsumerResampling_test.cpp",
"InputDevice_test.cpp",
"InputEvent_test.cpp",
diff --git a/libs/input/tests/InputConsumerFilteredResampling_test.cpp b/libs/input/tests/InputConsumerFilteredResampling_test.cpp
new file mode 100644
index 0000000..757cd18
--- /dev/null
+++ b/libs/input/tests/InputConsumerFilteredResampling_test.cpp
@@ -0,0 +1,218 @@
+/**
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <input/InputConsumerNoResampling.h>
+
+#include <chrono>
+#include <iostream>
+#include <memory>
+#include <queue>
+
+#include <TestEventMatchers.h>
+#include <TestInputChannel.h>
+#include <android-base/logging.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <input/Input.h>
+#include <input/InputEventBuilders.h>
+#include <input/Resampler.h>
+#include <utils/Looper.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+namespace {
+
+using std::chrono::nanoseconds;
+
+using ::testing::AllOf;
+using ::testing::Matcher;
+
+const int32_t ACTION_DOWN = AMOTION_EVENT_ACTION_DOWN;
+const int32_t ACTION_MOVE = AMOTION_EVENT_ACTION_MOVE;
+
+struct Pointer {
+ int32_t id{0};
+ ToolType toolType{ToolType::FINGER};
+ float x{0.0f};
+ float y{0.0f};
+ bool isResampled{false};
+
+ PointerBuilder asPointerBuilder() const {
+ return PointerBuilder{id, toolType}.x(x).y(y).isResampled(isResampled);
+ }
+};
+
+} // namespace
+
+class InputConsumerFilteredResamplingTest : public ::testing::Test, public InputConsumerCallbacks {
+protected:
+ InputConsumerFilteredResamplingTest()
+ : mClientTestChannel{std::make_shared<TestInputChannel>("TestChannel")},
+ mLooper{sp<Looper>::make(/*allowNonCallbacks=*/false)} {
+ Looper::setForThread(mLooper);
+ mConsumer = std::make_unique<
+ InputConsumerNoResampling>(mClientTestChannel, mLooper, *this, []() {
+ return std::make_unique<FilteredLegacyResampler>(/*minCutoffFreq=*/4.7, /*beta=*/0.01);
+ });
+ }
+
+ void invokeLooperCallback() const {
+ sp<LooperCallback> callback;
+ ASSERT_TRUE(mLooper->getFdStateDebug(mClientTestChannel->getFd(), /*ident=*/nullptr,
+ /*events=*/nullptr, &callback, /*data=*/nullptr));
+ ASSERT_NE(callback, nullptr);
+ callback->handleEvent(mClientTestChannel->getFd(), ALOOPER_EVENT_INPUT, /*data=*/nullptr);
+ }
+
+ void assertOnBatchedInputEventPendingWasCalled() {
+ ASSERT_GT(mOnBatchedInputEventPendingInvocationCount, 0UL)
+ << "onBatchedInputEventPending was not called";
+ --mOnBatchedInputEventPendingInvocationCount;
+ }
+
+ void assertReceivedMotionEvent(const Matcher<MotionEvent>& matcher) {
+ ASSERT_TRUE(!mMotionEvents.empty()) << "No motion events were received";
+ std::unique_ptr<MotionEvent> motionEvent = std::move(mMotionEvents.front());
+ mMotionEvents.pop();
+ ASSERT_NE(motionEvent, nullptr) << "The consumed motion event must not be nullptr";
+ EXPECT_THAT(*motionEvent, matcher);
+ }
+
+ InputMessage nextPointerMessage(nanoseconds eventTime, int32_t action, const Pointer& pointer);
+
+ std::shared_ptr<TestInputChannel> mClientTestChannel;
+ sp<Looper> mLooper;
+ std::unique_ptr<InputConsumerNoResampling> mConsumer;
+
+ // Batched input events
+ std::queue<std::unique_ptr<KeyEvent>> mKeyEvents;
+ std::queue<std::unique_ptr<MotionEvent>> mMotionEvents;
+ std::queue<std::unique_ptr<FocusEvent>> mFocusEvents;
+ std::queue<std::unique_ptr<CaptureEvent>> mCaptureEvents;
+ std::queue<std::unique_ptr<DragEvent>> mDragEvents;
+ std::queue<std::unique_ptr<TouchModeEvent>> mTouchModeEvents;
+
+private:
+ // InputConsumer callbacks
+ void onKeyEvent(std::unique_ptr<KeyEvent> event, uint32_t seq) override {
+ mKeyEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, /*handled=*/true);
+ }
+
+ void onMotionEvent(std::unique_ptr<MotionEvent> event, uint32_t seq) override {
+ mMotionEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, /*handled=*/true);
+ }
+
+ void onBatchedInputEventPending(int32_t pendingBatchSource) override {
+ if (!mConsumer->probablyHasInput()) {
+ ADD_FAILURE() << "Should deterministically have input because there is a batch";
+ }
+ ++mOnBatchedInputEventPendingInvocationCount;
+ }
+
+ void onFocusEvent(std::unique_ptr<FocusEvent> event, uint32_t seq) override {
+ mFocusEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, /*handled=*/true);
+ }
+
+ void onCaptureEvent(std::unique_ptr<CaptureEvent> event, uint32_t seq) override {
+ mCaptureEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, /*handled=*/true);
+ }
+
+ void onDragEvent(std::unique_ptr<DragEvent> event, uint32_t seq) override {
+ mDragEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, /*handled=*/true);
+ }
+
+ void onTouchModeEvent(std::unique_ptr<TouchModeEvent> event, uint32_t seq) override {
+ mTouchModeEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, /*handled=*/true);
+ }
+
+ uint32_t mLastSeq{0};
+ size_t mOnBatchedInputEventPendingInvocationCount{0};
+};
+
+InputMessage InputConsumerFilteredResamplingTest::nextPointerMessage(nanoseconds eventTime,
+ int32_t action,
+ const Pointer& pointer) {
+ ++mLastSeq;
+ return InputMessageBuilder{InputMessage::Type::MOTION, mLastSeq}
+ .eventTime(eventTime.count())
+ .source(AINPUT_SOURCE_TOUCHSCREEN)
+ .action(action)
+ .pointer(pointer.asPointerBuilder())
+ .build();
+}
+
+TEST_F(InputConsumerFilteredResamplingTest, NeighboringTimestampsDoNotResultInZeroDivision) {
+ mClientTestChannel->enqueueMessage(
+ nextPointerMessage(0ms, ACTION_DOWN, Pointer{.x = 0.0f, .y = 0.0f}));
+
+ invokeLooperCallback();
+
+ assertReceivedMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithSampleCount(1)));
+
+ const std::chrono::nanoseconds initialTime{56'821'700'000'000};
+
+ mClientTestChannel->enqueueMessage(nextPointerMessage(initialTime + 4'929'000ns, ACTION_MOVE,
+ Pointer{.x = 1.0f, .y = 1.0f}));
+ mClientTestChannel->enqueueMessage(nextPointerMessage(initialTime + 9'352'000ns, ACTION_MOVE,
+ Pointer{.x = 2.0f, .y = 2.0f}));
+ mClientTestChannel->enqueueMessage(nextPointerMessage(initialTime + 14'531'000ns, ACTION_MOVE,
+ Pointer{.x = 3.0f, .y = 3.0f}));
+
+ invokeLooperCallback();
+ mConsumer->consumeBatchedInputEvents(initialTime.count() + 18'849'395 /*ns*/);
+
+ assertOnBatchedInputEventPendingWasCalled();
+ // Three samples are expected. The first two of the batch, and the resampled one. The
+ // coordinates of the resampled sample are hardcoded because the matcher requires them. However,
+ // the primary intention here is to check that the last sample is resampled.
+ assertReceivedMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithSampleCount(3),
+ WithSample(/*sampleIndex=*/2,
+ Sample{initialTime + 13'849'395ns,
+ {PointerArgs{.x = 1.3286f,
+ .y = 1.3286f,
+ .isResampled = true}}})));
+
+ mClientTestChannel->enqueueMessage(nextPointerMessage(initialTime + 20'363'000ns, ACTION_MOVE,
+ Pointer{.x = 4.0f, .y = 4.0f}));
+ mClientTestChannel->enqueueMessage(nextPointerMessage(initialTime + 25'745'000ns, ACTION_MOVE,
+ Pointer{.x = 5.0f, .y = 5.0f}));
+ // This sample is part of the stream of messages, but should not be consumed because its
+ // timestamp is greater than the ajusted frame time.
+ mClientTestChannel->enqueueMessage(nextPointerMessage(initialTime + 31'337'000ns, ACTION_MOVE,
+ Pointer{.x = 6.0f, .y = 6.0f}));
+
+ invokeLooperCallback();
+ mConsumer->consumeBatchedInputEvents(initialTime.count() + 35'516'062 /*ns*/);
+
+ assertOnBatchedInputEventPendingWasCalled();
+ // Four samples are expected because the last sample of the previous batch was not consumed.
+ assertReceivedMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithSampleCount(4)));
+
+ mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/4, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/5, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/6, /*handled=*/true);
+}
+
+} // namespace android
diff --git a/libs/input/tests/OneEuroFilter_test.cpp b/libs/input/tests/OneEuroFilter_test.cpp
index 270e789..8645508 100644
--- a/libs/input/tests/OneEuroFilter_test.cpp
+++ b/libs/input/tests/OneEuroFilter_test.cpp
@@ -98,7 +98,10 @@
std::vector<Sample> filteredSignal;
for (const Sample& sample : signal) {
filteredSignal.push_back(
- Sample{sample.timestamp, mFilter.filter(sample.timestamp, sample.value)});
+ Sample{sample.timestamp,
+ mFilter.filter(std::chrono::duration_cast<std::chrono::nanoseconds>(
+ sample.timestamp),
+ sample.value)});
}
return filteredSignal;
}
diff --git a/libs/input/tests/TestEventMatchers.h b/libs/input/tests/TestEventMatchers.h
index 290a97d..56eaefd 100644
--- a/libs/input/tests/TestEventMatchers.h
+++ b/libs/input/tests/TestEventMatchers.h
@@ -17,6 +17,7 @@
#pragma once
#include <chrono>
+#include <cmath>
#include <ostream>
#include <vector>
@@ -156,14 +157,18 @@
++pointerIndex) {
const PointerCoords& pointerCoords =
*(motionEvent.getHistoricalRawPointerCoords(pointerIndex, mSampleIndex));
- if ((pointerCoords.getX() != mSample.pointers[pointerIndex].x) ||
- (pointerCoords.getY() != mSample.pointers[pointerIndex].y)) {
+
+ if ((std::abs(pointerCoords.getX() - mSample.pointers[pointerIndex].x) >
+ MotionEvent::ROUNDING_PRECISION) ||
+ (std::abs(pointerCoords.getY() - mSample.pointers[pointerIndex].y) >
+ MotionEvent::ROUNDING_PRECISION)) {
*os << "sample coordinates mismatch at pointer index " << pointerIndex
<< ". sample: (" << pointerCoords.getX() << ", " << pointerCoords.getY()
<< ") expected: (" << mSample.pointers[pointerIndex].x << ", "
<< mSample.pointers[pointerIndex].y << ")";
return false;
}
+
if (motionEvent.isResampled(pointerIndex, mSampleIndex) !=
mSample.pointers[pointerIndex].isResampled) {
*os << "resampling flag mismatch. sample: "
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index 8f23249..b3284e4 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -545,6 +545,7 @@
if (graphicBuffer && parameters.layer.luts) {
shader = mLutShader.lutShader(shader, parameters.layer.luts,
+ parameters.layer.sourceDataspace,
toSkColorSpace(parameters.outputDataSpace));
}
diff --git a/libs/renderengine/skia/filters/LutShader.cpp b/libs/renderengine/skia/filters/LutShader.cpp
index 1e43ff3..5e9dfbb 100644
--- a/libs/renderengine/skia/filters/LutShader.cpp
+++ b/libs/renderengine/skia/filters/LutShader.cpp
@@ -15,11 +15,13 @@
*/
#include "LutShader.h"
+#include <SkM44.h>
#include <SkTileMode.h>
#include <common/trace.h>
#include <cutils/ashmem.h>
#include <math/half.h>
#include <sys/mman.h>
+#include <ui/ColorSpace.h>
#include "include/core/SkColorSpace.h"
#include "src/core/SkColorFilterPriv.h"
@@ -36,6 +38,8 @@
uniform int size;
uniform int key;
uniform int dimension;
+ uniform vec3 luminanceCoefficients; // for CIE_Y
+
vec4 main(vec2 xy) {
float4 rgba = image.eval(xy);
float3 linear = toLinearSrgb(rgba.rgb);
@@ -51,12 +55,16 @@
return float4(linear.r * gainR, linear.g * gainG, linear.b * gainB, rgba.a);
// MAX_RGB
} else if (key == 1) {
- float4 rgba = image.eval(xy);
- float3 linear = toLinearSrgb(rgba.rgb);
float maxRGB = max(linear.r, max(linear.g, linear.b));
float index = maxRGB * float(size - 1);
float gain = lut.eval(vec2(index, 0.0) + 0.5).r;
return float4(linear * gain, rgba.a);
+ // CIE_Y
+ } else if (key == 2) {
+ float y = dot(linear, luminanceCoefficients) / 3.0;
+ float index = y * float(size - 1);
+ float gain = lut.eval(vec2(index, 0.0) + 0.5).r;
+ return float4(linear * gain, rgba.a);
}
} else if (dimension == 3) {
if (key == 0) {
@@ -110,11 +118,37 @@
return rgba;
})");
+// same as shader::toColorSpace function
+// TODO: put this function in a general place
+static ColorSpace toColorSpace(ui::Dataspace dataspace) {
+ switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
+ case HAL_DATASPACE_STANDARD_BT709:
+ return ColorSpace::sRGB();
+ case HAL_DATASPACE_STANDARD_DCI_P3:
+ return ColorSpace::DisplayP3();
+ case HAL_DATASPACE_STANDARD_BT2020:
+ case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE:
+ return ColorSpace::BT2020();
+ case HAL_DATASPACE_STANDARD_ADOBE_RGB:
+ return ColorSpace::AdobeRGB();
+ case HAL_DATASPACE_STANDARD_BT601_625:
+ case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED:
+ case HAL_DATASPACE_STANDARD_BT601_525:
+ case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED:
+ case HAL_DATASPACE_STANDARD_BT470M:
+ case HAL_DATASPACE_STANDARD_FILM:
+ case HAL_DATASPACE_STANDARD_UNSPECIFIED:
+ default:
+ return ColorSpace::sRGB();
+ }
+}
+
sk_sp<SkShader> LutShader::generateLutShader(sk_sp<SkShader> input,
const std::vector<float>& buffers,
const int32_t offset, const int32_t length,
const int32_t dimension, const int32_t size,
- const int32_t samplingKey) {
+ const int32_t samplingKey,
+ ui::Dataspace srcDataspace) {
SFTRACE_NAME("lut shader");
std::vector<half> buffer(length * 4); // 4 is for RGBA
auto d = static_cast<LutProperties::Dimension>(dimension);
@@ -133,12 +167,16 @@
}
}
/**
- * 1D Lut(rgba)
+ * 1D Lut RGB/MAX_RGB
* (R0, 0, 0, 0)
* (R1, 0, 0, 0)
+ *
+ * 1D Lut CIE_Y
+ * (Y0, 0, 0, 0)
+ * (Y1, 0, 0, 0)
* ...
*
- * 3D Lut
+ * 3D Lut MAX_RGB
* (R0, G0, B0, 0)
* (R1, G1, B1, 0)
* ...
@@ -162,6 +200,14 @@
const int uSize = static_cast<int>(size);
const int uKey = static_cast<int>(samplingKey);
const int uDimension = static_cast<int>(dimension);
+ if (static_cast<LutProperties::SamplingKey>(samplingKey) == LutProperties::SamplingKey::CIE_Y) {
+ // Use predefined colorspaces of input dataspace so that we can get D65 illuminant
+ mat3 toXYZMatrix(toColorSpace(srcDataspace).getRGBtoXYZ());
+ mBuilder->uniform("luminanceCoefficients") =
+ SkV3{toXYZMatrix[0][1], toXYZMatrix[1][1], toXYZMatrix[2][1]};
+ } else {
+ mBuilder->uniform("luminanceCoefficients") = SkV3{1.f, 1.f, 1.f};
+ }
mBuilder->uniform("size") = uSize;
mBuilder->uniform("key") = uKey;
mBuilder->uniform("dimension") = uDimension;
@@ -170,6 +216,7 @@
sk_sp<SkShader> LutShader::lutShader(sk_sp<SkShader>& input,
std::shared_ptr<gui::DisplayLuts> displayLuts,
+ ui::Dataspace srcDataspace,
sk_sp<SkColorSpace> outColorSpace) {
if (mBuilder == nullptr) {
const static SkRuntimeEffect::Result instance = SkRuntimeEffect::MakeForShader(kShader);
@@ -218,7 +265,7 @@
}
input = generateLutShader(input, buffers, offsets[i], bufferSizePerLut,
lutProperties[i].dimension, lutProperties[i].size,
- lutProperties[i].samplingKey);
+ lutProperties[i].samplingKey, srcDataspace);
}
auto colorXformLutToDst =
diff --git a/libs/renderengine/skia/filters/LutShader.h b/libs/renderengine/skia/filters/LutShader.h
index ce3e059..7c62fca 100644
--- a/libs/renderengine/skia/filters/LutShader.h
+++ b/libs/renderengine/skia/filters/LutShader.h
@@ -21,6 +21,7 @@
#include <aidl/android/hardware/graphics/composer3/LutProperties.h>
#include <gui/DisplayLuts.h>
+#include <ui/GraphicTypes.h>
namespace android {
namespace renderengine {
@@ -29,13 +30,13 @@
class LutShader {
public:
sk_sp<SkShader> lutShader(sk_sp<SkShader>& input, std::shared_ptr<gui::DisplayLuts> displayLuts,
- sk_sp<SkColorSpace> outColorSpace);
+ ui::Dataspace srcDataspace, sk_sp<SkColorSpace> outColorSpace);
private:
sk_sp<SkShader> generateLutShader(sk_sp<SkShader> input, const std::vector<float>& buffers,
const int32_t offset, const int32_t length,
const int32_t dimension, const int32_t size,
- const int32_t samplingKey);
+ const int32_t samplingKey, ui::Dataspace srcDataspace);
std::unique_ptr<SkRuntimeShaderBuilder> mBuilder;
};
diff --git a/libs/ui/include/ui/DynamicDisplayInfo.h b/libs/ui/include/ui/DynamicDisplayInfo.h
index af494dc..9d97151 100644
--- a/libs/ui/include/ui/DynamicDisplayInfo.h
+++ b/libs/ui/include/ui/DynamicDisplayInfo.h
@@ -59,6 +59,9 @@
// Represents frame rate for FrameRateCategory Normal and High.
ui::FrameRateCategoryRate frameRateCategoryRate;
+
+ // All the refresh rates supported for the default display mode.
+ std::vector<float> supportedRefreshRates;
};
} // namespace android::ui
diff --git a/services/automotive/display/Android.bp b/services/automotive/display/Android.bp
index 72bd292..b63e919 100644
--- a/services/automotive/display/Android.bp
+++ b/services/automotive/display/Android.bp
@@ -23,6 +23,11 @@
default_applicable_licenses: ["frameworks_native_license"],
}
+vintf_fragment {
+ name: "manifest_android.frameworks.automotive.display@1.0.xml",
+ src: "manifest_android.frameworks.automotive.display@1.0.xml",
+}
+
cc_binary {
name: "android.frameworks.automotive.display@1.0-service",
defaults: ["hidl_defaults"],
@@ -50,7 +55,7 @@
"-DLOG_TAG=\"AutomotiveDisplayService\""
],
- vintf_fragments: [
+ vintf_fragment_modules: [
"manifest_android.frameworks.automotive.display@1.0.xml",
],
}
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index e4069dd..668fa54 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -1557,6 +1557,19 @@
return distance1 < distance2 ? *lowerBound : *std::prev(lowerBound);
}
+std::vector<float> RefreshRateSelector::getSupportedFrameRates() const {
+ std::scoped_lock lock(mLock);
+ // TODO(b/356986687) Remove the limit once we have the anchor list implementation.
+ const size_t frameRatesSize = std::min<size_t>(11, mPrimaryFrameRates.size());
+ std::vector<float> supportedFrameRates;
+ supportedFrameRates.reserve(frameRatesSize);
+ std::transform(mPrimaryFrameRates.rbegin(),
+ mPrimaryFrameRates.rbegin() + static_cast<int>(frameRatesSize),
+ std::back_inserter(supportedFrameRates),
+ [](FrameRateMode mode) { return mode.fps.getValue(); });
+ return supportedFrameRates;
+}
+
auto RefreshRateSelector::getIdleTimerAction() const -> KernelIdleTimerAction {
std::lock_guard lock(mLock);
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index ee3a4f7..508f9d7 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -441,6 +441,8 @@
std::pair<Fps, Fps> getFrameRateCategoryRates() const { return kFrameRateCategoryRates; }
+ std::vector<float> getSupportedFrameRates() const EXCLUDES(mLock);
+
private:
friend struct TestableRefreshRateSelector;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 7ce050b..0ea26d8 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1231,6 +1231,8 @@
const auto [normal, high] = display->refreshRateSelector().getFrameRateCategoryRates();
ui::FrameRateCategoryRate frameRateCategoryRate(normal.getValue(), high.getValue());
info->frameRateCategoryRate = frameRateCategoryRate;
+
+ info->supportedRefreshRates = display->refreshRateSelector().getSupportedFrameRates();
info->activeColorMode = display->getCompositionDisplay()->getState().colorMode;
info->hdrCapabilities = filterOut4k30(display->getHdrCapabilities());
@@ -8643,6 +8645,11 @@
gui::FrameRateCategoryRate& frameRateCategoryRate = outInfo->frameRateCategoryRate;
frameRateCategoryRate.normal = info.frameRateCategoryRate.getNormal();
frameRateCategoryRate.high = info.frameRateCategoryRate.getHigh();
+ outInfo->supportedRefreshRates.clear();
+ outInfo->supportedRefreshRates.reserve(info.supportedRefreshRates.size());
+ for (float supportedRefreshRate : info.supportedRefreshRates) {
+ outInfo->supportedRefreshRates.push_back(supportedRefreshRate);
+ }
outInfo->supportedColorModes.clear();
outInfo->supportedColorModes.reserve(info.supportedColorModes.size());