diff options
147 files changed, 4801 insertions, 2571 deletions
diff --git a/aidl/gui/android/view/Surface.aidl b/aidl/gui/android/view/Surface.aidl index 7e892205d5..bb3faaff79 100644 --- a/aidl/gui/android/view/Surface.aidl +++ b/aidl/gui/android/view/Surface.aidl @@ -17,4 +17,4 @@ package android.view; -parcelable Surface cpp_header "gui/view/Surface.h"; +@JavaOnlyStableParcelable @NdkOnlyStableParcelable parcelable Surface cpp_header "gui/view/Surface.h" ndk_header "android/native_window_aidl.h"; diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp index 2684f048f8..2ae61b9603 100644 --- a/cmds/servicemanager/ServiceManager.cpp +++ b/cmds/servicemanager/ServiceManager.cpp @@ -612,7 +612,8 @@ void ServiceManager::binderDied(const wp<IBinder>& who) { } void ServiceManager::tryStartService(const std::string& name) { - ALOGI("Since '%s' could not be found, trying to start it as a lazy AIDL service", + ALOGI("Since '%s' could not be found, trying to start it as a lazy AIDL service. (if it's not " + "configured to be a lazy service, it may be stuck starting or still starting).", name.c_str()); std::thread([=] { diff --git a/data/etc/android.software.opengles.deqp.level-2023-03-01.xml b/data/etc/android.software.opengles.deqp.level-2023-03-01.xml new file mode 100644 index 0000000000..d0b594c73d --- /dev/null +++ b/data/etc/android.software.opengles.deqp.level-2023-03-01.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<!-- This is the standard feature indicating that the device passes OpenGL ES + dEQP tests associated with date 2023-03-01 (0x07E70301). --> +<permissions> + <feature name="android.software.opengles.deqp.level" version="132580097" /> +</permissions> diff --git a/data/etc/android.software.vulkan.deqp.level-2023-03-01.xml b/data/etc/android.software.vulkan.deqp.level-2023-03-01.xml new file mode 100644 index 0000000000..6ae248ac3c --- /dev/null +++ b/data/etc/android.software.vulkan.deqp.level-2023-03-01.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<!-- This is the standard feature indicating that the device passes Vulkan dEQP + tests associated with date 2023-03-01 (0x07E70301). --> +<permissions> + <feature name="android.software.vulkan.deqp.level" version="132580097" /> +</permissions> diff --git a/include/android/font.h b/include/android/font.h index 45eb81a533..022572535b 100644 --- a/include/android/font.h +++ b/include/android/font.h @@ -87,10 +87,11 @@ enum { AFONT_WEIGHT_MAX = 1000 }; +struct AFont; /** * AFont provides information of the single font configuration. */ -struct AFont; +typedef struct AFont AFont; /** * Close an AFont. diff --git a/include/android/font_matcher.h b/include/android/font_matcher.h index 63b0328749..60ff95e123 100644 --- a/include/android/font_matcher.h +++ b/include/android/font_matcher.h @@ -117,11 +117,12 @@ enum { AFAMILY_VARIANT_ELEGANT = 2, }; +struct AFontMatcher; /** * AFontMatcher performs match operation on given parameters and available font files. * This matcher is not a thread-safe object. Do not pass this matcher to other threads. */ -struct AFontMatcher; +typedef struct AFontMatcher AFontMatcher; /** * Select the best font from given parameters. diff --git a/include/android/system_fonts.h b/include/android/system_fonts.h index b0bbb954a9..94484eaf54 100644 --- a/include/android/system_fonts.h +++ b/include/android/system_fonts.h @@ -87,13 +87,14 @@ __BEGIN_DECLS +struct ASystemFontIterator; /** * ASystemFontIterator provides access to the system font configuration. * * ASystemFontIterator is an iterator for all available system font settings. * This iterator is not a thread-safe object. Do not pass this iterator to other threads. */ -struct ASystemFontIterator; +typedef struct ASystemFontIterator ASystemFontIterator; /** * Create a system font iterator. diff --git a/include/input/Input.h b/include/input/Input.h index 172e5b46d8..dd74a51e5e 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -366,7 +366,7 @@ struct PointerCoords { // Values of axes that are stored in this structure packed in order by axis id // for each axis that is present in the structure according to 'bits'. - float values[MAX_AXES]; + std::array<float, MAX_AXES> values; inline void clear() { BitSet64::clear(bits); @@ -406,7 +406,8 @@ struct PointerCoords { return !(*this == other); } - void copyFrom(const PointerCoords& other); + inline void copyFrom(const PointerCoords& other) { *this = other; } + PointerCoords& operator=(const PointerCoords&) = default; private: void tooManyAxes(int axis); diff --git a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h index fccc0afa89..fe5f35c266 100644 --- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h +++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h @@ -30,11 +30,11 @@ #include <android/binder_internal_logging.h> #include <android/binder_parcel.h> #include <android/binder_status.h> - #include <assert.h> - #include <unistd.h> + #include <cstddef> +#include <iostream> #include <string> namespace ndk { @@ -315,6 +315,11 @@ class ScopedAStatus : public impl::ScopedAResource<AStatus*, AStatus_delete, nul } }; +static inline std::ostream& operator<<(std::ostream& os, const ScopedAStatus& status) { + return os << status.getDescription(); + return os; +} + /** * Convenience wrapper. See AIBinder_DeathRecipient. */ diff --git a/libs/binder/ndk/tests/binderVendorDoubleLoadTest.cpp b/libs/binder/ndk/tests/binderVendorDoubleLoadTest.cpp index f3cd21822d..43b2cb8577 100644 --- a/libs/binder/ndk/tests/binderVendorDoubleLoadTest.cpp +++ b/libs/binder/ndk/tests/binderVendorDoubleLoadTest.cpp @@ -106,7 +106,7 @@ TEST(DoubleBinder, CallIntoNdk) { std::string outString; ScopedAStatus status = server->RepeatString("foo", &outString); EXPECT_EQ(STATUS_OK, AStatus_getExceptionCode(status.get())) - << serviceName << " " << status.getDescription(); + << serviceName << " " << status; EXPECT_EQ("foo", outString) << serviceName; } } diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp index a135796179..738d16ac66 100644 --- a/libs/binder/rust/Android.bp +++ b/libs/binder/rust/Android.bp @@ -88,6 +88,7 @@ rust_library { min_sdk_version: "Tiramisu", lints: "none", clippy_lints: "none", + visibility: [":__subpackages__"], } rust_bindgen { diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs index 195d9ac160..a0e61d95f9 100644 --- a/libs/binder/rust/src/lib.rs +++ b/libs/binder/rust/src/lib.rs @@ -148,4 +148,5 @@ pub mod unstable_api { pub use crate::binder::AsNative; pub use crate::proxy::unstable_api::new_spibinder; pub use crate::sys::AIBinder; + pub use crate::sys::AParcel; } diff --git a/libs/binder/rust/tests/parcel_fuzzer/Android.bp b/libs/binder/rust/tests/parcel_fuzzer/Android.bp new file mode 100644 index 0000000000..28e0200fe1 --- /dev/null +++ b/libs/binder/rust/tests/parcel_fuzzer/Android.bp @@ -0,0 +1,25 @@ +package { + // See: http://go/android-license-faq + default_applicable_licenses: ["frameworks_native_license"], +} + +rust_fuzz { + name: "parcel_fuzzer_rs", + srcs: [ + "parcel_fuzzer.rs", + ], + rustlibs: [ + "libarbitrary", + "libnum_traits", + "libbinder_rs", + "libbinder_random_parcel_rs", + "binderReadParcelIface-rust", + ], + + fuzz_config: { + cc: [ + "waghpawan@google.com", + "smoreland@google.com", + ], + }, +} diff --git a/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs b/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs new file mode 100644 index 0000000000..c5c7719df2 --- /dev/null +++ b/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs @@ -0,0 +1,161 @@ +/* + * Copyright (C) 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. + */ + +#![allow(missing_docs)] +#![no_main] + +#[macro_use] +extern crate libfuzzer_sys; + +mod read_utils; + +use crate::read_utils::READ_FUNCS; +use binder::binder_impl::{ + Binder, BorrowedParcel, IBinderInternal, Parcel, Stability, TransactionCode, +}; +use binder::{ + declare_binder_interface, BinderFeatures, Interface, Parcelable, ParcelableHolder, SpIBinder, + StatusCode, +}; +use binder_random_parcel_rs::create_random_parcel; +use libfuzzer_sys::arbitrary::Arbitrary; + +#[derive(Arbitrary, Debug)] +enum ReadOperation { + SetDataPosition { pos: i32 }, + GetDataSize, + ReadParcelableHolder { is_vintf: bool }, + ReadBasicTypes { instructions: Vec<usize> }, +} + +#[derive(Arbitrary, Debug)] +enum Operation<'a> { + Transact { code: u32, flag: u32, data: &'a [u8] }, + Append { start: i32, len: i32, data1: &'a [u8], data2: &'a [u8], append_all: bool }, + Read { read_operations: Vec<ReadOperation>, data: &'a [u8] }, +} + +/// Interface to fuzz transact with random parcel +pub trait BinderTransactTest: Interface {} + +declare_binder_interface! { + BinderTransactTest["Binder_Transact_Test"] { + native: BnBinderTransactTest(on_transact), + proxy: BpBinderTransactTest, + } +} + +impl BinderTransactTest for Binder<BnBinderTransactTest> {} + +impl BinderTransactTest for BpBinderTransactTest {} + +impl BinderTransactTest for () {} + +fn on_transact( + _service: &dyn BinderTransactTest, + _code: TransactionCode, + _parcel: &BorrowedParcel<'_>, + _reply: &mut BorrowedParcel<'_>, +) -> Result<(), StatusCode> { + Err(StatusCode::UNKNOWN_ERROR) +} + +fn do_transact(code: u32, data: &[u8], flag: u32) { + let p: Parcel = create_random_parcel(data); + let spibinder: Option<SpIBinder> = + Some(BnBinderTransactTest::new_binder((), BinderFeatures::default()).as_binder()); + let _reply = spibinder.submit_transact(code, p, flag); +} + +fn do_append_fuzz(start: i32, len: i32, data1: &[u8], data2: &[u8], append_all: bool) { + let mut p1 = create_random_parcel(data1); + let p2 = create_random_parcel(data2); + + // Fuzz both append methods + if append_all { + match p1.append_all_from(&p2) { + Ok(result) => result, + Err(e) => { + println!("Error occurred while appending a parcel using append_all_from: {:?}", e) + } + } + } else { + match p1.append_from(&p2, start, len) { + Ok(result) => result, + Err(e) => { + println!("Error occurred while appending a parcel using append_from: {:?}", e) + } + } + }; +} + +fn do_read_fuzz(read_operations: Vec<ReadOperation>, data: &[u8]) { + let parcel = create_random_parcel(data); + + for operation in read_operations { + match operation { + ReadOperation::SetDataPosition { pos } => { + unsafe { + // Safety: Safe if pos is less than current size of the parcel. + // It relies on C++ code for bound checks + match parcel.set_data_position(pos) { + Ok(result) => result, + Err(e) => println!("error occurred while setting data position: {:?}", e), + } + } + } + + ReadOperation::GetDataSize => { + let data_size = parcel.get_data_size(); + println!("data size from parcel: {:?}", data_size); + } + + ReadOperation::ReadParcelableHolder { is_vintf } => { + let stability = if is_vintf { Stability::Vintf } else { Stability::Local }; + let mut holder: ParcelableHolder = ParcelableHolder::new(stability); + match holder.read_from_parcel(parcel.borrowed_ref()) { + Ok(result) => result, + Err(err) => { + println!("error occurred while reading from parcel: {:?}", err) + } + } + } + + ReadOperation::ReadBasicTypes { instructions } => { + for instruction in instructions.iter() { + let read_index = instruction % READ_FUNCS.len(); + READ_FUNCS[read_index](parcel.borrowed_ref()); + } + } + } + } +} + +fuzz_target!(|operation: Operation| { + match operation { + Operation::Transact { code, flag, data } => { + do_transact(code, data, flag); + } + + Operation::Append { start, len, data1, data2, append_all } => { + do_append_fuzz(start, len, data1, data2, append_all); + } + + Operation::Read { read_operations, data } => { + do_read_fuzz(read_operations, data); + } + } +}); diff --git a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/Android.bp b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/Android.bp new file mode 100644 index 0000000000..43a309409d --- /dev/null +++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/Android.bp @@ -0,0 +1,52 @@ +package { + // See: http://go/android-license-faq + default_applicable_licenses: ["frameworks_native_license"], +} + +rust_bindgen { + name: "libbinder_random_parcel_bindgen", + crate_name: "binder_random_parcel_bindgen", + host_supported: true, + wrapper_src: "wrappers/RandomParcelWrapper.hpp", + source_stem: "bindings", + visibility: [":__subpackages__"], + bindgen_flags: [ + "--size_t-is-usize", + "--allowlist-function", + "createRandomParcel", + "--allowlist-function", + "fuzzRustService", + ], + shared_libs: [ + "libc++", + "libbinder_ndk", + ], + rustlibs: [ + "libbinder_rs", + ], +} + +rust_library { + name: "libbinder_random_parcel_rs", + crate_name: "binder_random_parcel_rs", + host_supported: true, + srcs: [ + "src/lib.rs", + ], + shared_libs: [ + "libbinder", + "libutils", + "libcutils", + "libc++", + ], + static_libs: [ + "libbinder_create_parcel", + "libbinder_random_parcel", + ], + rustlibs: [ + "libbinder_rs", + "libbinder_random_parcel_bindgen", + ], + lints: "none", + clippy_lints: "none", +} diff --git a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp new file mode 100644 index 0000000000..43e407cef1 --- /dev/null +++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp @@ -0,0 +1,33 @@ +package { + // See: http://go/android-license-faq + default_applicable_licenses: ["frameworks_native_license"], +} + +aidl_interface { + name: "testServiceInterface", + srcs: ["ITestService.aidl"], + unstable: true, + backend: { + rust: { + enabled: true, + }, + }, +} + +rust_fuzz { + name: "example_service_fuzzer", + srcs: [ + "service_fuzzer.rs", + ], + rustlibs: [ + "libbinder_rs", + "libbinder_random_parcel_rs", + "testServiceInterface-rust", + ], + fuzz_config: { + cc: [ + "waghpawan@google.com", + "smoreland@google.com", + ], + }, +} diff --git a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/ITestService.aidl b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/ITestService.aidl new file mode 100644 index 0000000000..8ce655862f --- /dev/null +++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/ITestService.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 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. + */ + +interface ITestService { + boolean repeatData(boolean token); +}
\ No newline at end of file diff --git a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/service_fuzzer.rs b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/service_fuzzer.rs new file mode 100644 index 0000000000..a427f28e29 --- /dev/null +++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/service_fuzzer.rs @@ -0,0 +1,39 @@ +/* + * Copyright (C) 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. + */ + +#![allow(missing_docs)] +#![no_main] +#[macro_use] +extern crate libfuzzer_sys; + +use binder::{self, BinderFeatures, Interface}; +use binder_random_parcel_rs::fuzz_service; +use testServiceInterface::aidl::ITestService::{self, BnTestService}; + +struct TestService; + +impl Interface for TestService {} + +impl ITestService::ITestService for TestService { + fn repeatData(&self, token: bool) -> binder::Result<bool> { + Ok(token) + } +} + +fuzz_target!(|data: &[u8]| { + let service = BnTestService::new_binder(TestService, BinderFeatures::default()); + fuzz_service(&mut service.as_binder(), data); +}); diff --git a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/src/lib.rs b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/src/lib.rs new file mode 100644 index 0000000000..1bbd6742f2 --- /dev/null +++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/src/lib.rs @@ -0,0 +1,44 @@ +/* + * Copyright (C) 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. + */ + +use binder::binder_impl::Parcel; +use binder::unstable_api::{AParcel, AsNative}; +use binder::SpIBinder; +use binder_random_parcel_bindgen::{createRandomParcel, fuzzRustService}; +use std::os::raw::c_void; + +/// This API creates a random parcel to be used by fuzzers +pub fn create_random_parcel(fuzzer_data: &[u8]) -> Parcel { + let mut parcel = Parcel::new(); + let aparcel_ptr: *mut AParcel = parcel.as_native_mut(); + let ptr = aparcel_ptr as *mut c_void; + unsafe { + // Safety: `Parcel::as_native_mut` and `slice::as_ptr` always + // return valid pointers. + createRandomParcel(ptr, fuzzer_data.as_ptr(), fuzzer_data.len()); + } + parcel +} + +/// This API automatically fuzzes provided service +pub fn fuzz_service(binder: &mut SpIBinder, fuzzer_data: &[u8]) { + let ptr = binder.as_native_mut() as *mut c_void; + unsafe { + // Safety: `SpIBinder::as_native_mut` and `slice::as_ptr` always + // return valid pointers. + fuzzRustService(ptr, fuzzer_data.as_ptr(), fuzzer_data.len()); + } +} diff --git a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/wrappers/RandomParcelWrapper.hpp b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/wrappers/RandomParcelWrapper.hpp new file mode 100644 index 0000000000..831bd5660c --- /dev/null +++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/wrappers/RandomParcelWrapper.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (C) 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 <cstdint> +#include <cstddef> + +extern "C" { + // This API is used by rust to fill random parcel. + void createRandomParcel(void* aParcel, const uint8_t* data, size_t len); + + // This API is used by fuzzers to automatically fuzz aidl services + void fuzzRustService(void* binder, const uint8_t* data, size_t len); +}
\ No newline at end of file diff --git a/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs b/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs new file mode 100644 index 0000000000..d2bfde1022 --- /dev/null +++ b/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs @@ -0,0 +1,133 @@ +/* + * Copyright (C) 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. + */ + +use binder::binder_impl::BorrowedParcel; +use binder::{ParcelFileDescriptor, Parcelable, SpIBinder}; +use binderReadParcelIface::aidl::EmptyParcelable::EmptyParcelable; +use binderReadParcelIface::aidl::GenericDataParcelable::GenericDataParcelable; +use binderReadParcelIface::aidl::SingleDataParcelable::SingleDataParcelable; + +macro_rules! read_parcel_interface { + ($data_type:ty) => { + |parcel: &BorrowedParcel<'_>| { + let _res = parcel.read::<$data_type>(); + } + }; +} + +#[derive(Debug, Default)] +pub struct SomeParcelable { + pub data: i32, +} + +impl binder::Parcelable for SomeParcelable { + fn write_to_parcel( + &self, + parcel: &mut binder::binder_impl::BorrowedParcel, + ) -> std::result::Result<(), binder::StatusCode> { + parcel.sized_write(|subparcel| subparcel.write(&self.data)) + } + + fn read_from_parcel( + &mut self, + parcel: &binder::binder_impl::BorrowedParcel, + ) -> std::result::Result<(), binder::StatusCode> { + parcel.sized_read(|subparcel| match subparcel.read() { + Ok(result) => { + self.data = result; + Ok(()) + } + Err(e) => Err(e), + }) + } +} + +binder::impl_deserialize_for_parcelable!(SomeParcelable); + +pub const READ_FUNCS: &[fn(&BorrowedParcel<'_>)] = &[ + //read basic types + read_parcel_interface!(bool), + read_parcel_interface!(i8), + read_parcel_interface!(i32), + read_parcel_interface!(i64), + read_parcel_interface!(f32), + read_parcel_interface!(f64), + read_parcel_interface!(u16), + read_parcel_interface!(u32), + read_parcel_interface!(u64), + read_parcel_interface!(String), + //read vec of basic types + read_parcel_interface!(Vec<i8>), + read_parcel_interface!(Vec<i32>), + read_parcel_interface!(Vec<i64>), + read_parcel_interface!(Vec<f32>), + read_parcel_interface!(Vec<f64>), + read_parcel_interface!(Vec<u16>), + read_parcel_interface!(Vec<u32>), + read_parcel_interface!(Vec<u64>), + read_parcel_interface!(Vec<String>), + read_parcel_interface!(Option<Vec<i8>>), + read_parcel_interface!(Option<Vec<i32>>), + read_parcel_interface!(Option<Vec<i64>>), + read_parcel_interface!(Option<Vec<f32>>), + read_parcel_interface!(Option<Vec<f64>>), + read_parcel_interface!(Option<Vec<u16>>), + read_parcel_interface!(Option<Vec<u32>>), + read_parcel_interface!(Option<Vec<u64>>), + read_parcel_interface!(Option<Vec<String>>), + read_parcel_interface!(ParcelFileDescriptor), + read_parcel_interface!(Vec<Option<ParcelFileDescriptor>>), + read_parcel_interface!(Option<Vec<ParcelFileDescriptor>>), + read_parcel_interface!(Option<Vec<Option<ParcelFileDescriptor>>>), + read_parcel_interface!(SpIBinder), + read_parcel_interface!(Vec<Option<SpIBinder>>), + read_parcel_interface!(Option<Vec<SpIBinder>>), + read_parcel_interface!(Option<Vec<Option<SpIBinder>>>), + read_parcel_interface!(SomeParcelable), + read_parcel_interface!(Vec<Option<SomeParcelable>>), + read_parcel_interface!(Option<Vec<SomeParcelable>>), + read_parcel_interface!(Option<Vec<Option<SomeParcelable>>>), + // Fuzz read_from_parcel for AIDL generated parcelables + |parcel| { + let mut empty_parcelable: EmptyParcelable = EmptyParcelable::default(); + match empty_parcelable.read_from_parcel(parcel) { + Ok(result) => result, + Err(e) => { + println!("EmptyParcelable: error occurred while reading from a parcel: {:?}", e) + } + } + }, + |parcel| { + let mut single_parcelable: SingleDataParcelable = SingleDataParcelable::default(); + match single_parcelable.read_from_parcel(parcel) { + Ok(result) => result, + Err(e) => println!( + "SingleDataParcelable: error occurred while reading from a parcel: {:?}", + e + ), + } + }, + |parcel| { + let mut generic_parcelable: GenericDataParcelable = GenericDataParcelable::default(); + match generic_parcelable.read_from_parcel(parcel) { + Ok(result) => result, + Err(e) => println!( + "GenericDataParcelable: error occurred while reading from a parcel: {:?}", + e + ), + } + }, +]; diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp index 3904e1d4cc..61a24127d6 100644 --- a/libs/binder/tests/parcel_fuzzer/Android.bp +++ b/libs/binder/tests/parcel_fuzzer/Android.bp @@ -20,6 +20,9 @@ aidl_interface { java: { enabled: false, }, + rust: { + enabled: true, + }, }, } diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp index 462ef9a5e9..a1fb70131e 100644 --- a/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp +++ b/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp @@ -29,3 +29,12 @@ void fuzzService(AIBinder* binder, FuzzedDataProvider&& provider) { } } // namespace android + +extern "C" { +// This API is used by fuzzers to automatically fuzz aidl services +void fuzzRustService(void* binder, const uint8_t* data, size_t len) { + AIBinder* aiBinder = static_cast<AIBinder*>(binder); + FuzzedDataProvider provider(data, len); + android::fuzzService(aiBinder, std::move(provider)); +} +} // extern "C" diff --git a/libs/binder/tests/parcel_fuzzer/rust_interface/Android.bp b/libs/binder/tests/parcel_fuzzer/rust_interface/Android.bp new file mode 100644 index 0000000000..b48dc27241 --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/rust_interface/Android.bp @@ -0,0 +1,24 @@ +package { + default_applicable_licenses: ["frameworks_native_license"], +} + +cc_library_static { + name: "libbinder_create_parcel", + host_supported: true, + target: { + darwin: { + enabled: false, + }, + }, + srcs: [ + "RandomParcelWrapper.cpp", + ], + shared_libs: [ + "libbase", + "libbinder", + "libbinder_ndk", + ], + static_libs: [ + "libbinder_random_parcel", + ], +} diff --git a/libs/binder/tests/parcel_fuzzer/rust_interface/RandomParcelWrapper.cpp b/libs/binder/tests/parcel_fuzzer/rust_interface/RandomParcelWrapper.cpp new file mode 100644 index 0000000000..2fb7820b0b --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/rust_interface/RandomParcelWrapper.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) 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 <android-base/logging.h> +#include <android/binder_libbinder.h> +#include <android/binder_parcel.h> +#include <fuzzbinder/random_parcel.h> + +extern "C" { + +void createRandomParcel(void* aParcel, const uint8_t* data, size_t len) { + CHECK_NE(aParcel, nullptr); + AParcel* parcel = static_cast<AParcel*>(aParcel); + FuzzedDataProvider provider(data, len); + android::RandomParcelOptions options; + + android::Parcel* platformParcel = AParcel_viewPlatformParcel(parcel); + fillRandomParcel(platformParcel, std::move(provider), &options); +} + +} // extern "C"
\ No newline at end of file diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp index 3d81c3292a..6d1dfe8124 100644 --- a/libs/gralloc/types/Android.bp +++ b/libs/gralloc/types/Android.bp @@ -58,7 +58,7 @@ cc_library { ], export_shared_lib_headers: [ - "android.hardware.graphics.common-V3-ndk", + "android.hardware.graphics.common-V4-ndk", "android.hardware.graphics.mapper@4.0", "libhidlbase", ], diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 0021bd6cc3..97e45c6d47 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -360,11 +360,12 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence } } for (const auto& staleRelease : staleReleases) { - BQA_LOGE("Faking releaseBufferCallback from transactionCompleteCallback"); - BBQ_TRACE("FakeReleaseCallback"); releaseBufferCallbackLocked(staleRelease, - stat.previousReleaseFence ? stat.previousReleaseFence : Fence::NO_FENCE, - stat.currentMaxAcquiredBufferCount); + stat.previousReleaseFence + ? stat.previousReleaseFence + : Fence::NO_FENCE, + stat.currentMaxAcquiredBufferCount, + true /* fakeRelease */); } } else { BQA_LOGE("Failed to find matching SurfaceControl in transactionCallback"); @@ -408,11 +409,13 @@ void BLASTBufferQueue::releaseBufferCallback( BBQ_TRACE(); std::unique_lock _lock{mMutex}; - releaseBufferCallbackLocked(id, releaseFence, currentMaxAcquiredBufferCount); + releaseBufferCallbackLocked(id, releaseFence, currentMaxAcquiredBufferCount, + false /* fakeRelease */); } -void BLASTBufferQueue::releaseBufferCallbackLocked(const ReleaseCallbackId& id, - const sp<Fence>& releaseFence, std::optional<uint32_t> currentMaxAcquiredBufferCount) { +void BLASTBufferQueue::releaseBufferCallbackLocked( + const ReleaseCallbackId& id, const sp<Fence>& releaseFence, + std::optional<uint32_t> currentMaxAcquiredBufferCount, bool fakeRelease) { ATRACE_CALL(); BQA_LOGV("releaseBufferCallback %s", id.to_string().c_str()); @@ -435,6 +438,11 @@ void BLASTBufferQueue::releaseBufferCallbackLocked(const ReleaseCallbackId& id, auto rb = ReleasedBuffer{id, releaseFence}; if (std::find(mPendingRelease.begin(), mPendingRelease.end(), rb) == mPendingRelease.end()) { mPendingRelease.emplace_back(rb); + if (fakeRelease) { + BQA_LOGE("Faking releaseBufferCallback from transactionCompleteCallback %" PRIu64, + id.framenumber); + BBQ_TRACE("FakeReleaseCallback"); + } } // Release all buffers that are beyond the ones that we need to hold diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 9c2ce0f242..9e175ec42e 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -2375,42 +2375,22 @@ status_t SurfaceComposerClient::getActiveDisplayMode(const sp<IBinder>& display, return NAME_NOT_FOUND; } -status_t SurfaceComposerClient::setDesiredDisplayModeSpecs( - const sp<IBinder>& displayToken, ui::DisplayModeId defaultMode, bool allowGroupSwitching, - float primaryRefreshRateMin, float primaryRefreshRateMax, float appRequestRefreshRateMin, - float appRequestRefreshRateMax) { +status_t SurfaceComposerClient::setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, + const gui::DisplayModeSpecs& specs) { binder::Status status = - ComposerServiceAIDL::getComposerService() - ->setDesiredDisplayModeSpecs(displayToken, defaultMode, allowGroupSwitching, - primaryRefreshRateMin, primaryRefreshRateMax, - appRequestRefreshRateMin, - appRequestRefreshRateMax); + ComposerServiceAIDL::getComposerService()->setDesiredDisplayModeSpecs(displayToken, + specs); return statusTFromBinderStatus(status); } status_t SurfaceComposerClient::getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, - ui::DisplayModeId* outDefaultMode, - bool* outAllowGroupSwitching, - float* outPrimaryRefreshRateMin, - float* outPrimaryRefreshRateMax, - float* outAppRequestRefreshRateMin, - float* outAppRequestRefreshRateMax) { - if (!outDefaultMode || !outAllowGroupSwitching || !outPrimaryRefreshRateMin || - !outPrimaryRefreshRateMax || !outAppRequestRefreshRateMin || !outAppRequestRefreshRateMax) { + gui::DisplayModeSpecs* outSpecs) { + if (!outSpecs) { return BAD_VALUE; } - gui::DisplayModeSpecs specs; binder::Status status = ComposerServiceAIDL::getComposerService()->getDesiredDisplayModeSpecs(displayToken, - &specs); - if (status.isOk()) { - *outDefaultMode = specs.defaultMode; - *outAllowGroupSwitching = specs.allowGroupSwitching; - *outPrimaryRefreshRateMin = specs.primaryRefreshRateMin; - *outPrimaryRefreshRateMax = specs.primaryRefreshRateMax; - *outAppRequestRefreshRateMin = specs.appRequestRefreshRateMin; - *outAppRequestRefreshRateMax = specs.appRequestRefreshRateMax; - } + outSpecs); return statusTFromBinderStatus(status); } diff --git a/libs/gui/aidl/android/gui/DisplayModeSpecs.aidl b/libs/gui/aidl/android/gui/DisplayModeSpecs.aidl index fb4fcdf8e8..af138c7539 100644 --- a/libs/gui/aidl/android/gui/DisplayModeSpecs.aidl +++ b/libs/gui/aidl/android/gui/DisplayModeSpecs.aidl @@ -18,10 +18,58 @@ package android.gui; /** @hide */ parcelable DisplayModeSpecs { + /** + * Defines the refresh rates ranges that should be used by SF. + */ + parcelable RefreshRateRanges { + /** + * Defines a range of refresh rates. + */ + parcelable RefreshRateRange { + float min; + float max; + } + + /** + * The range of refresh rates that the display should run at. + */ + RefreshRateRange physical; + + /** + * The range of refresh rates that apps should render at. + */ + RefreshRateRange render; + } + + /** + * Base mode ID. This is what system defaults to for all other settings, or + * if the refresh rate range is not available. + */ int defaultMode; + + /** + * If true this will allow switching between modes in different display configuration + * groups. This way the user may see visual interruptions when the display mode changes. + */ + boolean allowGroupSwitching; - float primaryRefreshRateMin; - float primaryRefreshRateMax; - float appRequestRefreshRateMin; - float appRequestRefreshRateMax; + + /** + * The primary physical and render refresh rate ranges represent DisplayManager's general + * guidance on the display modes SurfaceFlinger will consider when switching refresh + * rates and scheduling the frame rate. Unless SurfaceFlinger has a specific reason to do + * otherwise, it will stay within this range. + */ + RefreshRateRanges primaryRanges; + + /** + * The app request physical and render refresh rate ranges allow SurfaceFlinger to consider + * more display modes when switching refresh rates. Although SurfaceFlinger will + * generally stay within the primary range, specific considerations, such as layer frame + * rate settings specified via the setFrameRate() API, may cause SurfaceFlinger to go + * outside the primary range. SurfaceFlinger never goes outside the app request range. + * The app request range will be greater than or equal to the primary refresh rate range, + * never smaller. + */ + RefreshRateRanges appRequestRanges; } diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl index 92d9e7799c..40410fb59e 100644 --- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl +++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl @@ -327,25 +327,9 @@ interface ISurfaceComposer { /** * Sets the refresh rate boundaries for the display. * - * The primary refresh rate range represents display manager's general guidance on the display - * modes we'll consider when switching refresh rates. Unless we get an explicit signal from an - * app, we should stay within this range. - * - * The app request refresh rate range allows us to consider more display modes when switching - * refresh rates. Although we should generally stay within the primary range, specific - * considerations, such as layer frame rate settings specified via the setFrameRate() api, may - * cause us to go outside the primary range. We never go outside the app request range. The app - * request range will be greater than or equal to the primary refresh rate range, never smaller. - * - * defaultMode is used to narrow the list of display modes SurfaceFlinger will consider - * switching between. Only modes with a mode group and resolution matching defaultMode - * will be considered for switching. The defaultMode corresponds to an ID of mode in the list - * of supported modes returned from getDynamicDisplayInfo(). - */ - void setDesiredDisplayModeSpecs( - IBinder displayToken, int defaultMode, - boolean allowGroupSwitching, float primaryRefreshRateMin, float primaryRefreshRateMax, - float appRequestRefreshRateMin, float appRequestRefreshRateMax); + * @see DisplayModeSpecs.aidl for details. + */ + void setDesiredDisplayModeSpecs(IBinder displayToken, in DisplayModeSpecs specs); DisplayModeSpecs getDesiredDisplayModeSpecs(IBinder displayToken); diff --git a/libs/gui/fuzzer/Android.bp b/libs/gui/fuzzer/Android.bp index cdc9376a44..1c61d6bb8b 100644 --- a/libs/gui/fuzzer/Android.bp +++ b/libs/gui/fuzzer/Android.bp @@ -46,7 +46,7 @@ cc_defaults { "android.hardware.configstore-utils", "android.hardware.graphics.bufferqueue@1.0", "android.hardware.graphics.bufferqueue@2.0", - "android.hardware.power-V2-cpp", + "android.hardware.power-V4-cpp", "android.hidl.token@1.0", "libSurfaceFlingerProp", "libgui", diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h index 202517067f..9d1ee8f65b 100644 --- a/libs/gui/fuzzer/libgui_fuzzer_utils.h +++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h @@ -127,9 +127,7 @@ public: MOCK_METHOD(binder::Status, removeTunnelModeEnabledListener, (const sp<gui::ITunnelModeEnabledListener>&), (override)); MOCK_METHOD(binder::Status, setDesiredDisplayModeSpecs, - (const sp<IBinder>&, int32_t, bool, float, float, float, - float appRequestRefreshRateMax), - (override)); + (const sp<IBinder>&, const gui::DisplayModeSpecs&), (override)); MOCK_METHOD(binder::Status, getDesiredDisplayModeSpecs, (const sp<IBinder>&, gui::DisplayModeSpecs*), (override)); MOCK_METHOD(binder::Status, getDisplayBrightnessSupport, (const sp<IBinder>&, bool*), diff --git a/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp b/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp index eecbe0fe21..57720dd513 100644 --- a/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp +++ b/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp @@ -123,10 +123,37 @@ private: sp<SurfaceControl> makeSurfaceControl(); BlurRegion getBlurRegion(); void fuzzOnPullAtom(); + gui::DisplayModeSpecs getDisplayModeSpecs(); FuzzedDataProvider mFdp; }; +gui::DisplayModeSpecs SurfaceComposerClientFuzzer::getDisplayModeSpecs() { + const auto getRefreshRateRange = [&] { + gui::DisplayModeSpecs::RefreshRateRanges::RefreshRateRange range; + range.min = mFdp.ConsumeFloatingPoint<float>(); + range.max = mFdp.ConsumeFloatingPoint<float>(); + return range; + }; + + const auto getRefreshRateRanges = [&] { + gui::DisplayModeSpecs::RefreshRateRanges ranges; + ranges.physical = getRefreshRateRange(); + ranges.render = getRefreshRateRange(); + return ranges; + }; + + String8 displayName((mFdp.ConsumeRandomLengthString(kRandomStringMaxBytes)).c_str()); + sp<IBinder> displayToken = + SurfaceComposerClient::createDisplay(displayName, mFdp.ConsumeBool() /*secure*/); + gui::DisplayModeSpecs specs; + specs.defaultMode = mFdp.ConsumeIntegral<int32_t>(); + specs.allowGroupSwitching = mFdp.ConsumeBool(); + specs.primaryRanges = getRefreshRateRanges(); + specs.appRequestRanges = getRefreshRateRanges(); + return specs; +} + BlurRegion SurfaceComposerClientFuzzer::getBlurRegion() { int32_t left = mFdp.ConsumeIntegral<int32_t>(); int32_t right = mFdp.ConsumeIntegral<int32_t>(); @@ -247,12 +274,7 @@ void SurfaceComposerClientFuzzer::invokeSurfaceComposerClient() { String8 displayName((mFdp.ConsumeRandomLengthString(kRandomStringMaxBytes)).c_str()); sp<IBinder> displayToken = SurfaceComposerClient::createDisplay(displayName, mFdp.ConsumeBool() /*secure*/); - SurfaceComposerClient::setDesiredDisplayModeSpecs(displayToken, mFdp.ConsumeIntegral<int32_t>(), - mFdp.ConsumeBool() /*allowGroupSwitching*/, - mFdp.ConsumeFloatingPoint<float>(), - mFdp.ConsumeFloatingPoint<float>(), - mFdp.ConsumeFloatingPoint<float>(), - mFdp.ConsumeFloatingPoint<float>()); + SurfaceComposerClient::setDesiredDisplayModeSpecs(displayToken, getDisplayModeSpecs()); ui::ColorMode colorMode = mFdp.PickValueInArray(kColormodes); SurfaceComposerClient::setActiveColorMode(displayToken, colorMode); diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 957652e1f1..47dcc42e16 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -93,7 +93,8 @@ public: void releaseBufferCallback(const ReleaseCallbackId& id, const sp<Fence>& releaseFence, std::optional<uint32_t> currentMaxAcquiredBufferCount); void releaseBufferCallbackLocked(const ReleaseCallbackId& id, const sp<Fence>& releaseFence, - std::optional<uint32_t> currentMaxAcquiredBufferCount); + std::optional<uint32_t> currentMaxAcquiredBufferCount, + bool fakeRelease); void syncNextTransaction(std::function<void(SurfaceComposerClient::Transaction*)> callback, bool acquireSingleBuffer = true); void stopContinuousSyncTransaction(); diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h index 1f19f4e1da..7aec0bf09d 100644 --- a/libs/gui/include/gui/Surface.h +++ b/libs/gui/include/gui/Surface.h @@ -113,6 +113,24 @@ public: return surface != nullptr && surface->getIGraphicBufferProducer() != nullptr; } + static sp<IGraphicBufferProducer> getIGraphicBufferProducer(ANativeWindow* window) { + int val; + if (window->query(window, NATIVE_WINDOW_CONCRETE_TYPE, &val) >= 0 && + val == NATIVE_WINDOW_SURFACE) { + return ((Surface*) window)->mGraphicBufferProducer; + } + return nullptr; + } + + static sp<IBinder> getSurfaceControlHandle(ANativeWindow* window) { + int val; + if (window->query(window, NATIVE_WINDOW_CONCRETE_TYPE, &val) >= 0 && + val == NATIVE_WINDOW_SURFACE) { + return ((Surface*) window)->mSurfaceControlHandle; + } + return nullptr; + } + /* Attaches a sideband buffer stream to the Surface's IGraphicBufferProducer. * * A sideband stream is a device-specific mechanism for passing buffers diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index c450e85857..2038f1477a 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -159,18 +159,11 @@ public: static status_t getActiveDisplayMode(const sp<IBinder>& display, ui::DisplayMode*); // Sets the refresh rate boundaries for the display. - static status_t setDesiredDisplayModeSpecs( - const sp<IBinder>& displayToken, ui::DisplayModeId defaultMode, - bool allowGroupSwitching, float primaryRefreshRateMin, float primaryRefreshRateMax, - float appRequestRefreshRateMin, float appRequestRefreshRateMax); + static status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, + const gui::DisplayModeSpecs&); // Gets the refresh rate boundaries for the display. static status_t getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, - ui::DisplayModeId* outDefaultMode, - bool* outAllowGroupSwitching, - float* outPrimaryRefreshRateMin, - float* outPrimaryRefreshRateMax, - float* outAppRequestRefreshRateMin, - float* outAppRequestRefreshRateMax); + gui::DisplayModeSpecs*); // Get the coordinates of the display's native color primaries static status_t getDisplayNativePrimaries(const sp<IBinder>& display, diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 346b686466..67c669ddb7 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -920,16 +920,12 @@ public: } binder::Status setDesiredDisplayModeSpecs(const sp<IBinder>& /*displayToken*/, - int32_t /*defaultMode*/, bool /*allowGroupSwitching*/, - float /*primaryRefreshRateMin*/, - float /*primaryRefreshRateMax*/, - float /*appRequestRefreshRateMin*/, - float /*appRequestRefreshRateMax*/) override { + const gui::DisplayModeSpecs&) override { return binder::Status::ok(); } binder::Status getDesiredDisplayModeSpecs(const sp<IBinder>& /*displayToken*/, - gui::DisplayModeSpecs* /*outSpecs*/) override { + gui::DisplayModeSpecs*) override { return binder::Status::ok(); } diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index c1eb8e2a12..cf5a7e7b05 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -438,14 +438,6 @@ bool PointerCoords::operator==(const PointerCoords& other) const { return true; } -void PointerCoords::copyFrom(const PointerCoords& other) { - bits = other.bits; - uint32_t count = BitSet64::count(bits); - for (uint32_t i = 0; i < count; i++) { - values[i] = other.values[i]; - } -} - void PointerCoords::transform(const ui::Transform& transform) { const vec2 xy = transform.transform(getXYValue()); setAxisValue(AMOTION_EVENT_AXIS_X, xy.x); diff --git a/libs/jpegrecoverymap/Android.bp b/libs/jpegrecoverymap/Android.bp index 3ab2ba898e..0375915839 100644 --- a/libs/jpegrecoverymap/Android.bp +++ b/libs/jpegrecoverymap/Android.bp @@ -23,16 +23,19 @@ package { cc_library_static { name: "libjpegrecoverymap", - vendor_available: true, + host_supported: true, export_include_dirs: ["include"], local_include_dirs: ["include"], srcs: [ "recoverymap.cpp", + "recoverymapmath.cpp", ], shared_libs: [ + "libimage_io", + "libjpeg", "libutils", ], } @@ -63,4 +66,4 @@ cc_library_static { srcs: [ "jpegdecoder.cpp", ], -}
\ No newline at end of file +} diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h index 2ab75503a5..df24b10ebc 100644 --- a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h +++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h @@ -14,6 +14,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +#ifndef ANDROID_JPEGRECOVERYMAP_JPEGDECODER_H +#define ANDROID_JPEGRECOVERYMAP_JPEGDECODER_H + // We must include cstdio before jpeglib.h. It is a requirement of libjpeg. #include <cstdio> extern "C" { @@ -41,12 +45,22 @@ public: * Returns the decompressed raw image buffer pointer. This method must be called only after * calling decompressImage(). */ - const void* getDecompressedImagePtr(); + void* getDecompressedImagePtr(); /* * Returns the decompressed raw image buffer size. This method must be called only after * calling decompressImage(). */ size_t getDecompressedImageSize(); + /* + * Returns the image width in pixels. This method must be called only after calling + * decompressImage(). + */ + size_t getDecompressedImageWidth(); + /* + * Returns the image width in pixels. This method must be called only after calling + * decompressImage(). + */ + size_t getDecompressedImageHeight(); private: bool decode(const void* image, int length); // Returns false if errors occur. @@ -56,7 +70,12 @@ private: // Process 16 lines of Y and 16 lines of U/V each time. // We must pass at least 16 scanlines according to libjpeg documentation. static const int kCompressBatchSize = 16; - // The buffer that holds the compressed result. + // The buffer that holds the decompressed result. std::vector<JOCTET> mResultBuffer; + // Resolution of the decompressed image. + size_t mWidth; + size_t mHeight; }; } /* namespace android */ + +#endif // ANDROID_JPEGRECOVERYMAP_JPEGDECODER_H diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegencoder.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegencoder.h index 9641fda24c..61aeb8ace7 100644 --- a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegencoder.h +++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegencoder.h @@ -14,6 +14,9 @@ * limitations under the License. */ +#ifndef ANDROID_JPEGRECOVERYMAP_JPEGENCODER_H +#define ANDROID_JPEGRECOVERYMAP_JPEGENCODER_H + // We must include cstdio before jpeglib.h. It is a requirement of libjpeg. #include <cstdio> @@ -50,7 +53,7 @@ public: * Returns the compressed JPEG buffer pointer. This method must be called only after calling * compressImage(). */ - const void* getCompressedImagePtr(); + void* getCompressedImagePtr(); /* * Returns the compressed JPEG buffer size. This method must be called only after calling @@ -87,4 +90,6 @@ private: std::vector<JOCTET> mResultBuffer; }; -} /* namespace android */
\ No newline at end of file +} /* namespace android */ + +#endif // ANDROID_JPEGRECOVERYMAP_JPEGENCODER_H diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h new file mode 100644 index 0000000000..194cd2f403 --- /dev/null +++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 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 <utils/Errors.h> + +namespace android::recoverymap { + +enum { + // status_t map for errors in the media framework + // OK or NO_ERROR or 0 represents no error. + + // See system/core/include/utils/Errors.h + // System standard errors from -1 through (possibly) -133 + // + // Errors with special meanings and side effects. + // INVALID_OPERATION: Operation attempted in an illegal state (will try to signal to app). + // DEAD_OBJECT: Signal from CodecBase to MediaCodec that MediaServer has died. + // NAME_NOT_FOUND: Signal from CodecBase to MediaCodec that the component was not found. + + // JPEGR errors + JPEGR_IO_ERROR_BASE = -10000, + ERROR_JPEGR_INVALID_INPUT_TYPE = JPEGR_IO_ERROR_BASE, + ERROR_JPEGR_INVALID_OUTPUT_TYPE = JPEGR_IO_ERROR_BASE - 1, + ERROR_JPEGR_INVALID_NULL_PTR = JPEGR_IO_ERROR_BASE - 2, + ERROR_JPEGR_RESOLUTION_MISMATCH = JPEGR_IO_ERROR_BASE - 3, + ERROR_JPEGR_BUFFER_TOO_SMALL = JPEGR_IO_ERROR_BASE - 4, + + JPEGR_RUNTIME_ERROR_BASE = -20000, + ERROR_JPEGR_ENCODE_ERROR = JPEGR_RUNTIME_ERROR_BASE - 1, + ERROR_JPEGR_DECODE_ERROR = JPEGR_RUNTIME_ERROR_BASE - 2, + ERROR_JPEGR_CALCULATION_ERROR = JPEGR_RUNTIME_ERROR_BASE - 3, + ERROR_JPEGR_METADATA_ERROR = JPEGR_RUNTIME_ERROR_BASE - 4, +}; + +} // namespace android::recoverymap diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h index 15eca1e705..b2ca481aa7 100644 --- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h +++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h @@ -14,10 +14,20 @@ * limitations under the License. */ - #include <utils/Errors.h> +#ifndef ANDROID_JPEGRECOVERYMAP_RECOVERYMAP_H +#define ANDROID_JPEGRECOVERYMAP_RECOVERYMAP_H + +#include "jpegrerrorcode.h" namespace android::recoverymap { +typedef enum { + JPEGR_COLORSPACE_UNSPECIFIED, + JPEGR_COLORSPACE_BT709, + JPEGR_COLORSPACE_P3, + JPEGR_COLORSPACE_BT2100, +} jpegr_color_space; + /* * Holds information for uncompressed image or recovery map. */ @@ -28,6 +38,8 @@ struct jpegr_uncompressed_struct { int width; // Height of the recovery map or image in pixels. int height; + // Color space. + jpegr_color_space colorSpace; }; /* @@ -36,12 +48,25 @@ struct jpegr_uncompressed_struct { struct jpegr_compressed_struct { // Pointer to the data location. void* data; + // Data length. + int length; + // Color space. + jpegr_color_space colorSpace; +}; + +/* + * Holds information for EXIF metadata. + */ +struct jpegr_exif_struct { + // Pointer to the data location. + void* data; // Data length; int length; }; typedef struct jpegr_uncompressed_struct* jr_uncompressed_ptr; typedef struct jpegr_compressed_struct* jr_compressed_ptr; +typedef struct jpegr_exif_struct* jr_exif_ptr; class RecoveryMap { public: @@ -49,21 +74,29 @@ public: * Compress JPEGR image from 10-bit HDR YUV and 8-bit SDR YUV. * * Generate recovery map from the HDR and SDR inputs, compress SDR YUV to 8-bit JPEG and append - * the recovery map to the end of the compressed JPEG. + * the recovery map to the end of the compressed JPEG. HDR and SDR inputs must be the same + * resolution and color space. * @param uncompressed_p010_image uncompressed HDR image in P010 color format * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format * @param dest destination of the compressed JPEGR image + * @param quality target quality of the JPEG encoding, must be in range of 0-100 where 100 is + * the highest quality + * @param exif pointer to the exif metadata. * @return NO_ERROR if encoding succeeds, error code if error occurs. */ status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, jr_uncompressed_ptr uncompressed_yuv_420_image, - void* dest); + jr_compressed_ptr dest, + int quality, + jr_exif_ptr exif); /* - * Compress JPEGR image from 10-bit HDR YUV and 8-bit SDR YUV. + * Compress JPEGR image from 10-bit HDR YUV, 8-bit SDR YUV and compressed 8-bit JPEG. + * + * This method requires HAL Hardware JPEG encoder. * * Generate recovery map from the HDR and SDR inputs, append the recovery map to the end of the - * compressed JPEG. + * compressed JPEG. HDR and SDR inputs must be the same resolution and color space. * @param uncompressed_p010_image uncompressed HDR image in P010 color format * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format * @param compressed_jpeg_image compressed 8-bit JPEG image @@ -72,31 +105,48 @@ public: */ status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, jr_uncompressed_ptr uncompressed_yuv_420_image, - void* compressed_jpeg_image, - void* dest); + jr_compressed_ptr compressed_jpeg_image, + jr_compressed_ptr dest); /* * Compress JPEGR image from 10-bit HDR YUV and 8-bit SDR YUV. * + * This method requires HAL Hardware JPEG encoder. + * * Decode the compressed 8-bit JPEG image to YUV SDR, generate recovery map from the HDR input - * and the decoded SDR result, append the recovery map to the end of the compressed JPEG. + * and the decoded SDR result, append the recovery map to the end of the compressed JPEG. HDR + * and SDR inputs must be the same resolution and color space. * @param uncompressed_p010_image uncompressed HDR image in P010 color format * @param compressed_jpeg_image compressed 8-bit JPEG image * @param dest destination of the compressed JPEGR image * @return NO_ERROR if encoding succeeds, error code if error occurs. */ status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, - void* compressed_jpeg_image, - void* dest); + jr_compressed_ptr compressed_jpeg_image, + jr_compressed_ptr dest); /* * Decompress JPEGR image. * * @param compressed_jpegr_image compressed JPEGR image * @param dest destination of the uncompressed JPEGR image + * @param exif destination of the decoded EXIF metadata. Default value is nullptr where EXIF + * metadata will not be decoded. + * @param request_sdr flag that request SDR output, default to false (request HDR output). If + * set to true, decoder will only decode the primary image which is SDR. + * Setting of request_sdr and input source (HDR or SDR) can be found in + * the table below: + * | input source | request_sdr | output of decoding | + * | HDR | true | SDR | + * | HDR | false | HDR | + * | SDR | true | SDR | + * | SDR | false | SDR | * @return NO_ERROR if decoding succeeds, error code if error occurs. */ - status_t decodeJPEGR(void* compressed_jpegr_image, jr_uncompressed_ptr dest); + status_t decodeJPEGR(jr_compressed_ptr compressed_jpegr_image, + jr_uncompressed_ptr dest, + jr_exif_ptr exif = nullptr, + bool request_sdr = false); private: /* * This method is called in the decoding pipeline. It will decode the recovery map. @@ -105,7 +155,7 @@ private: * @param dest decoded recover map * @return NO_ERROR if decoding succeeds, error code if error occurs. */ - status_t decodeRecoveryMap(jr_compressed_ptr compressed_recovery_map, + status_t decompressRecoveryMap(jr_compressed_ptr compressed_recovery_map, jr_uncompressed_ptr dest); /* @@ -115,21 +165,24 @@ private: * @param dest encoded recover map * @return NO_ERROR if encoding succeeds, error code if error occurs. */ - status_t encodeRecoveryMap(jr_uncompressed_ptr uncompressed_recovery_map, + status_t compressRecoveryMap(jr_uncompressed_ptr uncompressed_recovery_map, jr_compressed_ptr dest); /* * This method is called in the encoding pipeline. It will take the uncompressed 8-bit and - * 10-bit yuv images as input, and calculate the uncompressed recovery map. + * 10-bit yuv images as input, and calculate the uncompressed recovery map. The input images + * must be the same resolution. * * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format * @param uncompressed_p010_image uncompressed HDR image in P010 color format - * @param dest recover map + * @param dest recovery map; caller responsible for memory of data + * @param hdr_ratio HDR ratio will be updated in this method * @return NO_ERROR if calculation succeeds, error code if error occurs. */ status_t generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image, jr_uncompressed_ptr uncompressed_p010_image, - jr_uncompressed_ptr dest); + jr_uncompressed_ptr dest, + float &hdr_ratio); /* * This method is called in the decoding pipeline. It will take the uncompressed (decoded) @@ -153,7 +206,7 @@ private: * @param dest destination of compressed recovery map * @return NO_ERROR if calculation succeeds, error code if error occurs. */ - status_t extractRecoveryMap(void* compressed_jpegr_image, jr_compressed_ptr dest); + status_t extractRecoveryMap(jr_compressed_ptr compressed_jpegr_image, jr_compressed_ptr dest); /* * This method is called in the encoding pipeline. It will take the standard 8-bit JPEG image @@ -162,12 +215,57 @@ private: * * @param compressed_jpeg_image compressed 8-bit JPEG image * @param compress_recovery_map compressed recover map + * @param hdr_ratio HDR ratio * @param dest compressed JPEGR image * @return NO_ERROR if calculation succeeds, error code if error occurs. */ - status_t appendRecoveryMap(void* compressed_jpeg_image, + status_t appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image, jr_compressed_ptr compressed_recovery_map, - void* dest); + float hdr_ratio, + jr_compressed_ptr dest); + + /* + * This method generates XMP metadata. + * + * below is an example of the XMP metadata that this function generates where + * secondary_image_length = 1000 + * hdr_ratio = 1.25 + * + * <x:xmpmeta + * xmlns:x="adobe:ns:meta/" + * x:xmptk="Adobe XMP Core 5.1.2"> + * <rdf:RDF + * xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> + * <rdf:Description + * xmlns:GContainer="http://ns.google.com/photos/1.0/container/"> + * <GContainer:Version>1</GContainer:Version> + * <GContainer:HdrRatio>1.25</GContainer:HdrRatio> + * <GContainer:Directory> + * <rdf:Seq> + * <rdf:li> + * <GContainer:Item + * Item:Semantic="Primary" + * Item:Mime="image/jpeg"/> + * </rdf:li> + * <rdf:li> + * <GContainer:Item + * Item:Semantic="RecoveryMap" + * Item:Mime="image/jpeg" + * Item:Length="1000"/> + * </rdf:li> + * </rdf:Seq> + * </GContainer:Directory> + * </rdf:Description> + * </rdf:RDF> + * </x:xmpmeta> + * + * @param secondary_image_length length of secondary image + * @param hdr_ratio hdr ratio + * @return XMP metadata in type of string + */ + std::string generateXmp(int secondary_image_length, float hdr_ratio); }; } // namespace android::recoverymap + +#endif // ANDROID_JPEGRECOVERYMAP_RECOVERYMAP_H diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h new file mode 100644 index 0000000000..8e9b07bf3d --- /dev/null +++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h @@ -0,0 +1,106 @@ +/* + * 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. + */ + +#ifndef ANDROID_JPEGRECOVERYMAP_RECOVERYMAPMATH_H +#define ANDROID_JPEGRECOVERYMAP_RECOVERYMAPMATH_H + +#include <stdint.h> + +#include <jpegrecoverymap/recoverymap.h> + +namespace android::recoverymap { + +const float kSdrWhiteNits = 100.0f; + +struct Color { + union { + struct { + float r; + float g; + float b; + }; + struct { + float y; + float u; + float v; + }; + }; +}; + +/* + * Convert from OETF'd bt.2100 RGB to YUV, according to BT.2100 + */ +Color bt2100RgbToYuv(Color e); + +/* + * Convert srgb YUV to RGB, according to ECMA TR/98. + */ +Color srgbYuvToRgb(Color e); + +/* + * TODO: better source for srgb transfer function + * Convert from srgb to linear, according to https://en.wikipedia.org/wiki/SRGB. + * [0.0, 1.0] range in and out. + */ +float srgbInvOetf(float e); +Color srgbInvOetf(Color e); + +/* + * Convert from HLG to scene luminance in nits, according to BT.2100. + */ +float hlgInvOetf(float e); + +/* + * Convert from scene luminance in nits to HLG, according to BT.2100. + */ +float hlgOetf(float e); +Color hlgOetf(Color e); + +/* + * Calculate the 8-bit unsigned integer recovery value for the given SDR and HDR + * luminances in linear space, and the hdr ratio to encode against. + */ +uint8_t encodeRecovery(float y_sdr, float y_hdr, float hdr_ratio); + +/* + * Calculates the linear luminance in nits after applying the given recovery + * value, with the given hdr ratio, to the given sdr input in the range [0, 1]. + */ +Color applyRecovery(Color e, float recovery, float hdr_ratio); + +/* + * Helper for sampling from images. + */ +Color getYuv420Pixel(jr_uncompressed_ptr image, size_t x, size_t y); + +/* + * Sample the recovery value for the map from a given x,y coordinate on a scale + * that is map scale factor larger than the map size. + */ +float sampleMap(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y); + +/* + * Sample the image Y value at the provided location, with a weighting based on nearby pixels + * and the map scale factor. + * + * Expect narrow-range image data for P010. + */ +float sampleYuv420Y(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y); +float sampleP010Y(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y); + +} // namespace android::recoverymap + +#endif // ANDROID_JPEGRECOVERYMAP_RECOVERYMAPMATH_H diff --git a/libs/jpegrecoverymap/jpegdecoder.cpp b/libs/jpegrecoverymap/jpegdecoder.cpp index 22a5389648..c1fb6c3f1d 100644 --- a/libs/jpegrecoverymap/jpegdecoder.cpp +++ b/libs/jpegrecoverymap/jpegdecoder.cpp @@ -95,7 +95,7 @@ bool JpegDecoder::decompressImage(const void* image, int length) { return true; } -const void* JpegDecoder::getDecompressedImagePtr() { +void* JpegDecoder::getDecompressedImagePtr() { return mResultBuffer.data(); } @@ -103,6 +103,14 @@ size_t JpegDecoder::getDecompressedImageSize() { return mResultBuffer.size(); } +size_t JpegDecoder::getDecompressedImageWidth() { + return mWidth; +} + +size_t JpegDecoder::getDecompressedImageHeight() { + return mHeight; +} + bool JpegDecoder::decode(const void* image, int length) { jpeg_decompress_struct cinfo; jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length); @@ -119,6 +127,9 @@ bool JpegDecoder::decode(const void* image, int length) { cinfo.src = &mgr; jpeg_read_header(&cinfo, TRUE); + mWidth = cinfo.image_width; + mHeight = cinfo.image_height; + if (cinfo.jpeg_color_space == JCS_YCbCr) { mResultBuffer.resize(cinfo.image_width * cinfo.image_height * 3 / 2, 0); } else if (cinfo.jpeg_color_space == JCS_GRAYSCALE) { @@ -222,4 +233,4 @@ bool JpegDecoder::decompressSingleChannel(jpeg_decompress_struct* cinfo, const u return true; } -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/libs/jpegrecoverymap/jpegencoder.cpp b/libs/jpegrecoverymap/jpegencoder.cpp index d45d9b33c9..1997bf9ea3 100644 --- a/libs/jpegrecoverymap/jpegencoder.cpp +++ b/libs/jpegrecoverymap/jpegencoder.cpp @@ -52,7 +52,7 @@ bool JpegEncoder::compressImage(const void* image, int width, int height, int qu return true; } -const void* JpegEncoder::getCompressedImagePtr() { +void* JpegEncoder::getCompressedImagePtr() { return mResultBuffer.data(); } @@ -236,4 +236,4 @@ bool JpegEncoder::compressSingleChannel(jpeg_compress_struct* cinfo, const uint8 return true; } -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/libs/jpegrecoverymap/recoverymap.cpp b/libs/jpegrecoverymap/recoverymap.cpp index 5d2572213b..bd16a68b0d 100644 --- a/libs/jpegrecoverymap/recoverymap.cpp +++ b/libs/jpegrecoverymap/recoverymap.cpp @@ -14,91 +14,322 @@ * limitations under the License. */ +// TODO: need to clean up handling around hdr_ratio and passing it around +// TODO: need to handle color space information; currently we assume everything +// is srgb in. +// TODO: handle PQ encode/decode (currently only HLG) + #include <jpegrecoverymap/recoverymap.h> +#include <jpegrecoverymap/jpegencoder.h> +#include <jpegrecoverymap/jpegdecoder.h> +#include <jpegrecoverymap/recoverymapmath.h> + +#include <image_io/jpeg/jpeg_marker.h> +#include <image_io/xml/xml_writer.h> + +#include <memory> +#include <sstream> +#include <string> + +using namespace std; namespace android::recoverymap { +#define JPEGR_CHECK(x) \ + { \ + status_t status = (x); \ + if ((status) != NO_ERROR) { \ + return status; \ + } \ + } + +// Map is quarter res / sixteenth size +static const size_t kMapDimensionScaleFactor = 4; + + +/* + * Helper function used for generating XMP metadata. + * + * @param prefix The prefix part of the name. + * @param suffix The suffix part of the name. + * @return A name of the form "prefix:suffix". + */ +string Name(const string &prefix, const string &suffix) { + std::stringstream ss; + ss << prefix << ":" << suffix; + return ss.str(); +} + +/* + * Helper function used for writing data to destination. + * + * @param destination destination of the data to be written. + * @param source source of data being written. + * @param length length of the data to be written. + * @param position cursor in desitination where the data is to be written. + * @return status of succeed or error code. + */ +status_t Write(jr_compressed_ptr destination, const void* source, size_t length, int &position) { + if (position + length > destination->length) { + return ERROR_JPEGR_BUFFER_TOO_SMALL; + } + + memcpy((uint8_t*)destination->data + sizeof(uint8_t) * position, source, length); + position += length; + return NO_ERROR; +} + status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, jr_uncompressed_ptr uncompressed_yuv_420_image, - void* dest) { + jr_compressed_ptr dest, + int quality, + jr_exif_ptr /* exif */) { if (uncompressed_p010_image == nullptr || uncompressed_yuv_420_image == nullptr || dest == nullptr) { - return BAD_VALUE; + return ERROR_JPEGR_INVALID_NULL_PTR; } - // TBD + if (quality < 0 || quality > 100) { + return ERROR_JPEGR_INVALID_INPUT_TYPE; + } + + if (uncompressed_p010_image->width != uncompressed_yuv_420_image->width + || uncompressed_p010_image->height != uncompressed_yuv_420_image->height) { + return ERROR_JPEGR_RESOLUTION_MISMATCH; + } + + jpegr_uncompressed_struct map; + float hdr_ratio = 0.0f; + JPEGR_CHECK(generateRecoveryMap( + uncompressed_yuv_420_image, uncompressed_p010_image, &map, hdr_ratio)); + std::unique_ptr<uint8_t[]> map_data; + map_data.reset(reinterpret_cast<uint8_t*>(map.data)); + + jpegr_compressed_struct compressed_map; + std::unique_ptr<uint8_t[]> compressed_map_data = + std::make_unique<uint8_t[]>(map.width * map.height); + compressed_map.data = compressed_map_data.get(); + JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map)); + + JpegEncoder jpeg_encoder; + // TODO: ICC data - need color space information + if (!jpeg_encoder.compressImage(uncompressed_yuv_420_image->data, + uncompressed_yuv_420_image->width, + uncompressed_yuv_420_image->height, quality, nullptr, 0)) { + return ERROR_JPEGR_ENCODE_ERROR; + } + jpegr_compressed_struct jpeg; + jpeg.data = jpeg_encoder.getCompressedImagePtr(); + jpeg.length = jpeg_encoder.getCompressedImageSize(); + + JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, hdr_ratio, dest)); + return NO_ERROR; } status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, jr_uncompressed_ptr uncompressed_yuv_420_image, - void* compressed_jpeg_image, - void* dest) { - + jr_compressed_ptr compressed_jpeg_image, + jr_compressed_ptr dest) { if (uncompressed_p010_image == nullptr || uncompressed_yuv_420_image == nullptr || compressed_jpeg_image == nullptr || dest == nullptr) { - return BAD_VALUE; + return ERROR_JPEGR_INVALID_NULL_PTR; } - // TBD + if (uncompressed_p010_image->width != uncompressed_yuv_420_image->width + || uncompressed_p010_image->height != uncompressed_yuv_420_image->height) { + return ERROR_JPEGR_RESOLUTION_MISMATCH; + } + + jpegr_uncompressed_struct map; + float hdr_ratio = 0.0f; + JPEGR_CHECK(generateRecoveryMap( + uncompressed_yuv_420_image, uncompressed_p010_image, &map, hdr_ratio)); + std::unique_ptr<uint8_t[]> map_data; + map_data.reset(reinterpret_cast<uint8_t*>(map.data)); + + jpegr_compressed_struct compressed_map; + std::unique_ptr<uint8_t[]> compressed_map_data = + std::make_unique<uint8_t[]>(map.width * map.height); + compressed_map.data = compressed_map_data.get(); + JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map)); + + JPEGR_CHECK(appendRecoveryMap(compressed_jpeg_image, &compressed_map, hdr_ratio, dest)); + return NO_ERROR; } status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, - void* compressed_jpeg_image, - void* dest) { + jr_compressed_ptr compressed_jpeg_image, + jr_compressed_ptr dest) { if (uncompressed_p010_image == nullptr || compressed_jpeg_image == nullptr || dest == nullptr) { - return BAD_VALUE; + return ERROR_JPEGR_INVALID_NULL_PTR; } - // TBD + JpegDecoder jpeg_decoder; + if (!jpeg_decoder.decompressImage(compressed_jpeg_image->data, compressed_jpeg_image->length)) { + return ERROR_JPEGR_DECODE_ERROR; + } + jpegr_uncompressed_struct uncompressed_yuv_420_image; + uncompressed_yuv_420_image.data = jpeg_decoder.getDecompressedImagePtr(); + uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth(); + uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight(); + + if (uncompressed_p010_image->width != uncompressed_yuv_420_image.width + || uncompressed_p010_image->height != uncompressed_yuv_420_image.height) { + return ERROR_JPEGR_RESOLUTION_MISMATCH; + } + + jpegr_uncompressed_struct map; + float hdr_ratio = 0.0f; + JPEGR_CHECK(generateRecoveryMap( + &uncompressed_yuv_420_image, uncompressed_p010_image, &map, hdr_ratio)); + std::unique_ptr<uint8_t[]> map_data; + map_data.reset(reinterpret_cast<uint8_t*>(map.data)); + + jpegr_compressed_struct compressed_map; + std::unique_ptr<uint8_t[]> compressed_map_data = + std::make_unique<uint8_t[]>(map.width * map.height); + compressed_map.data = compressed_map_data.get(); + JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map)); + + JPEGR_CHECK(appendRecoveryMap(compressed_jpeg_image, &compressed_map, hdr_ratio, dest)); + return NO_ERROR; } -status_t RecoveryMap::decodeJPEGR(void* compressed_jpegr_image, jr_uncompressed_ptr dest) { +status_t RecoveryMap::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image, + jr_uncompressed_ptr dest, + jr_exif_ptr /* exif */, + bool /* request_sdr */) { if (compressed_jpegr_image == nullptr || dest == nullptr) { - return BAD_VALUE; + return ERROR_JPEGR_INVALID_NULL_PTR; } - // TBD + jpegr_compressed_struct compressed_map; + JPEGR_CHECK(extractRecoveryMap(compressed_jpegr_image, &compressed_map)); + + jpegr_uncompressed_struct map; + JPEGR_CHECK(decompressRecoveryMap(&compressed_map, &map)); + + JpegDecoder jpeg_decoder; + if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length)) { + return ERROR_JPEGR_DECODE_ERROR; + } + + jpegr_uncompressed_struct uncompressed_yuv_420_image; + uncompressed_yuv_420_image.data = jpeg_decoder.getDecompressedImagePtr(); + uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth(); + uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight(); + + JPEGR_CHECK(applyRecoveryMap(&uncompressed_yuv_420_image, &map, dest)); + return NO_ERROR; } -status_t RecoveryMap::decodeRecoveryMap(jr_compressed_ptr compressed_recovery_map, - jr_uncompressed_ptr dest) { +status_t RecoveryMap::decompressRecoveryMap(jr_compressed_ptr compressed_recovery_map, + jr_uncompressed_ptr dest) { if (compressed_recovery_map == nullptr || dest == nullptr) { - return BAD_VALUE; + return ERROR_JPEGR_INVALID_NULL_PTR; } - // TBD + JpegDecoder jpeg_decoder; + if (!jpeg_decoder.decompressImage(compressed_recovery_map->data, + compressed_recovery_map->length)) { + return ERROR_JPEGR_DECODE_ERROR; + } + + dest->data = jpeg_decoder.getDecompressedImagePtr(); + dest->width = jpeg_decoder.getDecompressedImageWidth(); + dest->height = jpeg_decoder.getDecompressedImageHeight(); + return NO_ERROR; } -status_t RecoveryMap::encodeRecoveryMap(jr_uncompressed_ptr uncompressed_recovery_map, - jr_compressed_ptr dest) { +status_t RecoveryMap::compressRecoveryMap(jr_uncompressed_ptr uncompressed_recovery_map, + jr_compressed_ptr dest) { if (uncompressed_recovery_map == nullptr || dest == nullptr) { - return BAD_VALUE; + return ERROR_JPEGR_INVALID_NULL_PTR; } - // TBD + // TODO: should we have ICC data? + JpegEncoder jpeg_encoder; + if (!jpeg_encoder.compressImage(uncompressed_recovery_map->data, uncompressed_recovery_map->width, + uncompressed_recovery_map->height, 85, nullptr, 0, + true /* isSingleChannel */)) { + return ERROR_JPEGR_ENCODE_ERROR; + } + + if (dest->length < jpeg_encoder.getCompressedImageSize()) { + return ERROR_JPEGR_BUFFER_TOO_SMALL; + } + + memcpy(dest->data, jpeg_encoder.getCompressedImagePtr(), jpeg_encoder.getCompressedImageSize()); + dest->length = jpeg_encoder.getCompressedImageSize(); + return NO_ERROR; } status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image, jr_uncompressed_ptr uncompressed_p010_image, - jr_uncompressed_ptr dest) { + jr_uncompressed_ptr dest, + float &hdr_ratio) { if (uncompressed_yuv_420_image == nullptr || uncompressed_p010_image == nullptr || dest == nullptr) { - return BAD_VALUE; + return ERROR_JPEGR_INVALID_NULL_PTR; } - // TBD + if (uncompressed_yuv_420_image->width != uncompressed_p010_image->width + || uncompressed_yuv_420_image->height != uncompressed_p010_image->height) { + return ERROR_JPEGR_RESOLUTION_MISMATCH; + } + + size_t image_width = uncompressed_yuv_420_image->width; + size_t image_height = uncompressed_yuv_420_image->height; + size_t map_width = image_width / kMapDimensionScaleFactor; + size_t map_height = image_height / kMapDimensionScaleFactor; + + dest->width = map_width; + dest->height = map_height; + dest->data = new uint8_t[map_width * map_height]; + std::unique_ptr<uint8_t[]> map_data; + map_data.reset(reinterpret_cast<uint8_t*>(dest->data)); + + uint16_t yp_hdr_max = 0; + for (size_t y = 0; y < image_height; ++y) { + for (size_t x = 0; x < image_width; ++x) { + size_t pixel_idx = x + y * image_width; + uint16_t yp_hdr = reinterpret_cast<uint8_t*>(uncompressed_yuv_420_image->data)[pixel_idx]; + if (yp_hdr > yp_hdr_max) { + yp_hdr_max = yp_hdr; + } + } + } + + float y_hdr_max_nits = hlgInvOetf(yp_hdr_max); + hdr_ratio = y_hdr_max_nits / kSdrWhiteNits; + + for (size_t y = 0; y < map_height; ++y) { + for (size_t x = 0; x < map_width; ++x) { + float yp_sdr = sampleYuv420Y(uncompressed_yuv_420_image, kMapDimensionScaleFactor, x, y); + float yp_hdr = sampleP010Y(uncompressed_p010_image, kMapDimensionScaleFactor, x, y); + + float y_sdr_nits = srgbInvOetf(yp_sdr); + float y_hdr_nits = hlgInvOetf(yp_hdr); + + size_t pixel_idx = x + y * map_width; + reinterpret_cast<uint8_t*>(dest->data)[pixel_idx] = + encodeRecovery(y_sdr_nits, y_hdr_nits, hdr_ratio); + } + } + + map_data.release(); return NO_ERROR; } @@ -108,33 +339,148 @@ status_t RecoveryMap::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_ if (uncompressed_yuv_420_image == nullptr || uncompressed_recovery_map == nullptr || dest == nullptr) { - return BAD_VALUE; + return ERROR_JPEGR_INVALID_NULL_PTR; + } + + // TODO: need to get this from the XMP; should probably be a function + // parameter + float hdr_ratio = 4.0f; + + size_t width = uncompressed_yuv_420_image->width; + size_t height = uncompressed_yuv_420_image->height; + + dest->width = width; + dest->height = height; + size_t pixel_count = width * height; + + for (size_t y = 0; y < height; ++y) { + for (size_t x = 0; x < width; ++x) { + size_t pixel_y_idx = x + y * width; + + size_t pixel_uv_idx = x / 2 + (y / 2) * (width / 2); + + Color ypuv_sdr = getYuv420Pixel(uncompressed_yuv_420_image, x, y); + Color rgbp_sdr = srgbYuvToRgb(ypuv_sdr); + Color rgb_sdr = srgbInvOetf(rgbp_sdr); + + float recovery = sampleMap(uncompressed_recovery_map, kMapDimensionScaleFactor, x, y); + Color rgb_hdr = applyRecovery(rgb_sdr, recovery, hdr_ratio); + + Color rgbp_hdr = hlgOetf(rgb_hdr); + Color ypuv_hdr = bt2100RgbToYuv(rgbp_hdr); + + reinterpret_cast<uint16_t*>(dest->data)[pixel_y_idx] = ypuv_hdr.r; + reinterpret_cast<uint16_t*>(dest->data)[pixel_count + pixel_uv_idx] = ypuv_hdr.g; + reinterpret_cast<uint16_t*>(dest->data)[pixel_count + pixel_uv_idx + 1] = ypuv_hdr.b; + } } - // TBD return NO_ERROR; } -status_t RecoveryMap::extractRecoveryMap(void* compressed_jpegr_image, jr_compressed_ptr dest) { +status_t RecoveryMap::extractRecoveryMap(jr_compressed_ptr compressed_jpegr_image, + jr_compressed_ptr dest) { if (compressed_jpegr_image == nullptr || dest == nullptr) { - return BAD_VALUE; + return ERROR_JPEGR_INVALID_NULL_PTR; } // TBD return NO_ERROR; } -status_t RecoveryMap::appendRecoveryMap(void* compressed_jpeg_image, - jr_compressed_ptr compressed_recovery_map, - void* dest) { +status_t RecoveryMap::appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image, + jr_compressed_ptr compressed_recovery_map, + float hdr_ratio, + jr_compressed_ptr dest) { if (compressed_jpeg_image == nullptr || compressed_recovery_map == nullptr || dest == nullptr) { - return BAD_VALUE; + return ERROR_JPEGR_INVALID_NULL_PTR; } - // TBD + string xmp = generateXmp(compressed_recovery_map->length, hdr_ratio); + string nameSpace = "http://ns.adobe.com/xap/1.0/\0"; + + // 2 bytes: APP1 sign (ff e1) + // 29 bytes: length of name space "http://ns.adobe.com/xap/1.0/\0" + // x bytes: length of xmp packet + int length = 2 + nameSpace.size() + xmp.size(); + uint8_t lengthH = ((length >> 8) & 0xff); + uint8_t lengthL = (length & 0xff); + + int pos = 0; + + // JPEG/R structure: + // SOI (ff d8) + // APP1 (ff e1) + // 2 bytes of length (2 + 29 + length of xmp packet) + // name space ("http://ns.adobe.com/xap/1.0/\0") + // xmp + // primary image (without the first two bytes, the SOI sign) + // secondary image (the recovery map) + JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos)); + JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kSOI, 1, pos)); + JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos)); + JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP1, 1, pos)); + JPEGR_CHECK(Write(dest, &lengthH, 1, pos)); + JPEGR_CHECK(Write(dest, &lengthL, 1, pos)); + JPEGR_CHECK(Write(dest, (void*)nameSpace.c_str(), nameSpace.size(), pos)); + JPEGR_CHECK(Write(dest, (void*)xmp.c_str(), xmp.size(), pos)); + JPEGR_CHECK(Write(dest, + (uint8_t*)compressed_jpeg_image->data + 2, compressed_jpeg_image->length - 2, pos)); + JPEGR_CHECK(Write(dest, compressed_recovery_map->data, compressed_recovery_map->length, pos)); + dest->length = pos; + return NO_ERROR; } +string RecoveryMap::generateXmp(int secondary_image_length, float hdr_ratio) { + const string kContainerPrefix = "GContainer"; + const string kContainerUri = "http://ns.google.com/photos/1.0/container/"; + const string kItemPrefix = "Item"; + const string kRecoveryMap = "RecoveryMap"; + const string kDirectory = "Directory"; + const string kImageJpeg = "image/jpeg"; + const string kItem = "Item"; + const string kLength = "Length"; + const string kMime = "Mime"; + const string kPrimary = "Primary"; + const string kSemantic = "Semantic"; + const string kVersion = "Version"; + const int kVersionValue = 1; + + const string kConDir = Name(kContainerPrefix, kDirectory); + const string kContainerItem = Name(kContainerPrefix, kItem); + const string kItemLength = Name(kItemPrefix, kLength); + const string kItemMime = Name(kItemPrefix, kMime); + const string kItemSemantic = Name(kItemPrefix, kSemantic); + + const vector<string> kConDirSeq({kConDir, string("rdf:Seq")}); + const vector<string> kLiItem({string("rdf:li"), kContainerItem}); + + std::stringstream ss; + photos_editing_formats::image_io::XmlWriter writer(ss); + writer.StartWritingElement("x:xmpmeta"); + writer.WriteXmlns("x", "adobe:ns:meta/"); + writer.WriteAttributeNameAndValue("x:xmptk", "Adobe XMP Core 5.1.2"); + writer.StartWritingElement("rdf:RDF"); + writer.WriteXmlns("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#"); + writer.StartWritingElement("rdf:Description"); + writer.WriteXmlns(kContainerPrefix, kContainerUri); + writer.WriteElementAndContent(Name(kContainerPrefix, kVersion), kVersionValue); + writer.WriteElementAndContent(Name(kContainerPrefix, "HdrRatio"), hdr_ratio); + writer.StartWritingElements(kConDirSeq); + size_t item_depth = writer.StartWritingElements(kLiItem); + writer.WriteAttributeNameAndValue(kItemSemantic, kPrimary); + writer.WriteAttributeNameAndValue(kItemMime, kImageJpeg); + writer.FinishWritingElementsToDepth(item_depth); + writer.StartWritingElements(kLiItem); + writer.WriteAttributeNameAndValue(kItemSemantic, kRecoveryMap); + writer.WriteAttributeNameAndValue(kItemMime, kImageJpeg); + writer.WriteAttributeNameAndValue(kItemLength, secondary_image_length); + writer.FinishWriting(); + + return ss.str(); +} + } // namespace android::recoverymap diff --git a/libs/jpegrecoverymap/recoverymapmath.cpp b/libs/jpegrecoverymap/recoverymapmath.cpp new file mode 100644 index 0000000000..3e110bcd26 --- /dev/null +++ b/libs/jpegrecoverymap/recoverymapmath.cpp @@ -0,0 +1,169 @@ +/* + * 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 <cmath> + +#include <jpegrecoverymap/recoverymapmath.h> + +namespace android::recoverymap { + +static const float kBt2100R = 0.2627f, kBt2100G = 0.6780f, kBt2100B = 0.0593f; +static const float kBt2100Cb = 1.8814f, kBt2100Cr = 1.4746f; + +Color bt2100RgbToYuv(Color e) { + float yp = kBt2100R * e.r + kBt2100G * e.g + kBt2100B * e.b; + return {{{yp, (e.b - yp) / kBt2100Cb, (e.r - yp) / kBt2100Cr }}}; +} + +static const float kSrgbRCr = 1.402f, kSrgbGCb = 0.34414f, kSrgbGCr = 0.71414f, kSrgbBCb = 1.772f; + +Color srgbYuvToRgb(Color e) { + return {{{ e.y + kSrgbRCr * e.v, e.y - kSrgbGCb * e.u - kSrgbGCr * e.v, e.y + kSrgbBCb * e.u }}}; +} + +float srgbInvOetf(float e) { + if (e <= 0.04045f) { + return e / 12.92f; + } else { + return pow((e + 0.055f) / 1.055f, 2.4); + } +} + +Color srgbInvOetf(Color e) { + return {{{ srgbInvOetf(e.r), srgbInvOetf(e.g), srgbInvOetf(e.b) }}}; +} + +static const float kHlgA = 0.17883277f, kHlgB = 0.28466892f, kHlgC = 0.55991073; + +float hlgInvOetf(float e) { + if (e <= 0.5f) { + return pow(e, 2.0f) / 3.0f; + } else { + return (exp((e - kHlgC) / kHlgA) + kHlgB) / 12.0f; + } +} + +float hlgOetf(float e) { + if (e <= 1.0f/12.0f) { + return sqrt(3.0f * e); + } else { + return kHlgA * log(12.0f * e - kHlgB) + kHlgC; + } +} + +Color hlgOetf(Color e) { + return {{{ hlgOetf(e.r), hlgOetf(e.g), hlgOetf(e.b) }}}; +} + +uint8_t EncodeRecovery(float y_sdr, float y_hdr, float hdr_ratio) { + float gain = 1.0f; + if (y_sdr > 0.0f) { + gain = y_hdr / y_sdr; + } + + if (gain < -hdr_ratio) gain = -hdr_ratio; + if (gain > hdr_ratio) gain = hdr_ratio; + + return static_cast<uint8_t>(log2(gain) / log2(hdr_ratio) * 127.5f + 127.5f); +} + +float applyRecovery(float y_sdr, float recovery, float hdr_ratio) { + return exp2(log2(y_sdr) + recovery * log2(hdr_ratio)); +} + +// TODO: do we need something more clever for filtering either the map or images +// to generate the map? + +static float mapUintToFloat(uint8_t map_uint) { + return (static_cast<float>(map_uint) - 127.5f) / 127.5f; +} + +float sampleMap(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y) { + float x_map = static_cast<float>(x) / static_cast<float>(map_scale_factor); + float y_map = static_cast<float>(y) / static_cast<float>(map_scale_factor); + + size_t x_lower = static_cast<size_t>(floor(x_map)); + size_t x_upper = x_lower + 1; + size_t y_lower = static_cast<size_t>(floor(y_map)); + size_t y_upper = y_lower + 1; + + float x_influence = x_map - static_cast<float>(x_lower); + float y_influence = y_map - static_cast<float>(y_lower); + + float e1 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_lower + y_lower * map->width]); + float e2 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_lower + y_upper * map->width]); + float e3 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_upper + y_lower * map->width]); + float e4 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_upper + y_upper * map->width]); + + return e1 * (x_influence + y_influence) / 2.0f + + e2 * (x_influence + 1.0f - y_influence) / 2.0f + + e3 * (1.0f - x_influence + y_influence) / 2.0f + + e4 * (1.0f - x_influence + 1.0f - y_influence) / 2.0f; +} + +Color getYuv420Pixel(jr_uncompressed_ptr image, size_t x, size_t y) { + size_t pixel_count = image->width * image->height; + + size_t pixel_y_idx = x + y * image->width; + size_t pixel_uv_idx = x / 2 + (y / 2) * (image->width / 2); + + uint8_t y_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_y_idx]; + uint8_t u_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_count + pixel_uv_idx]; + uint8_t v_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_count * 5 / 4 + pixel_uv_idx]; + + // 128 bias for UV given we are using jpeglib; see: + // https://github.com/kornelski/libjpeg/blob/master/structure.doc + return {{{ static_cast<float>(y_uint) / 255.0f, + (static_cast<float>(u_uint) - 128.0f) / 255.0f, + (static_cast<float>(v_uint) - 128.0f) / 255.0f }}}; +} + +typedef float (*sampleComponentFn)(jr_uncompressed_ptr, size_t, size_t); + +static float sampleComponent(jr_uncompressed_ptr image, size_t map_scale_factor, size_t x, size_t y, + sampleComponentFn sample_fn) { + float e = 0.0f; + for (size_t dy = 0; dy < map_scale_factor; ++dy) { + for (size_t dx = 0; dx < map_scale_factor; ++dx) { + e += sample_fn(image, x * map_scale_factor + dx, y * map_scale_factor + dy); + } + } + + return e / static_cast<float>(map_scale_factor * map_scale_factor); +} + +static float getYuv420Y(jr_uncompressed_ptr image, size_t x, size_t y) { + size_t pixel_idx = x + y * image->width; + uint8_t y_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_idx]; + return static_cast<float>(y_uint) / 255.0f; +} + + +float sampleYuv420Y(jr_uncompressed_ptr image, size_t map_scale_factor, size_t x, size_t y) { + return sampleComponent(image, map_scale_factor, x, y, getYuv420Y); +} + +static float getP010Y(jr_uncompressed_ptr image, size_t x, size_t y) { + size_t pixel_idx = x + y * image->width; + uint8_t y_uint = reinterpret_cast<uint16_t*>(image->data)[pixel_idx]; + // Expecting narrow range input + return (static_cast<float>(y_uint) - 64.0f) / 960.0f; +} + +float sampleP010Y(jr_uncompressed_ptr image, size_t map_scale_factor, size_t x, size_t y) { + return sampleComponent(image, map_scale_factor, x, y, getP010Y); +} +} // namespace android::recoverymap diff --git a/libs/jpegrecoverymap/tests/Android.bp b/libs/jpegrecoverymap/tests/Android.bp index 7f37f611c7..41af991093 100644 --- a/libs/jpegrecoverymap/tests/Android.bp +++ b/libs/jpegrecoverymap/tests/Android.bp @@ -62,4 +62,4 @@ cc_test { "libjpegdecoder", "libgtest", ], -}
\ No newline at end of file +} diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp index b0750809f8..c345385839 100644 --- a/libs/nativewindow/ANativeWindow.cpp +++ b/libs/nativewindow/ANativeWindow.cpp @@ -20,10 +20,15 @@ // from nativewindow/includes/system/window.h // (not to be confused with the compatibility-only window.h from system/core/includes) #include <system/window.h> +#include <android/native_window_aidl.h> #include <private/android/AHardwareBufferHelpers.h> +#include <log/log.h> #include <ui/GraphicBuffer.h> +#include <gui/Surface.h> +#include <gui/view/Surface.h> +#include <android/binder_libbinder.h> using namespace android; @@ -59,6 +64,13 @@ static bool isDataSpaceValid(ANativeWindow* window, int32_t dataSpace) { return false; } } +static sp<IGraphicBufferProducer> IGraphicBufferProducer_from_ANativeWindow(ANativeWindow* window) { + return Surface::getIGraphicBufferProducer(window); +} + +static sp<IBinder> SurfaceControlHandle_from_ANativeWindow(ANativeWindow* window) { + return Surface::getSurfaceControlHandle(window); +} /************************************************************************************************** * NDK @@ -350,6 +362,42 @@ int ANativeWindow_setAutoPrerotation(ANativeWindow* window, bool autoPrerotation return native_window_set_auto_prerotation(window, autoPrerotation); } +binder_status_t ANativeWindow_readFromParcel( + const AParcel* _Nonnull parcel, ANativeWindow* _Nullable* _Nonnull outWindow) { + const Parcel* nativeParcel = AParcel_viewPlatformParcel(parcel); + + // Use a android::view::Surface to unparcel the window + std::shared_ptr<android::view::Surface> shimSurface = std::shared_ptr<android::view::Surface>(); + status_t ret = shimSurface->readFromParcel(nativeParcel); + if (ret != OK) { + ALOGE("%s: Error: Failed to create android::view::Surface from AParcel", __FUNCTION__); + return STATUS_BAD_VALUE; + } + sp<Surface> surface = sp<Surface>::make( + shimSurface->graphicBufferProducer, false, shimSurface->surfaceControlHandle); + ANativeWindow* anw = surface.get(); + ANativeWindow_acquire(anw); + *outWindow = anw; + return STATUS_OK; +} + +binder_status_t ANativeWindow_writeToParcel( + ANativeWindow* _Nonnull window, AParcel* _Nonnull parcel) { + int value; + int err = (*window->query)(window, NATIVE_WINDOW_CONCRETE_TYPE, &value); + if (err != OK || value != NATIVE_WINDOW_SURFACE) { + ALOGE("Error: ANativeWindow is not backed by Surface"); + return STATUS_BAD_VALUE; + } + // Use a android::view::Surface to parcelize the window + std::shared_ptr<android::view::Surface> shimSurface = std::shared_ptr<android::view::Surface>(); + shimSurface->graphicBufferProducer = IGraphicBufferProducer_from_ANativeWindow(window); + shimSurface->surfaceControlHandle = SurfaceControlHandle_from_ANativeWindow(window); + + Parcel* nativeParcel = AParcel_viewPlatformParcel(parcel); + return shimSurface->writeToParcel(nativeParcel); +} + /************************************************************************************************** * apex-stable **************************************************************************************************/ diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp index 3b58265440..bc0bfc52d5 100644 --- a/libs/nativewindow/Android.bp +++ b/libs/nativewindow/Android.bp @@ -110,9 +110,11 @@ cc_library { static_libs: [ "libarect", "libgrallocusage", + "libgui_aidl_static", ], header_libs: [ + "libgui_headers", "libarect_headers", "libnativebase_headers", "libnativewindow_headers", diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h index 281ec52528..a27e3dd503 100644 --- a/libs/nativewindow/include/android/native_window.h +++ b/libs/nativewindow/include/android/native_window.h @@ -376,7 +376,7 @@ int32_t ANativeWindow_clearFrameRate(ANativeWindow* window) __INTRODUCED_IN(__ANDROID_API_U__); #ifdef __cplusplus -}; +} #endif #endif // ANDROID_NATIVE_WINDOW_H diff --git a/libs/nativewindow/include/android/native_window_aidl.h b/libs/nativewindow/include/android/native_window_aidl.h new file mode 100644 index 0000000000..a252245a10 --- /dev/null +++ b/libs/nativewindow/include/android/native_window_aidl.h @@ -0,0 +1,161 @@ +/* + * 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. + */ + +/** + * @file native_window_aidl.h + * @brief NativeWindow NDK AIDL glue code + */ + +/** + * @addtogroup ANativeWindow + * + * Parcelable support for ANativeWindow. Can be used with libbinder_ndk + * + * @{ + */ + +#ifndef ANDROID_NATIVE_WINDOW_AIDL_H +#define ANDROID_NATIVE_WINDOW_AIDL_H + +#include <android/binder_parcel.h> +#include <android/native_window.h> +#include <sys/cdefs.h> + +__BEGIN_DECLS + +/** + * Read an ANativeWindow from a AParcel. The output buffer will have an + * initial reference acquired and will need to be released with + * ANativeWindow_release. + * + * Available since API level 34. + * + * \return STATUS_OK on success + * STATUS_BAD_VALUE if the parcel or outBuffer is null, or if there's an + * issue deserializing (eg, corrupted parcel) + * STATUS_BAD_TYPE if the parcel's current data position is not that of + * an ANativeWindow type + * STATUS_NO_MEMORY if an allocation fails + */ +binder_status_t ANativeWindow_readFromParcel(const AParcel* _Nonnull parcel, + ANativeWindow* _Nullable* _Nonnull outWindow) __INTRODUCED_IN(__ANDROID_API_U__); + +/** + * Write an ANativeWindow to an AParcel. + * + * Available since API level 34. + * + * \return STATUS_OK on success. + * STATUS_BAD_VALUE if either buffer or parcel is null, or if the ANativeWindow* + * fails to serialize (eg, internally corrupted) + * STATUS_NO_MEMORY if the parcel runs out of space to store the buffer & is + * unable to allocate more + * STATUS_FDS_NOT_ALLOWED if the parcel does not allow storing FDs + */ +binder_status_t ANativeWindow_writeToParcel(ANativeWindow* _Nonnull window, + AParcel* _Nonnull parcel) __INTRODUCED_IN(__ANDROID_API_U__); + +__END_DECLS + +// Only enable the AIDL glue helper if this is C++ +#ifdef __cplusplus + +namespace aidl::android::hardware { + +/** + * Wrapper class that enables interop with AIDL NDK generation + * Takes ownership of the ANativeWindow* given to it in reset() and will automatically + * destroy it in the destructor, similar to a smart pointer container + */ +class NativeWindow { +public: + NativeWindow() noexcept {} + explicit NativeWindow(ANativeWindow* _Nullable window) { + reset(window); + } + + explicit NativeWindow(NativeWindow&& other) noexcept { + mWindow = other.release(); // steal ownership from r-value + } + + ~NativeWindow() { + reset(); + } + + binder_status_t readFromParcel(const AParcel* _Nonnull parcel) { + reset(); + return ANativeWindow_readFromParcel(parcel, &mWindow); + } + + binder_status_t writeToParcel(AParcel* _Nonnull parcel) const { + if (!mWindow) { + return STATUS_BAD_VALUE; + } + return ANativeWindow_writeToParcel(mWindow, parcel); + } + + /** + * Destroys any currently owned ANativeWindow* and takes ownership of the given + * ANativeWindow* + * + * @param buffer The buffer to take ownership of + */ + void reset(ANativeWindow* _Nullable window = nullptr) noexcept { + if (mWindow) { + ANativeWindow_release(mWindow); + mWindow = nullptr; + } + if (window != nullptr) { + ANativeWindow_acquire(window); + } + mWindow = window; + } + inline ANativeWindow* _Nullable operator-> () const { return mWindow; } + inline ANativeWindow* _Nullable get() const { return mWindow; } + inline explicit operator bool () const { return mWindow != nullptr; } + + NativeWindow& operator=(NativeWindow&& other) noexcept { + mWindow = other.release(); // steal ownership from r-value + return *this; + } + + /** + * Stops managing any contained ANativeWindow*, returning it to the caller. Ownership + * is released. + * @return ANativeWindow* or null if this was empty + */ + [[nodiscard]] ANativeWindow* _Nullable release() noexcept { + ANativeWindow* _Nullable ret = mWindow; + mWindow = nullptr; + return ret; + } +private: + ANativeWindow* _Nullable mWindow = nullptr; + NativeWindow(const NativeWindow &other) = delete; + NativeWindow& operator=(const NativeWindow &other) = delete; +}; + +} // aidl::android::hardware + // +namespace aidl::android::view { + using Surface = aidl::android::hardware::NativeWindow; +} + +#endif // __cplusplus + +#endif // ANDROID_NATIVE_WINDOW_AIDL_H + +/** @} */ diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt index ce108b6096..76d23fab1d 100644 --- a/libs/nativewindow/libnativewindow.map.txt +++ b/libs/nativewindow/libnativewindow.map.txt @@ -57,6 +57,8 @@ LIBNATIVEWINDOW { ANativeWindow_setUsage; # llndk ANativeWindow_tryAllocateBuffers; # introduced=30 ANativeWindow_unlockAndPost; + ANativeWindow_readFromParcel; # introduced=UpsideDownCake + ANativeWindow_writeToParcel; # introduced=UpsideDownCake local: *; }; diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp index 9d9cb6b2bc..f1fc0a45ad 100644 --- a/libs/renderengine/RenderEngine.cpp +++ b/libs/renderengine/RenderEngine.cpp @@ -19,6 +19,7 @@ #include <cutils/properties.h> #include <log/log.h> #include "gl/GLESRenderEngine.h" +#include "renderengine/ExternalTexture.h" #include "threaded/RenderEngineThreaded.h" #include "skia/SkiaGLRenderEngine.h" @@ -70,10 +71,22 @@ ftl::Future<FenceResult> RenderEngine::drawLayers(const DisplaySettings& display base::unique_fd&& bufferFence) { const auto resultPromise = std::make_shared<std::promise<FenceResult>>(); std::future<FenceResult> resultFuture = resultPromise->get_future(); + updateProtectedContext(layers, buffer); drawLayersInternal(std::move(resultPromise), display, layers, buffer, useFramebufferCache, std::move(bufferFence)); return resultFuture; } +void RenderEngine::updateProtectedContext(const std::vector<LayerSettings>& layers, + const std::shared_ptr<ExternalTexture>& buffer) { + const bool needsProtectedContext = + (buffer && (buffer->getUsage() & GRALLOC_USAGE_PROTECTED)) || + std::any_of(layers.begin(), layers.end(), [](const LayerSettings& layer) { + const std::shared_ptr<ExternalTexture>& buffer = layer.source.buffer.buffer; + return buffer && (buffer->getUsage() & GRALLOC_USAGE_PROTECTED); + }); + useProtectedContext(needsProtectedContext); +} + } // namespace renderengine } // namespace android diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h index 1ee5cbaa3d..1b3492154b 100644 --- a/libs/renderengine/gl/GLESRenderEngine.h +++ b/libs/renderengine/gl/GLESRenderEngine.h @@ -61,7 +61,7 @@ public: std::future<void> primeCache() override; void genTextures(size_t count, uint32_t* names) override; void deleteTextures(size_t count, uint32_t const* names) override; - bool isProtected() const override { return mInProtectedContext; } + bool isProtected() const { return mInProtectedContext; } bool supportsProtectedContent() const override; void useProtectedContext(bool useProtectedContext) override; void cleanupPostRender() override; diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h index 199392c160..9182febbe0 100644 --- a/libs/renderengine/include/renderengine/RenderEngine.h +++ b/libs/renderengine/include/renderengine/RenderEngine.h @@ -126,12 +126,8 @@ public: // ----- BEGIN NEW INTERFACE ----- // queries that are required to be thread safe - virtual bool isProtected() const = 0; virtual bool supportsProtectedContent() const = 0; - // Attempt to switch RenderEngine into and out of protectedContext mode - virtual void useProtectedContext(bool useProtectedContext) = 0; - // Notify RenderEngine of changes to the dimensions of the active display // so that it can configure its internal caches accordingly. virtual void onActiveDisplaySizeChanged(ui::Size size) = 0; @@ -238,6 +234,13 @@ protected: friend class RenderEngineTest_cleanupPostRender_cleansUpOnce_Test; const RenderEngineType mRenderEngineType; + // Update protectedContext mode depending on whether or not any layer has a protected buffer. + void updateProtectedContext(const std::vector<LayerSettings>&, + const std::shared_ptr<ExternalTexture>&); + + // Attempt to switch RenderEngine into and out of protectedContext mode + virtual void useProtectedContext(bool useProtectedContext) = 0; + virtual void drawLayersInternal( const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, const DisplaySettings& display, const std::vector<LayerSettings>& layers, diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h index e7c5b8f0ab..1973c7d065 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.h +++ b/libs/renderengine/skia/SkiaRenderEngine.h @@ -68,7 +68,6 @@ public: std::future<void> primeCache() override final; void cleanupPostRender() override final; void cleanFramebufferCache() override final{ } - bool isProtected() const override final{ return mInProtectedContext; } bool supportsBackgroundBlur() override final { return mBlurFilter != nullptr; } @@ -102,6 +101,8 @@ protected: size_t getMaxViewportDims() const override final; GrDirectContext* getActiveGrContext(); + bool isProtected() const { return mInProtectedContext; } + // Implements PersistentCache as a way to monitor what SkSL shaders Skia has // cached. class SkSLCacheMonitor : public GrContextOptions::PersistentCache { diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp index 1a96289bc0..fe3a16d4bf 100644 --- a/libs/renderengine/tests/RenderEngineThreadedTest.cpp +++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp @@ -17,8 +17,10 @@ #include <cutils/properties.h> #include <gmock/gmock.h> #include <gtest/gtest.h> +#include <hardware/gralloc.h> #include <renderengine/impl/ExternalTexture.h> #include <renderengine/mock/RenderEngine.h> +#include <ui/PixelFormat.h> #include "../threaded/RenderEngineThreaded.h" namespace android { @@ -95,18 +97,6 @@ TEST_F(RenderEngineThreadedTest, getMaxViewportDims_returns0) { ASSERT_EQ(dims, result); } -TEST_F(RenderEngineThreadedTest, isProtected_returnsFalse) { - EXPECT_CALL(*mRenderEngine, isProtected()).WillOnce(Return(false)); - status_t result = mThreadedRE->isProtected(); - ASSERT_EQ(false, result); -} - -TEST_F(RenderEngineThreadedTest, isProtected_returnsTrue) { - EXPECT_CALL(*mRenderEngine, isProtected()).WillOnce(Return(true)); - size_t result = mThreadedRE->isProtected(); - ASSERT_EQ(true, result); -} - TEST_F(RenderEngineThreadedTest, supportsProtectedContent_returnsFalse) { EXPECT_CALL(*mRenderEngine, supportsProtectedContent()).WillOnce(Return(false)); status_t result = mThreadedRE->supportsProtectedContent(); @@ -119,28 +109,6 @@ TEST_F(RenderEngineThreadedTest, supportsProtectedContent_returnsTrue) { ASSERT_EQ(true, result); } -TEST_F(RenderEngineThreadedTest, useProtectedContext) { - EXPECT_CALL(*mRenderEngine, useProtectedContext(true)); - auto& ipExpect = EXPECT_CALL(*mRenderEngine, isProtected()).WillOnce(Return(false)); - EXPECT_CALL(*mRenderEngine, supportsProtectedContent()).WillOnce(Return(true)); - EXPECT_CALL(*mRenderEngine, isProtected()).After(ipExpect).WillOnce(Return(true)); - - mThreadedRE->useProtectedContext(true); - ASSERT_EQ(true, mThreadedRE->isProtected()); - - // call ANY synchronous function to ensure that useProtectedContext has completed. - mThreadedRE->getContextPriority(); - ASSERT_EQ(true, mThreadedRE->isProtected()); -} - -TEST_F(RenderEngineThreadedTest, useProtectedContext_quickReject) { - EXPECT_CALL(*mRenderEngine, useProtectedContext(false)).Times(0); - EXPECT_CALL(*mRenderEngine, isProtected()).WillOnce(Return(false)); - mThreadedRE->useProtectedContext(false); - // call ANY synchronous function to ensure that useProtectedContext has completed. - mThreadedRE->getContextPriority(); -} - TEST_F(RenderEngineThreadedTest, PostRenderCleanup_skipped) { EXPECT_CALL(*mRenderEngine, canSkipPostRenderCleanup()).WillOnce(Return(true)); EXPECT_CALL(*mRenderEngine, cleanupPostRender()).Times(0); @@ -182,6 +150,68 @@ TEST_F(RenderEngineThreadedTest, drawLayers) { base::unique_fd bufferFence; + EXPECT_CALL(*mRenderEngine, useProtectedContext(false)); + EXPECT_CALL(*mRenderEngine, drawLayersInternal) + .WillOnce([&](const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, + const renderengine::DisplaySettings&, + const std::vector<renderengine::LayerSettings>&, + const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + base::unique_fd&&) { resultPromise->set_value(Fence::NO_FENCE); }); + + ftl::Future<FenceResult> future = + mThreadedRE->drawLayers(settings, layers, buffer, false, std::move(bufferFence)); + ASSERT_TRUE(future.valid()); + auto result = future.get(); + ASSERT_TRUE(result.ok()); +} + +TEST_F(RenderEngineThreadedTest, drawLayers_protectedLayer) { + renderengine::DisplaySettings settings; + auto layerBuffer = sp<GraphicBuffer>::make(); + layerBuffer->usage |= GRALLOC_USAGE_PROTECTED; + renderengine::LayerSettings layer; + layer.source.buffer.buffer = std::make_shared< + renderengine::impl::ExternalTexture>(std::move(layerBuffer), *mRenderEngine, + renderengine::impl::ExternalTexture::Usage:: + READABLE); + std::vector<renderengine::LayerSettings> layers = {std::move(layer)}; + std::shared_ptr<renderengine::ExternalTexture> buffer = std::make_shared< + renderengine::impl:: + ExternalTexture>(sp<GraphicBuffer>::make(), *mRenderEngine, + renderengine::impl::ExternalTexture::Usage::READABLE | + renderengine::impl::ExternalTexture::Usage::WRITEABLE); + + base::unique_fd bufferFence; + + EXPECT_CALL(*mRenderEngine, useProtectedContext(true)); + EXPECT_CALL(*mRenderEngine, drawLayersInternal) + .WillOnce([&](const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, + const renderengine::DisplaySettings&, + const std::vector<renderengine::LayerSettings>&, + const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + base::unique_fd&&) { resultPromise->set_value(Fence::NO_FENCE); }); + + ftl::Future<FenceResult> future = + mThreadedRE->drawLayers(settings, layers, buffer, false, std::move(bufferFence)); + ASSERT_TRUE(future.valid()); + auto result = future.get(); + ASSERT_TRUE(result.ok()); +} + +TEST_F(RenderEngineThreadedTest, drawLayers_protectedOutputBuffer) { + renderengine::DisplaySettings settings; + std::vector<renderengine::LayerSettings> layers; + auto graphicBuffer = sp<GraphicBuffer>::make(); + graphicBuffer->usage |= GRALLOC_USAGE_PROTECTED; + std::shared_ptr<renderengine::ExternalTexture> buffer = std::make_shared< + renderengine::impl:: + ExternalTexture>(std::move(graphicBuffer), *mRenderEngine, + renderengine::impl::ExternalTexture::Usage::READABLE | + renderengine::impl::ExternalTexture::Usage::WRITEABLE); + + base::unique_fd bufferFence; + + EXPECT_CALL(*mRenderEngine, useProtectedContext(true)); EXPECT_CALL(*mRenderEngine, drawLayersInternal) .WillOnce([&](const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, const renderengine::DisplaySettings&, diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp index b41e8432a9..8aa41b3e50 100644 --- a/libs/renderengine/threaded/RenderEngineThreaded.cpp +++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp @@ -90,7 +90,6 @@ void RenderEngineThreaded::threadMain(CreateInstanceFactory factory) NO_THREAD_S } mRenderEngine = factory(); - mIsProtected = mRenderEngine->isProtected(); pthread_setname_np(pthread_self(), mThreadName); @@ -255,41 +254,11 @@ size_t RenderEngineThreaded::getMaxViewportDims() const { return mRenderEngine->getMaxViewportDims(); } -bool RenderEngineThreaded::isProtected() const { - waitUntilInitialized(); - std::lock_guard lock(mThreadMutex); - return mIsProtected; -} - bool RenderEngineThreaded::supportsProtectedContent() const { waitUntilInitialized(); return mRenderEngine->supportsProtectedContent(); } -void RenderEngineThreaded::useProtectedContext(bool useProtectedContext) { - if (isProtected() == useProtectedContext || - (useProtectedContext && !supportsProtectedContent())) { - return; - } - - { - std::lock_guard lock(mThreadMutex); - mFunctionCalls.push([useProtectedContext, this](renderengine::RenderEngine& instance) { - ATRACE_NAME("REThreaded::useProtectedContext"); - instance.useProtectedContext(useProtectedContext); - if (instance.isProtected() != useProtectedContext) { - ALOGE("Failed to switch RenderEngine context."); - // reset the cached mIsProtected value to a good state, but this does not - // prevent other callers of this method and isProtected from reading the - // invalid cached value. - mIsProtected = instance.isProtected(); - } - }); - mIsProtected = useProtectedContext; - } - mCondition.notify_one(); -} - void RenderEngineThreaded::cleanupPostRender() { if (canSkipPostRenderCleanup()) { return; @@ -334,6 +303,7 @@ ftl::Future<FenceResult> RenderEngineThreaded::drawLayers( mFunctionCalls.push([resultPromise, display, layers, buffer, useFramebufferCache, fd](renderengine::RenderEngine& instance) { ATRACE_NAME("REThreaded::drawLayers"); + instance.updateProtectedContext(layers, buffer); instance.drawLayersInternal(std::move(resultPromise), display, layers, buffer, useFramebufferCache, base::unique_fd(fd)); }); diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h index bf2ebea2a0..168e2d2b06 100644 --- a/libs/renderengine/threaded/RenderEngineThreaded.h +++ b/libs/renderengine/threaded/RenderEngineThreaded.h @@ -51,9 +51,7 @@ public: size_t getMaxTextureSize() const override; size_t getMaxViewportDims() const override; - bool isProtected() const override; bool supportsProtectedContent() const override; - void useProtectedContext(bool useProtectedContext) override; void cleanupPostRender() override; ftl::Future<FenceResult> drawLayers(const DisplaySettings& display, @@ -84,6 +82,9 @@ private: void waitUntilInitialized() const; static status_t setSchedFifo(bool enabled); + // No-op. This method is only called on leaf implementations of RenderEngine. + void useProtectedContext(bool) override {} + /* ------------------------------------------------------------------------ * Threading */ @@ -107,7 +108,6 @@ private: * Render Engine */ std::unique_ptr<renderengine::RenderEngine> mRenderEngine; - std::atomic<bool> mIsProtected = false; }; } // namespace threaded } // namespace renderengine diff --git a/libs/vibrator/ExternalVibration.cpp b/libs/vibrator/ExternalVibration.cpp index ec906458a3..80e911c65d 100644 --- a/libs/vibrator/ExternalVibration.cpp +++ b/libs/vibrator/ExternalVibration.cpp @@ -22,15 +22,6 @@ #include <log/log.h> #include <utils/Errors.h> - -// To guarantee if HapticScale enum has the same value as IExternalVibratorService -static_assert(static_cast<int>(android::os::HapticScale::MUTE) == static_cast<int>(android::os::IExternalVibratorService::SCALE_MUTE)); -static_assert(static_cast<int>(android::os::HapticScale::VERY_LOW) == static_cast<int>(android::os::IExternalVibratorService::SCALE_VERY_LOW)); -static_assert(static_cast<int>(android::os::HapticScale::LOW) == static_cast<int>(android::os::IExternalVibratorService::SCALE_LOW)); -static_assert(static_cast<int>(android::os::HapticScale::NONE) == static_cast<int>(android::os::IExternalVibratorService::SCALE_NONE)); -static_assert(static_cast<int>(android::os::HapticScale::HIGH) == static_cast<int>(android::os::IExternalVibratorService::SCALE_HIGH)); -static_assert(static_cast<int>(android::os::HapticScale::VERY_HIGH) == static_cast<int>(android::os::IExternalVibratorService::SCALE_VERY_HIGH)); - void writeAudioAttributes(const audio_attributes_t& attrs, android::Parcel* out) { out->writeInt32(attrs.usage); out->writeInt32(attrs.content_type); @@ -74,5 +65,25 @@ inline bool ExternalVibration::operator==(const ExternalVibration& rhs) const { return mToken == rhs.mToken; } +os::HapticScale ExternalVibration::externalVibrationScaleToHapticScale(int externalVibrationScale) { + switch (externalVibrationScale) { + case IExternalVibratorService::SCALE_MUTE: + return os::HapticScale::MUTE; + case IExternalVibratorService::SCALE_VERY_LOW: + return os::HapticScale::VERY_LOW; + case IExternalVibratorService::SCALE_LOW: + return os::HapticScale::LOW; + case IExternalVibratorService::SCALE_NONE: + return os::HapticScale::NONE; + case IExternalVibratorService::SCALE_HIGH: + return os::HapticScale::HIGH; + case IExternalVibratorService::SCALE_VERY_HIGH: + return os::HapticScale::VERY_HIGH; + default: + ALOGE("Unknown ExternalVibrationScale %d, not applying scaling", externalVibrationScale); + return os::HapticScale::NONE; + } +} + } // namespace os } // namespace android diff --git a/libs/vibrator/include/vibrator/ExternalVibration.h b/libs/vibrator/include/vibrator/ExternalVibration.h index 760dbce149..00cd3cd256 100644 --- a/libs/vibrator/include/vibrator/ExternalVibration.h +++ b/libs/vibrator/include/vibrator/ExternalVibration.h @@ -23,6 +23,7 @@ #include <binder/Parcelable.h> #include <system/audio.h> #include <utils/RefBase.h> +#include <vibrator/ExternalVibrationUtils.h> namespace android { namespace os { @@ -44,6 +45,10 @@ public : audio_attributes_t getAudioAttributes() const { return mAttrs; } sp<IExternalVibrationController> getController() { return mController; } + /* Converts the scale from non-public ExternalVibrationService into the HapticScale + * used by the utils. + */ + static os::HapticScale externalVibrationScaleToHapticScale(int externalVibrationScale); private: int32_t mUid; @@ -53,7 +58,7 @@ private: sp<IBinder> mToken = new BBinder(); }; -} // namespace android } // namespace os +} // namespace android #endif // ANDROID_EXTERNAL_VIBRATION_H diff --git a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h index c588bfdedd..ca219d3cbf 100644 --- a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h +++ b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h @@ -19,8 +19,6 @@ namespace android::os { -// Copied from frameworks/base/core/java/android/os/IExternalVibratorService.aidl -// The values are checked in ExternalVibration.cpp enum class HapticScale { MUTE = -100, VERY_LOW = -2, diff --git a/services/inputflinger/InputCommonConverter.cpp b/services/inputflinger/InputCommonConverter.cpp index 6db89d4759..628ce6fc9a 100644 --- a/services/inputflinger/InputCommonConverter.cpp +++ b/services/inputflinger/InputCommonConverter.cpp @@ -304,8 +304,8 @@ static void getHalPropertiesAndCoords(const NotifyMotionArgs& args, common::PointerCoords coords; // OK to copy bits because we have static_assert for pointerCoords axes coords.bits = args.pointerCoords[i].bits; - coords.values = std::vector<float>(args.pointerCoords[i].values, - args.pointerCoords[i].values + + coords.values = std::vector<float>(args.pointerCoords[i].values.cbegin(), + args.pointerCoords[i].values.cbegin() + BitSet64::count(args.pointerCoords[i].bits)); outPointerCoords.push_back(coords); } diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 694c127e40..f6982173af 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -1692,8 +1692,7 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr< } std::vector<TouchedWindow> touchedWindows = - findTouchedWindowTargetsLocked(currentTime, *entry, nextWakeupTime, - &conflictingPointerActions, + findTouchedWindowTargetsLocked(currentTime, *entry, &conflictingPointerActions, /*byref*/ injectionResult); for (const TouchedWindow& touchedWindow : touchedWindows) { LOG_ALWAYS_FATAL_IF(injectionResult != InputEventInjectionResult::SUCCEEDED, @@ -2035,9 +2034,39 @@ std::vector<Monitor> InputDispatcher::selectResponsiveMonitorsLocked( return responsiveMonitors; } +/** + * In general, touch should be always split between windows. Some exceptions: + * 1. Don't split touch is if we have an active pointer down, and a new pointer is going down that's + * from the same device, *and* the window that's receiving the current pointer does not support + * split touch. + * 2. Don't split mouse events + */ +bool InputDispatcher::shouldSplitTouch(const TouchState& touchState, + const MotionEntry& entry) const { + if (isFromSource(entry.source, AINPUT_SOURCE_MOUSE)) { + // We should never split mouse events + return false; + } + for (const TouchedWindow& touchedWindow : touchState.windows) { + if (touchedWindow.windowHandle->getInfo()->isSpy()) { + // Spy windows should not affect whether or not touch is split. + continue; + } + if (touchedWindow.windowHandle->getInfo()->supportsSplitTouch()) { + continue; + } + // Eventually, touchedWindow will contain the deviceId of each pointer that's currently + // being sent there. For now, use deviceId from touch state. + if (entry.deviceId == touchState.deviceId && !touchedWindow.pointerIds.isEmpty()) { + return false; + } + } + return true; +} + std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked( - nsecs_t currentTime, const MotionEntry& entry, nsecs_t* nextWakeupTime, - bool* outConflictingPointerActions, InputEventInjectionResult& outInjectionResult) { + nsecs_t currentTime, const MotionEntry& entry, bool* outConflictingPointerActions, + InputEventInjectionResult& outInjectionResult) { ATRACE_CALL(); std::vector<TouchedWindow> touchedWindows; @@ -2062,10 +2091,9 @@ std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked( tempTouchState = *oldState; } - bool isSplit = tempTouchState.split; - bool switchedDevice = tempTouchState.deviceId >= 0 && tempTouchState.displayId >= 0 && - (tempTouchState.deviceId != entry.deviceId || tempTouchState.source != entry.source || - tempTouchState.displayId != displayId); + bool isSplit = shouldSplitTouch(tempTouchState, entry); + const bool switchedDevice = (oldState != nullptr) && + (tempTouchState.deviceId != entry.deviceId || tempTouchState.source != entry.source); const bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE || maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER || @@ -2073,24 +2101,19 @@ std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked( const bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN || maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction); const bool isFromMouse = isFromSource(entry.source, AINPUT_SOURCE_MOUSE); - bool wrongDevice = false; if (newGesture) { bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN; - if (switchedDevice && tempTouchState.down && !down && !isHoverAction) { + if (switchedDevice && tempTouchState.isDown() && !down && !isHoverAction) { ALOGI("Dropping event because a pointer for a different device is already down " "in display %" PRId32, displayId); // TODO: test multiple simultaneous input streams. outInjectionResult = InputEventInjectionResult::FAILED; - switchedDevice = false; - wrongDevice = true; - goto Failed; + return touchedWindows; // wrong device } tempTouchState.reset(); - tempTouchState.down = down; tempTouchState.deviceId = entry.deviceId; tempTouchState.source = entry.source; - tempTouchState.displayId = displayId; isSplit = false; } else if (switchedDevice && maskedAction == AMOTION_EVENT_ACTION_MOVE) { ALOGI("Dropping move event because a pointer for a different device is already active " @@ -2098,9 +2121,7 @@ std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked( displayId); // TODO: test multiple simultaneous input streams. outInjectionResult = InputEventInjectionResult::FAILED; - switchedDevice = false; - wrongDevice = true; - goto Failed; + return touchedWindows; // wrong device } if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) { @@ -2142,7 +2163,7 @@ std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked( // No window is touched, so set split to true. This will allow the next pointer down to // be delivered to a new window which supports split touch. Pointers from a mouse device // should never be split. - tempTouchState.split = isSplit = !isFromMouse; + isSplit = !isFromMouse; } // Update hover state. @@ -2210,7 +2231,7 @@ std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked( /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */ // If the pointer is not currently down, then ignore the event. - if (!tempTouchState.down) { + if (!tempTouchState.isDown()) { if (DEBUG_FOCUS) { ALOGD("Dropping event because the pointer is not down or we previously " "dropped the pointer down event in display %" PRId32, @@ -2412,83 +2433,80 @@ std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked( Failed: // Update final pieces of touch state if the injector had permission. - if (!wrongDevice) { - if (switchedDevice) { + if (switchedDevice) { + if (DEBUG_FOCUS) { + ALOGD("Conflicting pointer actions: Switched to a different device."); + } + *outConflictingPointerActions = true; + } + + if (isHoverAction) { + // Started hovering, therefore no longer down. + if (oldState && oldState->isDown()) { if (DEBUG_FOCUS) { - ALOGD("Conflicting pointer actions: Switched to a different device."); + ALOGD("Conflicting pointer actions: Hover received while pointer was " + "down."); } *outConflictingPointerActions = true; } - - if (isHoverAction) { - // Started hovering, therefore no longer down. - if (oldState && oldState->down) { - if (DEBUG_FOCUS) { - ALOGD("Conflicting pointer actions: Hover received while pointer was " - "down."); - } - *outConflictingPointerActions = true; - } - tempTouchState.reset(); - if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER || - maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) { - tempTouchState.deviceId = entry.deviceId; - tempTouchState.source = entry.source; - tempTouchState.displayId = displayId; - } - } else if (maskedAction == AMOTION_EVENT_ACTION_UP || - maskedAction == AMOTION_EVENT_ACTION_CANCEL) { - // All pointers up or canceled. - tempTouchState.reset(); - } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { - // First pointer went down. - if (oldState && oldState->down) { - if (DEBUG_FOCUS) { - ALOGD("Conflicting pointer actions: Down received while already down."); - } - *outConflictingPointerActions = true; - } - } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) { - // One pointer went up. - int32_t pointerIndex = getMotionEventActionPointerIndex(action); - uint32_t pointerId = entry.pointerProperties[pointerIndex].id; - - for (size_t i = 0; i < tempTouchState.windows.size();) { - TouchedWindow& touchedWindow = tempTouchState.windows[i]; - touchedWindow.pointerIds.clearBit(pointerId); - if (touchedWindow.pointerIds.isEmpty()) { - tempTouchState.windows.erase(tempTouchState.windows.begin() + i); - continue; - } - i += 1; - } - } else if (!isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN) { - // If no split, we suppose all touched windows should receive pointer down. - const int32_t pointerIndex = getMotionEventActionPointerIndex(action); - for (size_t i = 0; i < tempTouchState.windows.size(); i++) { - TouchedWindow& touchedWindow = tempTouchState.windows[i]; - // Ignore drag window for it should just track one pointer. - if (mDragState && mDragState->dragWindow == touchedWindow.windowHandle) { - continue; - } - touchedWindow.pointerIds.markBit(entry.pointerProperties[pointerIndex].id); + tempTouchState.reset(); + if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER || + maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) { + tempTouchState.deviceId = entry.deviceId; + tempTouchState.source = entry.source; + } + } else if (maskedAction == AMOTION_EVENT_ACTION_UP || + maskedAction == AMOTION_EVENT_ACTION_CANCEL) { + // All pointers up or canceled. + tempTouchState.reset(); + } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { + // First pointer went down. + if (oldState && oldState->isDown()) { + if (DEBUG_FOCUS) { + ALOGD("Conflicting pointer actions: Down received while already down."); } + *outConflictingPointerActions = true; } + } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) { + // One pointer went up. + int32_t pointerIndex = getMotionEventActionPointerIndex(action); + uint32_t pointerId = entry.pointerProperties[pointerIndex].id; - // Save changes unless the action was scroll in which case the temporary touch - // state was only valid for this one action. - if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) { - if (tempTouchState.displayId >= 0) { - mTouchStatesByDisplay[displayId] = tempTouchState; - } else { - mTouchStatesByDisplay.erase(displayId); + for (size_t i = 0; i < tempTouchState.windows.size();) { + TouchedWindow& touchedWindow = tempTouchState.windows[i]; + touchedWindow.pointerIds.clearBit(pointerId); + if (touchedWindow.pointerIds.isEmpty()) { + tempTouchState.windows.erase(tempTouchState.windows.begin() + i); + continue; } + i += 1; } + } else if (!isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN) { + // If no split, we suppose all touched windows should receive pointer down. + const int32_t pointerIndex = getMotionEventActionPointerIndex(action); + for (size_t i = 0; i < tempTouchState.windows.size(); i++) { + TouchedWindow& touchedWindow = tempTouchState.windows[i]; + // Ignore drag window for it should just track one pointer. + if (mDragState && mDragState->dragWindow == touchedWindow.windowHandle) { + continue; + } + touchedWindow.pointerIds.markBit(entry.pointerProperties[pointerIndex].id); + } + } - // Update hover state. - mLastHoverWindowHandle = newHoverWindowHandle; + // Save changes unless the action was scroll in which case the temporary touch + // state was only valid for this one action. + if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) { + if (displayId >= 0) { + mTouchStatesByDisplay[displayId] = tempTouchState; + } else { + mTouchStatesByDisplay.erase(displayId); + } } + // Update hover state. + mLastHoverWindowHandle = newHoverWindowHandle; + return touchedWindows; } @@ -5082,16 +5100,16 @@ void InputDispatcher::setMaximumObscuringOpacityForTouch(float opacity) { mMaximumObscuringOpacityForTouch = opacity; } -std::pair<TouchState*, TouchedWindow*> InputDispatcher::findTouchStateAndWindowLocked( - const sp<IBinder>& token) { +std::tuple<TouchState*, TouchedWindow*, int32_t /*displayId*/> +InputDispatcher::findTouchStateWindowAndDisplayLocked(const sp<IBinder>& token) { for (auto& [displayId, state] : mTouchStatesByDisplay) { for (TouchedWindow& w : state.windows) { if (w.windowHandle->getToken() == token) { - return std::make_pair(&state, &w); + return std::make_tuple(&state, &w, displayId); } } } - return std::make_pair(nullptr, nullptr); + return std::make_tuple(nullptr, nullptr, ADISPLAY_ID_DEFAULT); } bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken, @@ -5107,13 +5125,12 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< std::scoped_lock _l(mLock); // Find the target touch state and touched window by fromToken. - auto [state, touchedWindow] = findTouchStateAndWindowLocked(fromToken); + auto [state, touchedWindow, displayId] = findTouchStateWindowAndDisplayLocked(fromToken); if (state == nullptr || touchedWindow == nullptr) { ALOGD("Focus transfer failed because from window is not being touched."); return false; } - const int32_t displayId = state->displayId; sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(toToken, displayId); if (toWindowHandle == nullptr) { ALOGW("Cannot transfer focus because to window not found."); @@ -5302,10 +5319,8 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { if (!mTouchStatesByDisplay.empty()) { dump += StringPrintf(INDENT "TouchStatesByDisplay:\n"); - for (const std::pair<int32_t, TouchState>& pair : mTouchStatesByDisplay) { - const TouchState& state = pair.second; - dump += StringPrintf(INDENT2 "%d: down=%s, split=%s, deviceId=%d, source=0x%08x\n", - state.displayId, toString(state.down), toString(state.split), + for (const auto& [displayId, state] : mTouchStatesByDisplay) { + dump += StringPrintf(INDENT2 "%d: deviceId=%d, source=0x%08x\n", displayId, state.deviceId, state.source); if (!state.windows.empty()) { dump += INDENT3 "Windows:\n"; @@ -5665,8 +5680,8 @@ status_t InputDispatcher::pilferPointersLocked(const sp<IBinder>& token) { return BAD_VALUE; } - auto [statePtr, windowPtr] = findTouchStateAndWindowLocked(token); - if (statePtr == nullptr || windowPtr == nullptr || !statePtr->down) { + auto [statePtr, windowPtr, displayId] = findTouchStateWindowAndDisplayLocked(token); + if (statePtr == nullptr || windowPtr == nullptr || windowPtr->pointerIds.isEmpty()) { ALOGW("Attempted to pilfer points from a channel without any on-going pointer streams." " Ignoring."); return BAD_VALUE; @@ -5678,11 +5693,8 @@ status_t InputDispatcher::pilferPointersLocked(const sp<IBinder>& token) { CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, "input channel stole pointer stream"); options.deviceId = state.deviceId; - options.displayId = state.displayId; - if (state.split) { - // If split pointers then selectively cancel pointers otherwise cancel all pointers - options.pointerIds = window.pointerIds; - } + options.displayId = displayId; + options.pointerIds = window.pointerIds; std::string canceledWindows; for (const TouchedWindow& w : state.windows) { const std::shared_ptr<InputChannel> channel = @@ -5701,11 +5713,7 @@ status_t InputDispatcher::pilferPointersLocked(const sp<IBinder>& token) { // This only blocks relevant pointers to be sent to other windows window.isPilferingPointers = true; - if (state.split) { - state.cancelPointersForWindowsExcept(window.pointerIds, token); - } else { - state.filterWindowsExcept(token); - } + state.cancelPointersForWindowsExcept(window.pointerIds, token); return OK; } diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index dea2caedf8..0ddbbeb27e 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -541,13 +541,13 @@ private: // shade is pulled down while we are counting down the timeout). void resetNoFocusedWindowTimeoutLocked() REQUIRES(mLock); + bool shouldSplitTouch(const TouchState& touchState, const MotionEntry& entry) const; int32_t getTargetDisplayId(const EventEntry& entry); sp<android::gui::WindowInfoHandle> findFocusedWindowTargetLocked( nsecs_t currentTime, const EventEntry& entry, nsecs_t* nextWakeupTime, android::os::InputEventInjectionResult& outInjectionResult) REQUIRES(mLock); std::vector<TouchedWindow> findTouchedWindowTargetsLocked( - nsecs_t currentTime, const MotionEntry& entry, nsecs_t* nextWakeupTime, - bool* outConflictingPointerActions, + nsecs_t currentTime, const MotionEntry& entry, bool* outConflictingPointerActions, android::os::InputEventInjectionResult& outInjectionResult) REQUIRES(mLock); std::vector<Monitor> selectResponsiveMonitorsLocked( const std::vector<Monitor>& gestureMonitors) const REQUIRES(mLock); @@ -676,8 +676,8 @@ private: bool handled) REQUIRES(mLock); // Find touched state and touched window by token. - std::pair<TouchState*, TouchedWindow*> findTouchStateAndWindowLocked(const sp<IBinder>& token) - REQUIRES(mLock); + std::tuple<TouchState*, TouchedWindow*, int32_t /*displayId*/> + findTouchStateWindowAndDisplayLocked(const sp<IBinder>& token) REQUIRES(mLock); // Statistics gathering. LatencyAggregator mLatencyAggregator GUARDED_BY(mLock); diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp index cf0c38af33..f5b7cb88d9 100644 --- a/services/inputflinger/dispatcher/TouchState.cpp +++ b/services/inputflinger/dispatcher/TouchState.cpp @@ -31,10 +31,6 @@ void TouchState::reset() { void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle, int32_t targetFlags, BitSet32 pointerIds, std::optional<nsecs_t> eventTime) { - if (targetFlags & InputTarget::FLAG_SPLIT) { - split = true; - } - for (size_t i = 0; i < windows.size(); i++) { TouchedWindow& touchedWindow = windows[i]; if (touchedWindow.windowHandle == windowHandle) { @@ -105,11 +101,6 @@ void TouchState::cancelPointersForNonPilferingWindows(const BitSet32 pointerIds) std::erase_if(windows, [](const TouchedWindow& w) { return w.pointerIds.isEmpty(); }); } -void TouchState::filterWindowsExcept(const sp<IBinder>& token) { - std::erase_if(windows, - [&token](const TouchedWindow& w) { return w.windowHandle->getToken() != token; }); -} - sp<WindowInfoHandle> TouchState::getFirstForegroundWindowHandle() const { for (size_t i = 0; i < windows.size(); i++) { const TouchedWindow& window = windows[i]; @@ -147,14 +138,9 @@ sp<WindowInfoHandle> TouchState::getWallpaperWindow() const { return nullptr; } -sp<WindowInfoHandle> TouchState::getWindow(const sp<IBinder>& token) const { - for (const TouchedWindow& touchedWindow : windows) { - const auto& windowHandle = touchedWindow.windowHandle; - if (windowHandle->getToken() == token) { - return windowHandle; - } - } - return nullptr; +bool TouchState::isDown() const { + return std::any_of(windows.begin(), windows.end(), + [](const TouchedWindow& window) { return !window.pointerIds.isEmpty(); }); } } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h index cf5f1e58c6..d1d3e9a1bc 100644 --- a/services/inputflinger/dispatcher/TouchState.h +++ b/services/inputflinger/dispatcher/TouchState.h @@ -28,15 +28,10 @@ class WindowInfoHandle; namespace inputdispatcher { struct TouchState { - bool down = false; - bool split = false; - // id of the device that is currently down, others are rejected int32_t deviceId = -1; // source of the device that is current down, others are rejected uint32_t source = 0; - // id to the display that currently has a touch, others are rejected - int32_t displayId = ADISPLAY_ID_NONE; std::vector<TouchedWindow> windows; @@ -50,7 +45,6 @@ struct TouchState { std::optional<nsecs_t> eventTime = std::nullopt); void removeWindowByToken(const sp<IBinder>& token); void filterNonAsIsTouchWindows(); - void filterWindowsExcept(const sp<IBinder>& token); // Cancel pointers for current set of windows except the window with particular binder token. void cancelPointersForWindowsExcept(const BitSet32 pointerIds, const sp<IBinder>& token); @@ -61,7 +55,8 @@ struct TouchState { sp<android::gui::WindowInfoHandle> getFirstForegroundWindowHandle() const; bool isSlippery() const; sp<android::gui::WindowInfoHandle> getWallpaperWindow() const; - sp<android::gui::WindowInfoHandle> getWindow(const sp<IBinder>&) const; + // Whether any of the windows are currently being touched + bool isDown() const; }; } // namespace inputdispatcher diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp index c5e1f0ca94..24168a12a6 100644 --- a/services/inputflinger/reader/Android.bp +++ b/services/inputflinger/reader/Android.bp @@ -54,6 +54,8 @@ filegroup { "mapper/VibratorInputMapper.cpp", "mapper/accumulator/CursorButtonAccumulator.cpp", "mapper/accumulator/CursorScrollAccumulator.cpp", + "mapper/accumulator/HidUsageAccumulator.cpp", + "mapper/accumulator/MultiTouchMotionAccumulator.cpp", "mapper/accumulator/SingleTouchMotionAccumulator.cpp", "mapper/accumulator/TouchButtonAccumulator.cpp", ], diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp index 18d03f8bab..0aaef53c0d 100644 --- a/services/inputflinger/reader/EventHub.cpp +++ b/services/inputflinger/reader/EventHub.cpp @@ -149,6 +149,14 @@ static std::string sha1(const std::string& in) { return out; } +/* The set of all Android key codes that correspond to buttons (bit-switches) on a stylus. */ +static constexpr std::array<int32_t, 4> STYLUS_BUTTON_KEYCODES = { + AKEYCODE_STYLUS_BUTTON_PRIMARY, + AKEYCODE_STYLUS_BUTTON_SECONDARY, + AKEYCODE_STYLUS_BUTTON_TERTIARY, + AKEYCODE_STYLUS_BUTTON_TAIL, +}; + /** * Return true if name matches "v4l-touch*" */ @@ -2171,13 +2179,15 @@ void EventHub::openDeviceLocked(const std::string& devicePath) { device->readDeviceBitMask(EVIOCGBIT(EV_MSC, 0), device->mscBitmask); device->readDeviceBitMask(EVIOCGPROP(0), device->propBitmask); - // See if this is a keyboard. Ignore everything in the button range except for - // joystick and gamepad buttons which are handled like keyboards for the most part. + // See if this is a device with keys. This could be full keyboard, or other devices like + // gamepads, joysticks, and styluses with buttons that should generate key presses. bool haveKeyboardKeys = device->keyBitmask.any(0, BTN_MISC) || device->keyBitmask.any(BTN_WHEEL, KEY_MAX + 1); bool haveGamepadButtons = device->keyBitmask.any(BTN_MISC, BTN_MOUSE) || device->keyBitmask.any(BTN_JOYSTICK, BTN_DIGI); - if (haveKeyboardKeys || haveGamepadButtons) { + bool haveStylusButtons = device->keyBitmask.test(BTN_STYLUS) || + device->keyBitmask.test(BTN_STYLUS2) || device->keyBitmask.test(BTN_STYLUS3); + if (haveKeyboardKeys || haveGamepadButtons || haveStylusButtons) { device->classes |= InputDeviceClass::KEYBOARD; } @@ -2187,11 +2197,13 @@ void EventHub::openDeviceLocked(const std::string& devicePath) { device->classes |= InputDeviceClass::CURSOR; } - // See if this is a rotary encoder type device. + // See if the device is specially configured to be of a certain type. std::string deviceType; if (device->configuration && device->configuration->tryGetProperty("device.type", deviceType)) { if (deviceType == "rotaryEncoder") { device->classes |= InputDeviceClass::ROTARY_ENCODER; + } else if (deviceType == "externalStylus") { + device->classes |= InputDeviceClass::EXTERNAL_STYLUS; } } @@ -2208,14 +2220,10 @@ void EventHub::openDeviceLocked(const std::string& devicePath) { } else if (device->keyBitmask.test(BTN_TOUCH) && device->absBitmask.test(ABS_X) && device->absBitmask.test(ABS_Y)) { device->classes |= InputDeviceClass::TOUCH; - // Is this a BT stylus? + // Is this a stylus that reports contact/pressure independently of touch coordinates? } else if ((device->absBitmask.test(ABS_PRESSURE) || device->keyBitmask.test(BTN_TOUCH)) && !device->absBitmask.test(ABS_X) && !device->absBitmask.test(ABS_Y)) { device->classes |= InputDeviceClass::EXTERNAL_STYLUS; - // Keyboard will try to claim some of the buttons but we really want to reserve those so we - // can fuse it with the touch screen data, so just take them back. Note this means an - // external stylus cannot also be a keyboard device. - device->classes &= ~InputDeviceClass::KEYBOARD; } // See if this device is a joystick. @@ -2300,6 +2308,16 @@ void EventHub::openDeviceLocked(const std::string& devicePath) { break; } } + + // See if this device has any stylus buttons that we would want to fuse with touch data. + if (!device->classes.any(InputDeviceClass::TOUCH | InputDeviceClass::TOUCH_MT)) { + for (int32_t keycode : STYLUS_BUTTON_KEYCODES) { + if (device->hasKeycodeLocked(keycode)) { + device->classes |= InputDeviceClass::EXTERNAL_STYLUS; + break; + } + } + } } // If the device isn't recognized as something we handle, don't monitor it. diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp index 5291776638..e6ab8722f6 100644 --- a/services/inputflinger/reader/InputDevice.cpp +++ b/services/inputflinger/reader/InputDevice.cpp @@ -313,7 +313,10 @@ std::list<NotifyArgs> InputDevice::configure(nsecs_t when, const InputReaderConf } } - if (!changes || (changes & InputReaderConfiguration::CHANGE_ENABLED_STATE)) { + if (changes & InputReaderConfiguration::CHANGE_ENABLED_STATE) { + // Do not execute this code on the first configure, because 'setEnabled' would call + // InputMapper::reset, and you can't reset a mapper before it has been configured. + // The mappers are configured for the first time at the bottom of this function. auto it = config->disabledDevices.find(mId); bool enabled = it == config->disabledDevices.end(); out += setEnabled(enabled, when); diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h index 6933ec7399..8e5f15f338 100644 --- a/services/inputflinger/reader/include/EventHub.h +++ b/services/inputflinger/reader/include/EventHub.h @@ -67,22 +67,15 @@ struct RawEvent { /* Describes an absolute axis. */ struct RawAbsoluteAxisInfo { - bool valid; // true if the information is valid, false otherwise - - int32_t minValue; // minimum value - int32_t maxValue; // maximum value - int32_t flat; // center flat position, eg. flat == 8 means center is between -8 and 8 - int32_t fuzz; // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise - int32_t resolution; // resolution in units per mm or radians per mm - - inline void clear() { - valid = false; - minValue = 0; - maxValue = 0; - flat = 0; - fuzz = 0; - resolution = 0; - } + bool valid{false}; // true if the information is valid, false otherwise + + int32_t minValue{}; // minimum value + int32_t maxValue{}; // maximum value + int32_t flat{}; // center flat position, eg. flat == 8 means center is between -8 and 8 + int32_t fuzz{}; // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise + int32_t resolution{}; // resolution in units per mm or radians per mm + + inline void clear() { *this = RawAbsoluteAxisInfo(); } }; /* diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h index b9a2b4ce92..439123bf1d 100644 --- a/services/inputflinger/reader/include/InputDevice.h +++ b/services/inputflinger/reader/include/InputDevice.h @@ -378,8 +378,11 @@ public: mEventHub->getAbsoluteAxisInfo(mId, code, &info); return info.valid; } - inline bool isKeyPressed(int32_t code) const { - return mEventHub->getScanCodeState(mId, code) == AKEY_STATE_DOWN; + inline bool isKeyPressed(int32_t scanCode) const { + return mEventHub->getScanCodeState(mId, scanCode) == AKEY_STATE_DOWN; + } + inline bool isKeyCodePressed(int32_t keyCode) const { + return mEventHub->getKeyCodeState(mId, keyCode) == AKEY_STATE_DOWN; } inline int32_t getAbsoluteAxisValue(int32_t code) const { int32_t value; diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp index 0404c9acc1..56fc5fa99a 100644 --- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp +++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp @@ -24,7 +24,7 @@ namespace android { ExternalStylusInputMapper::ExternalStylusInputMapper(InputDeviceContext& deviceContext) - : InputMapper(deviceContext) {} + : InputMapper(deviceContext), mTouchButtonAccumulator(deviceContext) {} uint32_t ExternalStylusInputMapper::getSources() const { return AINPUT_SOURCE_STYLUS; @@ -48,13 +48,13 @@ std::list<NotifyArgs> ExternalStylusInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) { getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPressureAxis); - mTouchButtonAccumulator.configure(getDeviceContext()); + mTouchButtonAccumulator.configure(); return {}; } std::list<NotifyArgs> ExternalStylusInputMapper::reset(nsecs_t when) { mSingleTouchMotionAccumulator.reset(getDeviceContext()); - mTouchButtonAccumulator.reset(getDeviceContext()); + mTouchButtonAccumulator.reset(); return InputMapper::reset(when); } diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp index 8704d1b211..da9413e4ca 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp @@ -24,67 +24,70 @@ namespace android { // --- Static Definitions --- -static int32_t rotateValueUsingRotationMap(int32_t value, int32_t orientation, - const int32_t map[][4], size_t mapSize) { +static int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) { + static constexpr int32_t KEYCODE_ROTATION_MAP[][4] = { + // key codes enumerated counter-clockwise with the original (unrotated) key first + // no rotation, 90 degree rotation, 180 degree rotation, 270 degree rotation + {AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT}, + {AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN}, + {AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT}, + {AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP}, + {AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT, + AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT}, + {AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP, + AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN}, + {AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT, + AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT}, + {AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN, + AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP}, + }; + + LOG_ALWAYS_FATAL_IF(orientation < 0 || orientation > 3, "Invalid orientation: %d", orientation); if (orientation != DISPLAY_ORIENTATION_0) { - for (size_t i = 0; i < mapSize; i++) { - if (value == map[i][0]) { - return map[i][orientation]; + for (const auto& rotation : KEYCODE_ROTATION_MAP) { + if (rotation[DISPLAY_ORIENTATION_0] == keyCode) { + return rotation[orientation]; } } } - return value; + return keyCode; } -static const int32_t keyCodeRotationMap[][4] = { - // key codes enumerated counter-clockwise with the original (unrotated) key first - // no rotation, 90 degree rotation, 180 degree rotation, 270 degree rotation - {AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT}, - {AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN}, - {AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT}, - {AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP}, - {AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT, - AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT}, - {AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP, - AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN}, - {AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT, - AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT}, - {AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN, - AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP}, -}; - -static const size_t keyCodeRotationMapSize = - sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]); - -static int32_t rotateStemKey(int32_t value, int32_t orientation, const int32_t map[][2], - size_t mapSize) { - if (orientation == DISPLAY_ORIENTATION_180) { - for (size_t i = 0; i < mapSize; i++) { - if (value == map[i][0]) { - return map[i][1]; - } - } - } - return value; +static bool isSupportedScanCode(int32_t scanCode) { + // KeyboardInputMapper handles keys from keyboards, gamepads, and styluses. + return scanCode < BTN_MOUSE || (scanCode >= BTN_JOYSTICK && scanCode < BTN_DIGI) || + scanCode == BTN_STYLUS || scanCode == BTN_STYLUS2 || scanCode == BTN_STYLUS3 || + scanCode >= BTN_WHEEL; } -// The mapping can be defined using input device configuration properties keyboard.rotated.stem_X -static int32_t stemKeyRotationMap[][2] = { - // key codes enumerated with the original (unrotated) key first - // no rotation, 180 degree rotation - {AKEYCODE_STEM_PRIMARY, AKEYCODE_STEM_PRIMARY}, - {AKEYCODE_STEM_1, AKEYCODE_STEM_1}, - {AKEYCODE_STEM_2, AKEYCODE_STEM_2}, - {AKEYCODE_STEM_3, AKEYCODE_STEM_3}, -}; - -static const size_t stemKeyRotationMapSize = - sizeof(stemKeyRotationMap) / sizeof(stemKeyRotationMap[0]); - -static int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) { - keyCode = rotateStemKey(keyCode, orientation, stemKeyRotationMap, stemKeyRotationMapSize); - return rotateValueUsingRotationMap(keyCode, orientation, keyCodeRotationMap, - keyCodeRotationMapSize); +static bool isMediaKey(int32_t keyCode) { + switch (keyCode) { + case AKEYCODE_MEDIA_PLAY: + case AKEYCODE_MEDIA_PAUSE: + case AKEYCODE_MEDIA_PLAY_PAUSE: + case AKEYCODE_MUTE: + case AKEYCODE_HEADSETHOOK: + case AKEYCODE_MEDIA_STOP: + case AKEYCODE_MEDIA_NEXT: + case AKEYCODE_MEDIA_PREVIOUS: + case AKEYCODE_MEDIA_REWIND: + case AKEYCODE_MEDIA_RECORD: + case AKEYCODE_MEDIA_FAST_FORWARD: + case AKEYCODE_MEDIA_SKIP_FORWARD: + case AKEYCODE_MEDIA_SKIP_BACKWARD: + case AKEYCODE_MEDIA_STEP_FORWARD: + case AKEYCODE_MEDIA_STEP_BACKWARD: + case AKEYCODE_MEDIA_AUDIO_TRACK: + case AKEYCODE_VOLUME_UP: + case AKEYCODE_VOLUME_DOWN: + case AKEYCODE_VOLUME_MUTE: + case AKEYCODE_TV_AUDIO_DESCRIPTION: + case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP: + case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN: + return true; + default: + return false; + } } // --- KeyboardInputMapper --- @@ -93,8 +96,6 @@ KeyboardInputMapper::KeyboardInputMapper(InputDeviceContext& deviceContext, uint int32_t keyboardType) : InputMapper(deviceContext), mSource(source), mKeyboardType(keyboardType) {} -KeyboardInputMapper::~KeyboardInputMapper() {} - uint32_t KeyboardInputMapper::getSources() const { return mSource; } @@ -130,7 +131,7 @@ void KeyboardInputMapper::dump(std::string& dump) { } std::optional<DisplayViewport> KeyboardInputMapper::findViewport( - nsecs_t when, const InputReaderConfiguration* config) { + const InputReaderConfiguration* config) { if (getDeviceContext().getAssociatedViewport()) { return getDeviceContext().getAssociatedViewport(); } @@ -154,35 +155,16 @@ std::list<NotifyArgs> KeyboardInputMapper::configure(nsecs_t when, } if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { - mViewport = findViewport(when, config); + mViewport = findViewport(config); } return out; } -static void mapStemKey(int32_t keyCode, const PropertyMap& config, char const* property) { - int32_t mapped = 0; - if (config.tryGetProperty(property, mapped) && mapped > 0) { - for (size_t i = 0; i < stemKeyRotationMapSize; i++) { - if (stemKeyRotationMap[i][0] == keyCode) { - stemKeyRotationMap[i][1] = mapped; - return; - } - } - } -} - void KeyboardInputMapper::configureParameters() { mParameters.orientationAware = false; const PropertyMap& config = getDeviceContext().getConfiguration(); config.tryGetProperty("keyboard.orientationAware", mParameters.orientationAware); - if (mParameters.orientationAware) { - mapStemKey(AKEYCODE_STEM_PRIMARY, config, "keyboard.rotated.stem_primary"); - mapStemKey(AKEYCODE_STEM_1, config, "keyboard.rotated.stem_1"); - mapStemKey(AKEYCODE_STEM_2, config, "keyboard.rotated.stem_2"); - mapStemKey(AKEYCODE_STEM_3, config, "keyboard.rotated.stem_3"); - } - mParameters.handlesKeyRepeat = false; config.tryGetProperty("keyboard.handlesKeyRepeat", mParameters.handlesKeyRepeat); @@ -190,7 +172,7 @@ void KeyboardInputMapper::configureParameters() { config.tryGetProperty("keyboard.doNotWakeByDefault", mParameters.doNotWakeByDefault); } -void KeyboardInputMapper::dumpParameters(std::string& dump) { +void KeyboardInputMapper::dumpParameters(std::string& dump) const { dump += INDENT3 "Parameters:\n"; dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware)); dump += StringPrintf(INDENT4 "HandlesKeyRepeat: %s\n", toString(mParameters.handlesKeyRepeat)); @@ -198,7 +180,7 @@ void KeyboardInputMapper::dumpParameters(std::string& dump) { std::list<NotifyArgs> KeyboardInputMapper::reset(nsecs_t when) { std::list<NotifyArgs> out = cancelAllDownKeys(when); - mCurrentHidUsage = 0; + mHidUsageAccumulator.reset(); resetLedState(); @@ -208,68 +190,21 @@ std::list<NotifyArgs> KeyboardInputMapper::reset(nsecs_t when) { std::list<NotifyArgs> KeyboardInputMapper::process(const RawEvent* rawEvent) { std::list<NotifyArgs> out; + mHidUsageAccumulator.process(*rawEvent); switch (rawEvent->type) { case EV_KEY: { int32_t scanCode = rawEvent->code; - int32_t usageCode = mCurrentHidUsage; - mCurrentHidUsage = 0; - if (isKeyboardOrGamepadKey(scanCode)) { + if (isSupportedScanCode(scanCode)) { out += processKey(rawEvent->when, rawEvent->readTime, rawEvent->value != 0, - scanCode, usageCode); - } - break; - } - case EV_MSC: { - if (rawEvent->code == MSC_SCAN) { - mCurrentHidUsage = rawEvent->value; + scanCode, mHidUsageAccumulator.consumeCurrentHidUsage()); } break; } - case EV_SYN: { - if (rawEvent->code == SYN_REPORT) { - mCurrentHidUsage = 0; - } - } } return out; } -bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) { - return scanCode < BTN_MOUSE || scanCode >= BTN_WHEEL || - (scanCode >= BTN_MISC && scanCode < BTN_MOUSE) || - (scanCode >= BTN_JOYSTICK && scanCode < BTN_DIGI); -} - -bool KeyboardInputMapper::isMediaKey(int32_t keyCode) { - switch (keyCode) { - case AKEYCODE_MEDIA_PLAY: - case AKEYCODE_MEDIA_PAUSE: - case AKEYCODE_MEDIA_PLAY_PAUSE: - case AKEYCODE_MUTE: - case AKEYCODE_HEADSETHOOK: - case AKEYCODE_MEDIA_STOP: - case AKEYCODE_MEDIA_NEXT: - case AKEYCODE_MEDIA_PREVIOUS: - case AKEYCODE_MEDIA_REWIND: - case AKEYCODE_MEDIA_RECORD: - case AKEYCODE_MEDIA_FAST_FORWARD: - case AKEYCODE_MEDIA_SKIP_FORWARD: - case AKEYCODE_MEDIA_SKIP_BACKWARD: - case AKEYCODE_MEDIA_STEP_FORWARD: - case AKEYCODE_MEDIA_STEP_BACKWARD: - case AKEYCODE_MEDIA_AUDIO_TRACK: - case AKEYCODE_VOLUME_UP: - case AKEYCODE_VOLUME_DOWN: - case AKEYCODE_VOLUME_MUTE: - case AKEYCODE_TV_AUDIO_DESCRIPTION: - case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP: - case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN: - return true; - } - return false; -} - std::list<NotifyArgs> KeyboardInputMapper::processKey(nsecs_t when, nsecs_t readTime, bool down, int32_t scanCode, int32_t usageCode) { std::list<NotifyArgs> out; @@ -285,6 +220,7 @@ std::list<NotifyArgs> KeyboardInputMapper::processKey(nsecs_t when, nsecs_t read } nsecs_t downTime = when; + std::optional<size_t> keyDownIndex = findKeyDownIndex(scanCode); if (down) { // Rotate key codes according to orientation if needed. if (mParameters.orientationAware) { @@ -292,11 +228,10 @@ std::list<NotifyArgs> KeyboardInputMapper::processKey(nsecs_t when, nsecs_t read } // Add key down. - ssize_t keyDownIndex = findKeyDown(scanCode); - if (keyDownIndex >= 0) { + if (keyDownIndex) { // key repeat, be sure to use same keycode as before in case of rotation - keyCode = mKeyDowns[keyDownIndex].keyCode; - downTime = mKeyDowns[keyDownIndex].downTime; + keyCode = mKeyDowns[*keyDownIndex].keyCode; + downTime = mKeyDowns[*keyDownIndex].downTime; } else { // key down if ((policyFlags & POLICY_FLAG_VIRTUAL) && @@ -315,12 +250,11 @@ std::list<NotifyArgs> KeyboardInputMapper::processKey(nsecs_t when, nsecs_t read } } else { // Remove key down. - ssize_t keyDownIndex = findKeyDown(scanCode); - if (keyDownIndex >= 0) { + if (keyDownIndex) { // key up, be sure to use same keycode as before in case of rotation - keyCode = mKeyDowns[keyDownIndex].keyCode; - downTime = mKeyDowns[keyDownIndex].downTime; - mKeyDowns.erase(mKeyDowns.begin() + (size_t)keyDownIndex); + keyCode = mKeyDowns[*keyDownIndex].keyCode; + downTime = mKeyDowns[*keyDownIndex].downTime; + mKeyDowns.erase(mKeyDowns.begin() + *keyDownIndex); } else { // key was not actually down ALOGI("Dropping key up from device %s because the key was not down. " @@ -353,22 +287,22 @@ std::list<NotifyArgs> KeyboardInputMapper::processKey(nsecs_t when, nsecs_t read policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT; } - out.push_back(NotifyKeyArgs(getContext()->getNextId(), when, readTime, getDeviceId(), mSource, - getDisplayId(), policyFlags, - down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, - AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, - downTime)); + out.emplace_back(NotifyKeyArgs(getContext()->getNextId(), when, readTime, getDeviceId(), + mSource, getDisplayId(), policyFlags, + down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, + AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, + downTime)); return out; } -ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) { +std::optional<size_t> KeyboardInputMapper::findKeyDownIndex(int32_t scanCode) { size_t n = mKeyDowns.size(); for (size_t i = 0; i < n; i++) { if (mKeyDowns[i].scanCode == scanCode) { return i; } } - return -1; + return {}; } int32_t KeyboardInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { @@ -481,12 +415,12 @@ std::list<NotifyArgs> KeyboardInputMapper::cancelAllDownKeys(nsecs_t when) { std::list<NotifyArgs> out; size_t n = mKeyDowns.size(); for (size_t i = 0; i < n; i++) { - out.push_back(NotifyKeyArgs(getContext()->getNextId(), when, - systemTime(SYSTEM_TIME_MONOTONIC), getDeviceId(), mSource, - getDisplayId(), 0 /*policyFlags*/, AKEY_EVENT_ACTION_UP, - AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_CANCELED, - mKeyDowns[i].keyCode, mKeyDowns[i].scanCode, AMETA_NONE, - mKeyDowns[i].downTime)); + out.emplace_back(NotifyKeyArgs(getContext()->getNextId(), when, + systemTime(SYSTEM_TIME_MONOTONIC), getDeviceId(), mSource, + getDisplayId(), 0 /*policyFlags*/, AKEY_EVENT_ACTION_UP, + AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_CANCELED, + mKeyDowns[i].keyCode, mKeyDowns[i].scanCode, AMETA_NONE, + mKeyDowns[i].downTime)); } mKeyDowns.clear(); mMetaState = AMETA_NONE; diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h index 8d72ee9629..11d5ad26e8 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h @@ -16,6 +16,7 @@ #pragma once +#include "HidUsageAccumulator.h" #include "InputMapper.h" namespace android { @@ -23,7 +24,7 @@ namespace android { class KeyboardInputMapper : public InputMapper { public: KeyboardInputMapper(InputDeviceContext& deviceContext, uint32_t source, int32_t keyboardType); - virtual ~KeyboardInputMapper(); + ~KeyboardInputMapper() override = default; uint32_t getSources() const override; void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; @@ -47,58 +48,54 @@ public: private: // The current viewport. - std::optional<DisplayViewport> mViewport; + std::optional<DisplayViewport> mViewport{}; struct KeyDown { - nsecs_t downTime; - int32_t keyCode; - int32_t scanCode; + nsecs_t downTime{}; + int32_t keyCode{}; + int32_t scanCode{}; }; - uint32_t mSource; - int32_t mKeyboardType; + uint32_t mSource{}; + int32_t mKeyboardType{}; - std::vector<KeyDown> mKeyDowns; // keys that are down - int32_t mMetaState; + std::vector<KeyDown> mKeyDowns{}; // keys that are down + int32_t mMetaState{}; - int32_t mCurrentHidUsage; // most recent HID usage seen this packet, or 0 if none + HidUsageAccumulator mHidUsageAccumulator; struct LedState { - bool avail; // led is available - bool on; // we think the led is currently on + bool avail{}; // led is available + bool on{}; // we think the led is currently on }; - LedState mCapsLockLedState; - LedState mNumLockLedState; - LedState mScrollLockLedState; + LedState mCapsLockLedState{}; + LedState mNumLockLedState{}; + LedState mScrollLockLedState{}; // Immutable configuration parameters. struct Parameters { - bool orientationAware; - bool handlesKeyRepeat; - bool doNotWakeByDefault; - } mParameters; + bool orientationAware{}; + bool handlesKeyRepeat{}; + bool doNotWakeByDefault{}; + } mParameters{}; void configureParameters(); - void dumpParameters(std::string& dump); + void dumpParameters(std::string& dump) const; int32_t getOrientation(); int32_t getDisplayId(); - bool isKeyboardOrGamepadKey(int32_t scanCode); - bool isMediaKey(int32_t keyCode); - [[nodiscard]] std::list<NotifyArgs> processKey(nsecs_t when, nsecs_t readTime, bool down, int32_t scanCode, int32_t usageCode); bool updateMetaStateIfNeeded(int32_t keyCode, bool down); - ssize_t findKeyDown(int32_t scanCode); + std::optional<size_t> findKeyDownIndex(int32_t scanCode); void resetLedState(); void initializeLedState(LedState& ledState, int32_t led); void updateLedStateForModifier(LedState& ledState, int32_t led, int32_t modifier, bool reset); - std::optional<DisplayViewport> findViewport(nsecs_t when, - const InputReaderConfiguration* config); + std::optional<DisplayViewport> findViewport(const InputReaderConfiguration* config); [[nodiscard]] std::list<NotifyArgs> cancelAllDownKeys(nsecs_t when); }; diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp index acba4f6513..8e757a5bf7 100644 --- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp @@ -28,163 +28,6 @@ namespace android { // Maximum number of slots supported when using the slot-based Multitouch Protocol B. static constexpr size_t MAX_SLOTS = 32; -// --- MultiTouchMotionAccumulator --- - -MultiTouchMotionAccumulator::MultiTouchMotionAccumulator() - : mCurrentSlot(-1), - mUsingSlotsProtocol(false), - mHaveStylus(false) {} - -void MultiTouchMotionAccumulator::configure(InputDeviceContext& deviceContext, size_t slotCount, - bool usingSlotsProtocol) { - mUsingSlotsProtocol = usingSlotsProtocol; - mHaveStylus = deviceContext.hasAbsoluteAxis(ABS_MT_TOOL_TYPE); - mSlots = std::vector<Slot>(slotCount); - - mCurrentSlot = -1; - if (mUsingSlotsProtocol) { - // Query the driver for the current slot index and use it as the initial slot - // before we start reading events from the device. It is possible that the - // current slot index will not be the same as it was when the first event was - // written into the evdev buffer, which means the input mapper could start - // out of sync with the initial state of the events in the evdev buffer. - // In the extremely unlikely case that this happens, the data from - // two slots will be confused until the next ABS_MT_SLOT event is received. - // This can cause the touch point to "jump", but at least there will be - // no stuck touches. - int32_t initialSlot; - if (const auto status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot); - status == OK) { - mCurrentSlot = initialSlot; - } else { - ALOGD("Could not retrieve current multi-touch slot index. status=%d", status); - } - } -} - -void MultiTouchMotionAccumulator::resetSlots() { - for (Slot& slot : mSlots) { - slot.clear(); - } - mCurrentSlot = -1; -} - -void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) { - if (rawEvent->type == EV_ABS) { - bool newSlot = false; - if (mUsingSlotsProtocol) { - if (rawEvent->code == ABS_MT_SLOT) { - mCurrentSlot = rawEvent->value; - newSlot = true; - } - } else if (mCurrentSlot < 0) { - mCurrentSlot = 0; - } - - if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlots.size()) { - if (DEBUG_POINTERS) { - if (newSlot) { - ALOGW("MultiTouch device emitted invalid slot index %d but it " - "should be between 0 and %zd; ignoring this slot.", - mCurrentSlot, mSlots.size() - 1); - } - } - } else { - Slot& slot = mSlots[mCurrentSlot]; - // If mUsingSlotsProtocol is true, it means the raw pointer has axis info of - // ABS_MT_TRACKING_ID and ABS_MT_SLOT, so driver should send a valid trackingId while - // updating the slot. - if (!mUsingSlotsProtocol) { - slot.mInUse = true; - } - - switch (rawEvent->code) { - case ABS_MT_POSITION_X: - slot.mAbsMTPositionX = rawEvent->value; - warnIfNotInUse(*rawEvent, slot); - break; - case ABS_MT_POSITION_Y: - slot.mAbsMTPositionY = rawEvent->value; - warnIfNotInUse(*rawEvent, slot); - break; - case ABS_MT_TOUCH_MAJOR: - slot.mAbsMTTouchMajor = rawEvent->value; - break; - case ABS_MT_TOUCH_MINOR: - slot.mAbsMTTouchMinor = rawEvent->value; - slot.mHaveAbsMTTouchMinor = true; - break; - case ABS_MT_WIDTH_MAJOR: - slot.mAbsMTWidthMajor = rawEvent->value; - break; - case ABS_MT_WIDTH_MINOR: - slot.mAbsMTWidthMinor = rawEvent->value; - slot.mHaveAbsMTWidthMinor = true; - break; - case ABS_MT_ORIENTATION: - slot.mAbsMTOrientation = rawEvent->value; - break; - case ABS_MT_TRACKING_ID: - if (mUsingSlotsProtocol && rawEvent->value < 0) { - // The slot is no longer in use but it retains its previous contents, - // which may be reused for subsequent touches. - slot.mInUse = false; - } else { - slot.mInUse = true; - slot.mAbsMTTrackingId = rawEvent->value; - } - break; - case ABS_MT_PRESSURE: - slot.mAbsMTPressure = rawEvent->value; - break; - case ABS_MT_DISTANCE: - slot.mAbsMTDistance = rawEvent->value; - break; - case ABS_MT_TOOL_TYPE: - slot.mAbsMTToolType = rawEvent->value; - slot.mHaveAbsMTToolType = true; - break; - } - } - } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) { - // MultiTouch Sync: The driver has returned all data for *one* of the pointers. - mCurrentSlot += 1; - } -} - -void MultiTouchMotionAccumulator::finishSync() { - if (!mUsingSlotsProtocol) { - resetSlots(); - } -} - -bool MultiTouchMotionAccumulator::hasStylus() const { - return mHaveStylus; -} - -void MultiTouchMotionAccumulator::warnIfNotInUse(const RawEvent& event, const Slot& slot) { - if (!slot.mInUse) { - ALOGW("Received unexpected event (0x%0x, 0x%0x) for slot %i with tracking id %i", - event.code, event.value, mCurrentSlot, slot.mAbsMTTrackingId); - } -} - -// --- MultiTouchMotionAccumulator::Slot --- - -int32_t MultiTouchMotionAccumulator::Slot::getToolType() const { - if (mHaveAbsMTToolType) { - switch (mAbsMTToolType) { - case MT_TOOL_FINGER: - return AMOTION_EVENT_TOOL_TYPE_FINGER; - case MT_TOOL_PEN: - return AMOTION_EVENT_TOOL_TYPE_STYLUS; - case MT_TOOL_PALM: - return AMOTION_EVENT_TOOL_TYPE_PALM; - } - } - return AMOTION_EVENT_TOOL_TYPE_UNKNOWN; -} - // --- MultiTouchInputMapper --- MultiTouchInputMapper::MultiTouchInputMapper(InputDeviceContext& deviceContext) diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h index 047e62de62..ddf9e80a6c 100644 --- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h +++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h @@ -17,77 +17,10 @@ #pragma once #include "TouchInputMapper.h" +#include "accumulator/MultiTouchMotionAccumulator.h" namespace android { -/* Keeps track of the state of multi-touch protocol. */ -class MultiTouchMotionAccumulator { -public: - class Slot { - public: - inline bool isInUse() const { return mInUse; } - inline int32_t getX() const { return mAbsMTPositionX; } - inline int32_t getY() const { return mAbsMTPositionY; } - inline int32_t getTouchMajor() const { return mAbsMTTouchMajor; } - inline int32_t getTouchMinor() const { - return mHaveAbsMTTouchMinor ? mAbsMTTouchMinor : mAbsMTTouchMajor; - } - inline int32_t getToolMajor() const { return mAbsMTWidthMajor; } - inline int32_t getToolMinor() const { - return mHaveAbsMTWidthMinor ? mAbsMTWidthMinor : mAbsMTWidthMajor; - } - inline int32_t getOrientation() const { return mAbsMTOrientation; } - inline int32_t getTrackingId() const { return mAbsMTTrackingId; } - inline int32_t getPressure() const { return mAbsMTPressure; } - inline int32_t getDistance() const { return mAbsMTDistance; } - inline int32_t getToolType() const; - - private: - friend class MultiTouchMotionAccumulator; - - bool mInUse = false; - bool mHaveAbsMTTouchMinor = false; - bool mHaveAbsMTWidthMinor = false; - bool mHaveAbsMTToolType = false; - - int32_t mAbsMTPositionX = 0; - int32_t mAbsMTPositionY = 0; - int32_t mAbsMTTouchMajor = 0; - int32_t mAbsMTTouchMinor = 0; - int32_t mAbsMTWidthMajor = 0; - int32_t mAbsMTWidthMinor = 0; - int32_t mAbsMTOrientation = 0; - int32_t mAbsMTTrackingId = -1; - int32_t mAbsMTPressure = 0; - int32_t mAbsMTDistance = 0; - int32_t mAbsMTToolType = 0; - - void clear() { *this = Slot(); } - }; - - MultiTouchMotionAccumulator(); - - void configure(InputDeviceContext& deviceContext, size_t slotCount, bool usingSlotsProtocol); - void process(const RawEvent* rawEvent); - void finishSync(); - bool hasStylus() const; - - inline size_t getSlotCount() const { return mSlots.size(); } - inline const Slot& getSlot(size_t index) const { - LOG_ALWAYS_FATAL_IF(index < 0 || index >= mSlots.size(), "Invalid index: %zu", index); - return mSlots[index]; - } - -private: - int32_t mCurrentSlot; - std::vector<Slot> mSlots; - bool mUsingSlotsProtocol; - bool mHaveStylus; - - void resetSlots(); - void warnIfNotInUse(const RawEvent& event, const Slot& slot); -}; - class MultiTouchInputMapper : public TouchInputMapper { public: explicit MultiTouchInputMapper(InputDeviceContext& deviceContext); diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 615889ebe3..3947cf7b9f 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -73,53 +73,8 @@ inline static int32_t signExtendNybble(int32_t value) { return value >= 8 ? value - 16 : value; } -// --- RawPointerAxes --- - -RawPointerAxes::RawPointerAxes() { - clear(); -} - -void RawPointerAxes::clear() { - x.clear(); - y.clear(); - pressure.clear(); - touchMajor.clear(); - touchMinor.clear(); - toolMajor.clear(); - toolMinor.clear(); - orientation.clear(); - distance.clear(); - tiltX.clear(); - tiltY.clear(); - trackingId.clear(); - slot.clear(); -} - // --- RawPointerData --- -RawPointerData::RawPointerData() { - clear(); -} - -void RawPointerData::clear() { - pointerCount = 0; - clearIdBits(); -} - -void RawPointerData::copyFrom(const RawPointerData& other) { - pointerCount = other.pointerCount; - hoveringIdBits = other.hoveringIdBits; - touchingIdBits = other.touchingIdBits; - canceledIdBits = other.canceledIdBits; - - for (uint32_t i = 0; i < pointerCount; i++) { - pointers[i] = other.pointers[i]; - - int id = pointers[i].id; - idToIndex[id] = other.idToIndex[id]; - } -} - void RawPointerData::getCentroidOfTouchingPointers(float* outX, float* outY) const { float x = 0, y = 0; uint32_t count = touchingIdBits.count(); @@ -137,39 +92,11 @@ void RawPointerData::getCentroidOfTouchingPointers(float* outX, float* outY) con *outY = y; } -// --- CookedPointerData --- - -CookedPointerData::CookedPointerData() { - clear(); -} - -void CookedPointerData::clear() { - pointerCount = 0; - hoveringIdBits.clear(); - touchingIdBits.clear(); - canceledIdBits.clear(); - validIdBits.clear(); -} - -void CookedPointerData::copyFrom(const CookedPointerData& other) { - pointerCount = other.pointerCount; - hoveringIdBits = other.hoveringIdBits; - touchingIdBits = other.touchingIdBits; - validIdBits = other.validIdBits; - - for (uint32_t i = 0; i < pointerCount; i++) { - pointerProperties[i].copyFrom(other.pointerProperties[i]); - pointerCoords[i].copyFrom(other.pointerCoords[i]); - - int id = pointerProperties[i].id; - idToIndex[id] = other.idToIndex[id]; - } -} - // --- TouchInputMapper --- TouchInputMapper::TouchInputMapper(InputDeviceContext& deviceContext) : InputMapper(deviceContext), + mTouchButtonAccumulator(deviceContext), mSource(0), mDeviceMode(DeviceMode::DISABLED), mDisplayWidth(-1), @@ -360,7 +287,7 @@ std::list<NotifyArgs> TouchInputMapper::configure(nsecs_t when, // Configure common accumulators. mCursorScrollAccumulator.configure(getDeviceContext()); - mTouchButtonAccumulator.configure(getDeviceContext()); + mTouchButtonAccumulator.configure(); // Configure absolute axis information. configureRawPointerAxes(); @@ -510,6 +437,10 @@ void TouchInputMapper::configureParameters() { mParameters.supportsUsi = false; getDeviceContext().getConfiguration().tryGetProperty("touch.supportsUsi", mParameters.supportsUsi); + + mParameters.enableForInactiveViewport = false; + getDeviceContext().getConfiguration().tryGetProperty("touch.enableForInactiveViewport", + mParameters.enableForInactiveViewport); } void TouchInputMapper::dumpParameters(std::string& dump) { @@ -527,6 +458,8 @@ void TouchInputMapper::dumpParameters(std::string& dump) { dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware)); dump += INDENT4 "Orientation: " + ftl::enum_string(mParameters.orientation) + "\n"; dump += StringPrintf(INDENT4 "SupportsUsi: %s\n", toString(mParameters.supportsUsi)); + dump += StringPrintf(INDENT4 "EnableForInactiveViewport: %s\n", + toString(mParameters.enableForInactiveViewport)); } void TouchInputMapper::configureRawPointerAxes() { @@ -897,6 +830,8 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) mDeviceMode = DeviceMode::POINTER; if (hasStylus()) { mSource |= AINPUT_SOURCE_STYLUS; + } else { + mSource |= AINPUT_SOURCE_TOUCHPAD; } } else if (isTouchScreen()) { mSource = AINPUT_SOURCE_TOUCHSCREEN; @@ -929,7 +864,7 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) "becomes available.", getDeviceName().c_str()); mDeviceMode = DeviceMode::DISABLED; - } else if (!newViewportOpt->isActive) { + } else if (!mParameters.enableForInactiveViewport && !newViewportOpt->isActive) { ALOGI("Disabling %s (device %i) because the associated viewport is not active", getDeviceName().c_str(), getDeviceId()); mDeviceMode = DeviceMode::DISABLED; @@ -1449,7 +1384,7 @@ std::list<NotifyArgs> TouchInputMapper::reset(nsecs_t when) { mCursorButtonAccumulator.reset(getDeviceContext()); mCursorScrollAccumulator.reset(getDeviceContext()); - mTouchButtonAccumulator.reset(getDeviceContext()); + mTouchButtonAccumulator.reset(); mPointerVelocityControl.reset(); mWheelXVelocityControl.reset(); @@ -1582,7 +1517,7 @@ std::list<NotifyArgs> TouchInputMapper::processRawTouches(bool timeout) { // All ready to go. clearStylusDataPendingFlags(); - mCurrentRawState.copyFrom(next); + mCurrentRawState = next; if (mCurrentRawState.when < mLastRawState.when) { mCurrentRawState.when = mLastRawState.when; mCurrentRawState.readTime = mLastRawState.readTime; @@ -1597,7 +1532,7 @@ std::list<NotifyArgs> TouchInputMapper::processRawTouches(bool timeout) { if (timeout) { nsecs_t when = mExternalStylusFusionTimeout - STYLUS_DATA_LATENCY; clearStylusDataPendingFlags(); - mCurrentRawState.copyFrom(mLastRawState); + mCurrentRawState = mLastRawState; ALOGD_IF(DEBUG_STYLUS_FUSION, "Timeout expired, synthesizing event with new stylus data"); const nsecs_t readTime = when; // consider this synthetic event to be zero latency @@ -1722,8 +1657,8 @@ std::list<NotifyArgs> TouchInputMapper::cookAndDispatch(nsecs_t when, nsecs_t re mCurrentRawState.rawHScroll = 0; // Copy current touch to last touch in preparation for the next cycle. - mLastRawState.copyFrom(mCurrentRawState); - mLastCookedState.copyFrom(mCurrentCookedState); + mLastRawState = mCurrentRawState; + mLastCookedState = mCurrentCookedState; return out; } @@ -1743,8 +1678,8 @@ void TouchInputMapper::updateTouchSpots() { mPointerController->fade(PointerControllerInterface::Transition::GRADUAL); mPointerController->setButtonState(mCurrentRawState.buttonState); - mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, + mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords.cbegin(), + mCurrentCookedState.cookedPointerData.idToIndex.cbegin(), mCurrentCookedState.cookedPointerData.touchingIdBits, mViewport.displayId); } @@ -1992,6 +1927,36 @@ std::list<NotifyArgs> TouchInputMapper::abortTouches(nsecs_t when, nsecs_t readT return out; } +// Updates pointer coords and properties for pointers with specified ids that have moved. +// Returns true if any of them changed. +static bool updateMovedPointers(const PropertiesArray& inProperties, CoordsArray& inCoords, + const IdToIndexArray& inIdToIndex, PropertiesArray& outProperties, + CoordsArray& outCoords, IdToIndexArray& outIdToIndex, + BitSet32 idBits) { + bool changed = false; + while (!idBits.isEmpty()) { + uint32_t id = idBits.clearFirstMarkedBit(); + uint32_t inIndex = inIdToIndex[id]; + uint32_t outIndex = outIdToIndex[id]; + + const PointerProperties& curInProperties = inProperties[inIndex]; + const PointerCoords& curInCoords = inCoords[inIndex]; + PointerProperties& curOutProperties = outProperties[outIndex]; + PointerCoords& curOutCoords = outCoords[outIndex]; + + if (curInProperties != curOutProperties) { + curOutProperties.copyFrom(curInProperties); + changed = true; + } + + if (curInCoords != curOutCoords) { + curOutCoords.copyFrom(curInCoords); + changed = true; + } + } + return changed; +} + std::list<NotifyArgs> TouchInputMapper::dispatchTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) { std::list<NotifyArgs> out; @@ -2159,9 +2124,9 @@ std::list<NotifyArgs> TouchInputMapper::dispatchButtonRelease(nsecs_t when, nsec out.push_back(dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0, metaState, buttonState, 0, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1, + mLastCookedState.cookedPointerData.pointerProperties, + mLastCookedState.cookedPointerData.pointerCoords, + mLastCookedState.cookedPointerData.idToIndex, idBits, -1, mOrientedXPrecision, mOrientedYPrecision, mDownTime, MotionClassification::NONE)); } @@ -2538,8 +2503,8 @@ std::list<NotifyArgs> TouchInputMapper::dispatchPointerGestures(nsecs_t when, ns } if (mPointerGesture.currentGestureMode == PointerGesture::Mode::FREEFORM) { - mPointerController->setSpots(mPointerGesture.currentGestureCoords, - mPointerGesture.currentGestureIdToIndex, + mPointerController->setSpots(mPointerGesture.currentGestureCoords.cbegin(), + mPointerGesture.currentGestureIdToIndex.cbegin(), mPointerGesture.currentGestureIdBits, mPointerController->getDisplayId()); } @@ -3599,6 +3564,8 @@ std::list<NotifyArgs> TouchInputMapper::abortPointerMouse(nsecs_t when, nsecs_t std::list<NotifyArgs> TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uint32_t policyFlags, bool down, bool hovering) { + LOG_ALWAYS_FATAL_IF(mDeviceMode != DeviceMode::POINTER, + "%s cannot be used when the device is not in POINTER mode.", __func__); std::list<NotifyArgs> out; int32_t metaState = getContext()->getGlobalMetaState(); @@ -3725,6 +3692,10 @@ std::list<NotifyArgs> TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsec if (down || hovering) { mPointerSimple.lastCoords.copyFrom(mPointerSimple.currentCoords); mPointerSimple.lastProperties.copyFrom(mPointerSimple.currentProperties); + mPointerSimple.displayId = displayId; + mPointerSimple.source = mSource; + mPointerSimple.lastCursorX = xCursorPosition; + mPointerSimple.lastCursorY = yCursorPosition; } else { mPointerSimple.reset(); } @@ -3733,17 +3704,32 @@ std::list<NotifyArgs> TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsec std::list<NotifyArgs> TouchInputMapper::abortPointerSimple(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) { - mPointerSimple.currentCoords.clear(); - mPointerSimple.currentProperties.clear(); - - return dispatchPointerSimple(when, readTime, policyFlags, false, false); + std::list<NotifyArgs> out; + if (mPointerSimple.down || mPointerSimple.hovering) { + int32_t metaState = getContext()->getGlobalMetaState(); + out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(), + mPointerSimple.source, mPointerSimple.displayId, policyFlags, + AMOTION_EVENT_ACTION_CANCEL, 0, AMOTION_EVENT_FLAG_CANCELED, + metaState, mLastRawState.buttonState, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &mPointerSimple.lastProperties, &mPointerSimple.lastCoords, + mOrientedXPrecision, mOrientedYPrecision, + mPointerSimple.lastCursorX, mPointerSimple.lastCursorY, + mPointerSimple.downTime, + /* videoFrames */ {})); + if (mPointerController != nullptr) { + mPointerController->fade(PointerControllerInterface::Transition::GRADUAL); + } + } + mPointerSimple.reset(); + return out; } NotifyMotionArgs TouchInputMapper::dispatchMotion( nsecs_t when, nsecs_t readTime, uint32_t policyFlags, uint32_t source, int32_t action, int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState, - int32_t edgeFlags, const PointerProperties* properties, const PointerCoords* coords, - const uint32_t* idToIndex, BitSet32 idBits, int32_t changedId, float xPrecision, + int32_t edgeFlags, const PropertiesArray& properties, const CoordsArray& coords, + const IdToIndexArray& idToIndex, BitSet32 idBits, int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime, MotionClassification classification) { PointerCoords pointerCoords[MAX_POINTERS]; PointerProperties pointerProperties[MAX_POINTERS]; @@ -3797,36 +3783,6 @@ NotifyMotionArgs TouchInputMapper::dispatchMotion( downTime, std::move(frames)); } -bool TouchInputMapper::updateMovedPointers(const PointerProperties* inProperties, - const PointerCoords* inCoords, - const uint32_t* inIdToIndex, - PointerProperties* outProperties, - PointerCoords* outCoords, const uint32_t* outIdToIndex, - BitSet32 idBits) const { - bool changed = false; - while (!idBits.isEmpty()) { - uint32_t id = idBits.clearFirstMarkedBit(); - uint32_t inIndex = inIdToIndex[id]; - uint32_t outIndex = outIdToIndex[id]; - - const PointerProperties& curInProperties = inProperties[inIndex]; - const PointerCoords& curInCoords = inCoords[inIndex]; - PointerProperties& curOutProperties = outProperties[outIndex]; - PointerCoords& curOutCoords = outCoords[outIndex]; - - if (curInProperties != curOutProperties) { - curOutProperties.copyFrom(curInProperties); - changed = true; - } - - if (curInCoords != curOutCoords) { - curOutCoords.copyFrom(curInCoords); - changed = true; - } - } - return changed; -} - std::list<NotifyArgs> TouchInputMapper::cancelTouch(nsecs_t when, nsecs_t readTime) { std::list<NotifyArgs> out; out += abortPointerUsage(when, readTime, 0 /*policyFlags*/); diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h index 7680090188..d5e4d5ae28 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchInputMapper.h @@ -29,53 +29,56 @@ namespace android { /* Raw axis information from the driver. */ struct RawPointerAxes { - RawAbsoluteAxisInfo x; - RawAbsoluteAxisInfo y; - RawAbsoluteAxisInfo pressure; - RawAbsoluteAxisInfo touchMajor; - RawAbsoluteAxisInfo touchMinor; - RawAbsoluteAxisInfo toolMajor; - RawAbsoluteAxisInfo toolMinor; - RawAbsoluteAxisInfo orientation; - RawAbsoluteAxisInfo distance; - RawAbsoluteAxisInfo tiltX; - RawAbsoluteAxisInfo tiltY; - RawAbsoluteAxisInfo trackingId; - RawAbsoluteAxisInfo slot; - - RawPointerAxes(); + RawAbsoluteAxisInfo x{}; + RawAbsoluteAxisInfo y{}; + RawAbsoluteAxisInfo pressure{}; + RawAbsoluteAxisInfo touchMajor{}; + RawAbsoluteAxisInfo touchMinor{}; + RawAbsoluteAxisInfo toolMajor{}; + RawAbsoluteAxisInfo toolMinor{}; + RawAbsoluteAxisInfo orientation{}; + RawAbsoluteAxisInfo distance{}; + RawAbsoluteAxisInfo tiltX{}; + RawAbsoluteAxisInfo tiltY{}; + RawAbsoluteAxisInfo trackingId{}; + RawAbsoluteAxisInfo slot{}; + inline int32_t getRawWidth() const { return x.maxValue - x.minValue + 1; } inline int32_t getRawHeight() const { return y.maxValue - y.minValue + 1; } - void clear(); + inline void clear() { *this = RawPointerAxes(); } }; +using PropertiesArray = std::array<PointerProperties, MAX_POINTERS>; +using CoordsArray = std::array<PointerCoords, MAX_POINTERS>; +using IdToIndexArray = std::array<uint32_t, MAX_POINTER_ID + 1>; + /* Raw data for a collection of pointers including a pointer id mapping table. */ struct RawPointerData { struct Pointer { - uint32_t id; - int32_t x; - int32_t y; - int32_t pressure; - int32_t touchMajor; - int32_t touchMinor; - int32_t toolMajor; - int32_t toolMinor; - int32_t orientation; - int32_t distance; - int32_t tiltX; - int32_t tiltY; - int32_t toolType; // a fully decoded AMOTION_EVENT_TOOL_TYPE constant - bool isHovering; + uint32_t id{0xFFFFFFFF}; + int32_t x{}; + int32_t y{}; + int32_t pressure{}; + int32_t touchMajor{}; + int32_t touchMinor{}; + int32_t toolMajor{}; + int32_t toolMinor{}; + int32_t orientation{}; + int32_t distance{}; + int32_t tiltX{}; + int32_t tiltY{}; + // A fully decoded AMOTION_EVENT_TOOL_TYPE constant. + int32_t toolType{AMOTION_EVENT_TOOL_TYPE_UNKNOWN}; + bool isHovering{false}; }; - uint32_t pointerCount; - Pointer pointers[MAX_POINTERS]; - BitSet32 hoveringIdBits, touchingIdBits, canceledIdBits; - uint32_t idToIndex[MAX_POINTER_ID + 1]; + uint32_t pointerCount{}; + std::array<Pointer, MAX_POINTERS> pointers{}; + BitSet32 hoveringIdBits{}, touchingIdBits{}, canceledIdBits{}; + IdToIndexArray idToIndex{}; + + inline void clear() { *this = RawPointerData(); } - RawPointerData(); - void clear(); - void copyFrom(const RawPointerData& other); void getCentroidOfTouchingPointers(float* outX, float* outY) const; inline void markIdBit(uint32_t id, bool isHovering) { @@ -99,15 +102,13 @@ struct RawPointerData { /* Cooked data for a collection of pointers including a pointer id mapping table. */ struct CookedPointerData { - uint32_t pointerCount; - PointerProperties pointerProperties[MAX_POINTERS]; - PointerCoords pointerCoords[MAX_POINTERS]; - BitSet32 hoveringIdBits, touchingIdBits, canceledIdBits, validIdBits; - uint32_t idToIndex[MAX_POINTER_ID + 1]; + uint32_t pointerCount{}; + PropertiesArray pointerProperties{}; + CoordsArray pointerCoords{}; + BitSet32 hoveringIdBits{}, touchingIdBits{}, canceledIdBits{}, validIdBits{}; + IdToIndexArray idToIndex{}; - CookedPointerData(); - void clear(); - void copyFrom(const CookedPointerData& other); + inline void clear() { *this = CookedPointerData(); } inline const PointerCoords& pointerCoordsForId(uint32_t id) const { return pointerCoords[idToIndex[id]]; @@ -235,6 +236,9 @@ protected: // Whether the device supports the Universal Stylus Initiative (USI) protocol for styluses. bool supportsUsi; + + // Allows touches while the display is off. + bool enableForInactiveViewport; } mParameters; // Immutable calibration parameters in parsed form. @@ -314,65 +318,33 @@ protected: RawPointerAxes mRawPointerAxes; struct RawState { - nsecs_t when; - nsecs_t readTime; + nsecs_t when{}; + nsecs_t readTime{}; // Raw pointer sample data. - RawPointerData rawPointerData; + RawPointerData rawPointerData{}; - int32_t buttonState; + int32_t buttonState{}; // Scroll state. - int32_t rawVScroll; - int32_t rawHScroll; - - explicit inline RawState() { clear(); } - - void copyFrom(const RawState& other) { - when = other.when; - readTime = other.readTime; - rawPointerData.copyFrom(other.rawPointerData); - buttonState = other.buttonState; - rawVScroll = other.rawVScroll; - rawHScroll = other.rawHScroll; - } + int32_t rawVScroll{}; + int32_t rawHScroll{}; - void clear() { - when = 0; - readTime = 0; - rawPointerData.clear(); - buttonState = 0; - rawVScroll = 0; - rawHScroll = 0; - } + inline void clear() { *this = RawState(); } }; struct CookedState { // Cooked pointer sample data. - CookedPointerData cookedPointerData; + CookedPointerData cookedPointerData{}; // Id bits used to differentiate fingers, stylus and mouse tools. - BitSet32 fingerIdBits; - BitSet32 stylusIdBits; - BitSet32 mouseIdBits; - - int32_t buttonState; - - void copyFrom(const CookedState& other) { - cookedPointerData.copyFrom(other.cookedPointerData); - fingerIdBits = other.fingerIdBits; - stylusIdBits = other.stylusIdBits; - mouseIdBits = other.mouseIdBits; - buttonState = other.buttonState; - } + BitSet32 fingerIdBits{}; + BitSet32 stylusIdBits{}; + BitSet32 mouseIdBits{}; - void clear() { - cookedPointerData.clear(); - fingerIdBits.clear(); - stylusIdBits.clear(); - mouseIdBits.clear(); - buttonState = 0; - } + int32_t buttonState{}; + + inline void clear() { *this = CookedState(); } }; std::vector<RawState> mRawStatesPending; @@ -528,9 +500,9 @@ private: float mPointerGestureMaxSwipeWidth; struct PointerDistanceHeapElement { - uint32_t currentPointerIndex : 8; - uint32_t lastPointerIndex : 8; - uint64_t distance : 48; // squared distance + uint32_t currentPointerIndex : 8 {}; + uint32_t lastPointerIndex : 8 {}; + uint64_t distance : 48 {}; // squared distance }; enum class PointerUsage { @@ -627,15 +599,15 @@ private: // Pointer coords and ids for the current and previous pointer gesture. Mode currentGestureMode; BitSet32 currentGestureIdBits; - uint32_t currentGestureIdToIndex[MAX_POINTER_ID + 1]; - PointerProperties currentGestureProperties[MAX_POINTERS]; - PointerCoords currentGestureCoords[MAX_POINTERS]; + IdToIndexArray currentGestureIdToIndex{}; + PropertiesArray currentGestureProperties{}; + CoordsArray currentGestureCoords{}; Mode lastGestureMode; BitSet32 lastGestureIdBits; - uint32_t lastGestureIdToIndex[MAX_POINTER_ID + 1]; - PointerProperties lastGestureProperties[MAX_POINTERS]; - PointerCoords lastGestureCoords[MAX_POINTERS]; + IdToIndexArray lastGestureIdToIndex{}; + PropertiesArray lastGestureProperties{}; + CoordsArray lastGestureCoords{}; // Time the pointer gesture last went down. nsecs_t downTime; @@ -709,6 +681,12 @@ private: // Time the pointer last went down. nsecs_t downTime; + // Values reported for the last pointer event. + uint32_t source; + int32_t displayId; + float lastCursorX; + float lastCursorY; + void reset() { currentCoords.clear(); currentProperties.clear(); @@ -717,6 +695,10 @@ private: down = false; hovering = false; downTime = 0; + source = 0; + displayId = ADISPLAY_ID_NONE; + lastCursorX = 0.f; + lastCursorY = 0.f; } } mPointerSimple; @@ -812,17 +794,10 @@ private: [[nodiscard]] NotifyMotionArgs dispatchMotion( nsecs_t when, nsecs_t readTime, uint32_t policyFlags, uint32_t source, int32_t action, int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState, - int32_t edgeFlags, const PointerProperties* properties, const PointerCoords* coords, - const uint32_t* idToIndex, BitSet32 idBits, int32_t changedId, float xPrecision, + int32_t edgeFlags, const PropertiesArray& properties, const CoordsArray& coords, + const IdToIndexArray& idToIndex, BitSet32 idBits, int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime, MotionClassification classification); - // Updates pointer coords and properties for pointers with specified ids that have moved. - // Returns true if any of them changed. - bool updateMovedPointers(const PointerProperties* inProperties, const PointerCoords* inCoords, - const uint32_t* inIdToIndex, PointerProperties* outProperties, - PointerCoords* outCoords, const uint32_t* outIdToIndex, - BitSet32 idBits) const; - // Returns if this touch device is a touch screen with an associated display. bool isTouchScreen(); // Updates touch spots if they are enabled. Should only be used when this device is a @@ -834,7 +809,6 @@ private: static void assignPointerIds(const RawState& last, RawState& current); - const char* modeToString(DeviceMode deviceMode); void rotateAndScale(float& x, float& y) const; }; diff --git a/services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.cpp new file mode 100644 index 0000000000..2da1d814fa --- /dev/null +++ b/services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 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 "HidUsageAccumulator.h" + +namespace android { + +void HidUsageAccumulator::process(const RawEvent& rawEvent) { + if (rawEvent.type == EV_MSC && rawEvent.code == MSC_SCAN) { + mCurrentHidUsage = rawEvent.value; + return; + } + + if (rawEvent.type == EV_SYN && rawEvent.code == SYN_REPORT) { + reset(); + return; + } +} + +int32_t HidUsageAccumulator::consumeCurrentHidUsage() { + const int32_t currentHidUsage = mCurrentHidUsage; + reset(); + return currentHidUsage; +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.h b/services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.h new file mode 100644 index 0000000000..740a710483 --- /dev/null +++ b/services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 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 "EventHub.h" + +#include <cstdint> + +namespace android { + +/* Keeps track of the state of currently reported HID usage code. */ +class HidUsageAccumulator { +public: + explicit HidUsageAccumulator() = default; + inline void reset() { *this = HidUsageAccumulator(); } + + void process(const RawEvent& rawEvent); + + /* This must be called when processing the `EV_KEY` event. Returns 0 if invalid. */ + int32_t consumeCurrentHidUsage(); + +private: + int32_t mCurrentHidUsage{}; +}; + +} // namespace android diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp new file mode 100644 index 0000000000..b0cef676fb --- /dev/null +++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp @@ -0,0 +1,176 @@ +/* + * Copyright (C) 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. + */ + +// clang-format off +#include "../Macros.h" +// clang-format on +#include "MultiTouchMotionAccumulator.h" + +namespace android { + +// --- MultiTouchMotionAccumulator --- + +MultiTouchMotionAccumulator::MultiTouchMotionAccumulator() + : mCurrentSlot(-1), mUsingSlotsProtocol(false), mHaveStylus(false) {} + +void MultiTouchMotionAccumulator::configure(InputDeviceContext& deviceContext, size_t slotCount, + bool usingSlotsProtocol) { + mUsingSlotsProtocol = usingSlotsProtocol; + mHaveStylus = deviceContext.hasAbsoluteAxis(ABS_MT_TOOL_TYPE); + mSlots = std::vector<Slot>(slotCount); + + mCurrentSlot = -1; + if (mUsingSlotsProtocol) { + // Query the driver for the current slot index and use it as the initial slot before we + // start reading events from the device. It is possible that the current slot index will + // not be the same as it was when the first event was written into the evdev buffer, which + // means the input mapper could start out of sync with the initial state of the events in + // the evdev buffer. In the extremely unlikely case that this happens, the data from two + // slots will be confused until the next ABS_MT_SLOT event is received. This can cause the + // touch point to "jump", but at least there will be no stuck touches. + int32_t initialSlot; + if (const auto status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot); + status == OK) { + mCurrentSlot = initialSlot; + } else { + ALOGD("Could not retrieve current multi-touch slot index. status=%d", status); + } + } +} + +void MultiTouchMotionAccumulator::resetSlots() { + for (Slot& slot : mSlots) { + slot.clear(); + } + mCurrentSlot = -1; +} + +void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) { + if (rawEvent->type == EV_ABS) { + bool newSlot = false; + if (mUsingSlotsProtocol) { + if (rawEvent->code == ABS_MT_SLOT) { + mCurrentSlot = rawEvent->value; + newSlot = true; + } + } else if (mCurrentSlot < 0) { + mCurrentSlot = 0; + } + + if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlots.size()) { + if (newSlot) { + ALOGW_IF(DEBUG_POINTERS, + "MultiTouch device emitted invalid slot index %d but it " + "should be between 0 and %zd; ignoring this slot.", + mCurrentSlot, mSlots.size() - 1); + } + } else { + Slot& slot = mSlots[mCurrentSlot]; + // If mUsingSlotsProtocol is true, it means the raw pointer has axis info of + // ABS_MT_TRACKING_ID and ABS_MT_SLOT, so driver should send a valid trackingId while + // updating the slot. + if (!mUsingSlotsProtocol) { + slot.mInUse = true; + } + + switch (rawEvent->code) { + case ABS_MT_POSITION_X: + slot.mAbsMtPositionX = rawEvent->value; + warnIfNotInUse(*rawEvent, slot); + break; + case ABS_MT_POSITION_Y: + slot.mAbsMtPositionY = rawEvent->value; + warnIfNotInUse(*rawEvent, slot); + break; + case ABS_MT_TOUCH_MAJOR: + slot.mAbsMtTouchMajor = rawEvent->value; + break; + case ABS_MT_TOUCH_MINOR: + slot.mAbsMtTouchMinor = rawEvent->value; + slot.mHaveAbsMtTouchMinor = true; + break; + case ABS_MT_WIDTH_MAJOR: + slot.mAbsMtWidthMajor = rawEvent->value; + break; + case ABS_MT_WIDTH_MINOR: + slot.mAbsMtWidthMinor = rawEvent->value; + slot.mHaveAbsMtWidthMinor = true; + break; + case ABS_MT_ORIENTATION: + slot.mAbsMtOrientation = rawEvent->value; + break; + case ABS_MT_TRACKING_ID: + if (mUsingSlotsProtocol && rawEvent->value < 0) { + // The slot is no longer in use but it retains its previous contents, + // which may be reused for subsequent touches. + slot.mInUse = false; + } else { + slot.mInUse = true; + slot.mAbsMtTrackingId = rawEvent->value; + } + break; + case ABS_MT_PRESSURE: + slot.mAbsMtPressure = rawEvent->value; + break; + case ABS_MT_DISTANCE: + slot.mAbsMtDistance = rawEvent->value; + break; + case ABS_MT_TOOL_TYPE: + slot.mAbsMtToolType = rawEvent->value; + slot.mHaveAbsMtToolType = true; + break; + } + } + } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) { + // MultiTouch Sync: The driver has returned all data for *one* of the pointers. + mCurrentSlot += 1; + } +} + +void MultiTouchMotionAccumulator::finishSync() { + if (!mUsingSlotsProtocol) { + resetSlots(); + } +} + +bool MultiTouchMotionAccumulator::hasStylus() const { + return mHaveStylus; +} + +void MultiTouchMotionAccumulator::warnIfNotInUse(const RawEvent& event, const Slot& slot) { + if (!slot.mInUse) { + ALOGW("Received unexpected event (0x%0x, 0x%0x) for slot %i with tracking id %i", + event.code, event.value, mCurrentSlot, slot.mAbsMtTrackingId); + } +} + +// --- MultiTouchMotionAccumulator::Slot --- + +int32_t MultiTouchMotionAccumulator::Slot::getToolType() const { + if (mHaveAbsMtToolType) { + switch (mAbsMtToolType) { + case MT_TOOL_FINGER: + return AMOTION_EVENT_TOOL_TYPE_FINGER; + case MT_TOOL_PEN: + return AMOTION_EVENT_TOOL_TYPE_STYLUS; + case MT_TOOL_PALM: + return AMOTION_EVENT_TOOL_TYPE_PALM; + } + } + return AMOTION_EVENT_TOOL_TYPE_UNKNOWN; +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h new file mode 100644 index 0000000000..625a00fdd4 --- /dev/null +++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 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 <linux/input-event-codes.h> +#include <stdint.h> +#include <vector> + +#include "EventHub.h" +#include "InputDevice.h" + +namespace android { + +/* Keeps track of the state of multi-touch protocol. */ +class MultiTouchMotionAccumulator { +public: + class Slot { + public: + inline bool isInUse() const { return mInUse; } + inline int32_t getX() const { return mAbsMtPositionX; } + inline int32_t getY() const { return mAbsMtPositionY; } + inline int32_t getTouchMajor() const { return mAbsMtTouchMajor; } + inline int32_t getTouchMinor() const { + return mHaveAbsMtTouchMinor ? mAbsMtTouchMinor : mAbsMtTouchMajor; + } + inline int32_t getToolMajor() const { return mAbsMtWidthMajor; } + inline int32_t getToolMinor() const { + return mHaveAbsMtWidthMinor ? mAbsMtWidthMinor : mAbsMtWidthMajor; + } + inline int32_t getOrientation() const { return mAbsMtOrientation; } + inline int32_t getTrackingId() const { return mAbsMtTrackingId; } + inline int32_t getPressure() const { return mAbsMtPressure; } + inline int32_t getDistance() const { return mAbsMtDistance; } + int32_t getToolType() const; + + private: + friend class MultiTouchMotionAccumulator; + + bool mInUse = false; + bool mHaveAbsMtTouchMinor = false; + bool mHaveAbsMtWidthMinor = false; + bool mHaveAbsMtToolType = false; + + int32_t mAbsMtPositionX = 0; + int32_t mAbsMtPositionY = 0; + int32_t mAbsMtTouchMajor = 0; + int32_t mAbsMtTouchMinor = 0; + int32_t mAbsMtWidthMajor = 0; + int32_t mAbsMtWidthMinor = 0; + int32_t mAbsMtOrientation = 0; + int32_t mAbsMtTrackingId = -1; + int32_t mAbsMtPressure = 0; + int32_t mAbsMtDistance = 0; + int32_t mAbsMtToolType = 0; + + void clear() { *this = Slot(); } + }; + + MultiTouchMotionAccumulator(); + + void configure(InputDeviceContext& deviceContext, size_t slotCount, bool usingSlotsProtocol); + void process(const RawEvent* rawEvent); + void finishSync(); + bool hasStylus() const; + + inline size_t getSlotCount() const { return mSlots.size(); } + inline const Slot& getSlot(size_t index) const { + LOG_ALWAYS_FATAL_IF(index < 0 || index >= mSlots.size(), "Invalid index: %zu", index); + return mSlots[index]; + } + +private: + int32_t mCurrentSlot; + std::vector<Slot> mSlots; + bool mUsingSlotsProtocol; + bool mHaveStylus; + + void resetSlots(); + void warnIfNotInUse(const RawEvent& event, const Slot& slot); +}; + +} // namespace android diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp index 86153d3f5e..1891205ed6 100644 --- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp +++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp @@ -21,55 +21,39 @@ namespace android { -TouchButtonAccumulator::TouchButtonAccumulator() : mHaveBtnTouch(false), mHaveStylus(false) { - clearButtons(); +void TouchButtonAccumulator::configure() { + mHaveBtnTouch = mDeviceContext.hasScanCode(BTN_TOUCH); + mHaveStylus = mDeviceContext.hasScanCode(BTN_TOOL_PEN) || + mDeviceContext.hasScanCode(BTN_TOOL_RUBBER) || + mDeviceContext.hasScanCode(BTN_TOOL_BRUSH) || + mDeviceContext.hasScanCode(BTN_TOOL_PENCIL) || + mDeviceContext.hasScanCode(BTN_TOOL_AIRBRUSH); } -void TouchButtonAccumulator::configure(InputDeviceContext& deviceContext) { - mHaveBtnTouch = deviceContext.hasScanCode(BTN_TOUCH); - mHaveStylus = deviceContext.hasScanCode(BTN_TOOL_PEN) || - deviceContext.hasScanCode(BTN_TOOL_RUBBER) || - deviceContext.hasScanCode(BTN_TOOL_BRUSH) || - deviceContext.hasScanCode(BTN_TOOL_PENCIL) || - deviceContext.hasScanCode(BTN_TOOL_AIRBRUSH); -} - -void TouchButtonAccumulator::reset(InputDeviceContext& deviceContext) { - mBtnTouch = deviceContext.isKeyPressed(BTN_TOUCH); - mBtnStylus = deviceContext.isKeyPressed(BTN_STYLUS); +void TouchButtonAccumulator::reset() { + mBtnTouch = mDeviceContext.isKeyPressed(BTN_TOUCH); + mBtnStylus = mDeviceContext.isKeyPressed(BTN_STYLUS) || + mDeviceContext.isKeyCodePressed(AKEYCODE_STYLUS_BUTTON_PRIMARY); // BTN_0 is what gets mapped for the HID usage Digitizers.SecondaryBarrelSwitch - mBtnStylus2 = deviceContext.isKeyPressed(BTN_STYLUS2) || deviceContext.isKeyPressed(BTN_0); - mBtnToolFinger = deviceContext.isKeyPressed(BTN_TOOL_FINGER); - mBtnToolPen = deviceContext.isKeyPressed(BTN_TOOL_PEN); - mBtnToolRubber = deviceContext.isKeyPressed(BTN_TOOL_RUBBER); - mBtnToolBrush = deviceContext.isKeyPressed(BTN_TOOL_BRUSH); - mBtnToolPencil = deviceContext.isKeyPressed(BTN_TOOL_PENCIL); - mBtnToolAirbrush = deviceContext.isKeyPressed(BTN_TOOL_AIRBRUSH); - mBtnToolMouse = deviceContext.isKeyPressed(BTN_TOOL_MOUSE); - mBtnToolLens = deviceContext.isKeyPressed(BTN_TOOL_LENS); - mBtnToolDoubleTap = deviceContext.isKeyPressed(BTN_TOOL_DOUBLETAP); - mBtnToolTripleTap = deviceContext.isKeyPressed(BTN_TOOL_TRIPLETAP); - mBtnToolQuadTap = deviceContext.isKeyPressed(BTN_TOOL_QUADTAP); -} - -void TouchButtonAccumulator::clearButtons() { - mBtnTouch = 0; - mBtnStylus = 0; - mBtnStylus2 = 0; - mBtnToolFinger = 0; - mBtnToolPen = 0; - mBtnToolRubber = 0; - mBtnToolBrush = 0; - mBtnToolPencil = 0; - mBtnToolAirbrush = 0; - mBtnToolMouse = 0; - mBtnToolLens = 0; - mBtnToolDoubleTap = 0; - mBtnToolTripleTap = 0; - mBtnToolQuadTap = 0; + mBtnStylus2 = mDeviceContext.isKeyPressed(BTN_STYLUS2) || mDeviceContext.isKeyPressed(BTN_0) || + mDeviceContext.isKeyCodePressed(AKEYCODE_STYLUS_BUTTON_SECONDARY); + mBtnToolFinger = mDeviceContext.isKeyPressed(BTN_TOOL_FINGER); + mBtnToolPen = mDeviceContext.isKeyPressed(BTN_TOOL_PEN); + mBtnToolRubber = mDeviceContext.isKeyPressed(BTN_TOOL_RUBBER); + mBtnToolBrush = mDeviceContext.isKeyPressed(BTN_TOOL_BRUSH); + mBtnToolPencil = mDeviceContext.isKeyPressed(BTN_TOOL_PENCIL); + mBtnToolAirbrush = mDeviceContext.isKeyPressed(BTN_TOOL_AIRBRUSH); + mBtnToolMouse = mDeviceContext.isKeyPressed(BTN_TOOL_MOUSE); + mBtnToolLens = mDeviceContext.isKeyPressed(BTN_TOOL_LENS); + mBtnToolDoubleTap = mDeviceContext.isKeyPressed(BTN_TOOL_DOUBLETAP); + mBtnToolTripleTap = mDeviceContext.isKeyPressed(BTN_TOOL_TRIPLETAP); + mBtnToolQuadTap = mDeviceContext.isKeyPressed(BTN_TOOL_QUADTAP); + mHidUsageAccumulator.reset(); } void TouchButtonAccumulator::process(const RawEvent* rawEvent) { + mHidUsageAccumulator.process(*rawEvent); + if (rawEvent->type == EV_KEY) { switch (rawEvent->code) { case BTN_TOUCH: @@ -116,7 +100,29 @@ void TouchButtonAccumulator::process(const RawEvent* rawEvent) { case BTN_TOOL_QUADTAP: mBtnToolQuadTap = rawEvent->value; break; + default: + processMappedKey(rawEvent->code, rawEvent->value); } + return; + } +} + +void TouchButtonAccumulator::processMappedKey(int32_t scanCode, bool down) { + int32_t keyCode, metaState; + uint32_t flags; + if (mDeviceContext.mapKey(scanCode, mHidUsageAccumulator.consumeCurrentHidUsage(), + 0 /*metaState*/, &keyCode, &metaState, &flags) != OK) { + return; + } + switch (keyCode) { + case AKEYCODE_STYLUS_BUTTON_PRIMARY: + mBtnStylus = down; + break; + case AKEYCODE_STYLUS_BUTTON_SECONDARY: + mBtnStylus2 = down; + break; + default: + break; } } diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h index 7b889be238..65b0a62747 100644 --- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h +++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h @@ -16,7 +16,8 @@ #pragma once -#include <stdint.h> +#include <cstdint> +#include "HidUsageAccumulator.h" namespace android { @@ -26,9 +27,11 @@ struct RawEvent; /* Keeps track of the state of touch, stylus and tool buttons. */ class TouchButtonAccumulator { public: - TouchButtonAccumulator(); - void configure(InputDeviceContext& deviceContext); - void reset(InputDeviceContext& deviceContext); + explicit TouchButtonAccumulator(InputDeviceContext& deviceContext) + : mDeviceContext(deviceContext){}; + + void configure(); + void reset(); void process(const RawEvent* rawEvent); @@ -39,25 +42,29 @@ public: bool hasStylus() const; private: - bool mHaveBtnTouch; - bool mHaveStylus; - - bool mBtnTouch; - bool mBtnStylus; - bool mBtnStylus2; - bool mBtnToolFinger; - bool mBtnToolPen; - bool mBtnToolRubber; - bool mBtnToolBrush; - bool mBtnToolPencil; - bool mBtnToolAirbrush; - bool mBtnToolMouse; - bool mBtnToolLens; - bool mBtnToolDoubleTap; - bool mBtnToolTripleTap; - bool mBtnToolQuadTap; - - void clearButtons(); + bool mHaveBtnTouch{}; + bool mHaveStylus{}; + + bool mBtnTouch{}; + bool mBtnStylus{}; + bool mBtnStylus2{}; + bool mBtnToolFinger{}; + bool mBtnToolPen{}; + bool mBtnToolRubber{}; + bool mBtnToolBrush{}; + bool mBtnToolPencil{}; + bool mBtnToolAirbrush{}; + bool mBtnToolMouse{}; + bool mBtnToolLens{}; + bool mBtnToolDoubleTap{}; + bool mBtnToolTripleTap{}; + bool mBtnToolQuadTap{}; + + HidUsageAccumulator mHidUsageAccumulator{}; + + InputDeviceContext& mDeviceContext; + + void processMappedKey(int32_t scanCode, bool down); }; } // namespace android diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index f333306f26..e4e22df5de 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -39,6 +39,7 @@ #include <gtest/gtest.h> #include <gui/constants.h> +#include <thread> #include "android/hardware/input/InputDeviceCountryCode.h" #include "input/DisplayViewport.h" #include "input/Input.h" @@ -141,6 +142,16 @@ static void assertAxisNotPresent(MultiTouchInputMapper& mapper, int axis) { ASSERT_EQ(nullptr, motionRange); } +[[maybe_unused]] static void dumpReader(InputReader& reader) { + std::string dump; + reader.dump(dump); + std::istringstream iss(dump); + for (std::string line; std::getline(iss, line);) { + ALOGE("%s", line.c_str()); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } +} + // --- FakePointerController --- class FakePointerController : public PointerControllerInterface { @@ -758,7 +769,7 @@ private: status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, RawAbsoluteAxisInfo* outAxisInfo) const override { Device* device = getDevice(deviceId); - if (device && device->enabled) { + if (device) { ssize_t index = device->absoluteAxes.indexOfKey(axis); if (index >= 0) { *outAxisInfo = device->absoluteAxes.valueAt(index); @@ -2362,7 +2373,7 @@ TEST_F(InputReaderIntegrationTest, TestInvalidDevice) { // An invalid input device that is only used for this test. class InvalidUinputDevice : public UinputDevice { public: - InvalidUinputDevice() : UinputDevice("Invalid Device") {} + InvalidUinputDevice() : UinputDevice("Invalid Device", 99 /*productId*/) {} private: void configureDevice(int fd, uinput_user_dev* device) override {} @@ -2391,18 +2402,11 @@ TEST_F(InputReaderIntegrationTest, AddNewDevice) { ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); ASSERT_EQ(initialNumDevices + 1, mFakePolicy->getInputDevices().size()); - // Find the test device by its name. - const std::vector<InputDeviceInfo> inputDevices = mFakePolicy->getInputDevices(); - const auto& it = - std::find_if(inputDevices.begin(), inputDevices.end(), - [&keyboard](const InputDeviceInfo& info) { - return info.getIdentifier().name == keyboard->getName(); - }); - - ASSERT_NE(it, inputDevices.end()); - ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, it->getKeyboardType()); - ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, it->getSources()); - ASSERT_EQ(0U, it->getMotionRanges().size()); + const auto device = findDeviceByName(keyboard->getName()); + ASSERT_TRUE(device.has_value()); + ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, device->getKeyboardType()); + ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, device->getSources()); + ASSERT_EQ(0U, device->getMotionRanges().size()); keyboard.reset(); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); @@ -2437,6 +2441,41 @@ TEST_F(InputReaderIntegrationTest, SendsEventsToInputListener) { ASSERT_LE(keyArgs.eventTime, keyArgs.readTime); } +TEST_F(InputReaderIntegrationTest, ExternalStylusesButtons) { + std::unique_ptr<UinputExternalStylus> stylus = createUinputDevice<UinputExternalStylus>(); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); + + const auto device = findDeviceByName(stylus->getName()); + ASSERT_TRUE(device.has_value()); + + // An external stylus with buttons should also be recognized as a keyboard. + ASSERT_EQ(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_STYLUS, device->getSources()) + << "Unexpected source " << inputEventSourceToString(device->getSources()).c_str(); + ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, device->getKeyboardType()); + + const auto DOWN = + AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithSource(AINPUT_SOURCE_KEYBOARD)); + const auto UP = AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), WithSource(AINPUT_SOURCE_KEYBOARD)); + + stylus->pressAndReleaseKey(BTN_STYLUS); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled( + AllOf(DOWN, WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY)))); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled( + AllOf(UP, WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY)))); + + stylus->pressAndReleaseKey(BTN_STYLUS2); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled( + AllOf(DOWN, WithKeyCode(AKEYCODE_STYLUS_BUTTON_SECONDARY)))); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled( + AllOf(UP, WithKeyCode(AKEYCODE_STYLUS_BUTTON_SECONDARY)))); + + stylus->pressAndReleaseKey(BTN_STYLUS3); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled( + AllOf(DOWN, WithKeyCode(AKEYCODE_STYLUS_BUTTON_TERTIARY)))); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled( + AllOf(UP, WithKeyCode(AKEYCODE_STYLUS_BUTTON_TERTIARY)))); +} + /** * The Steam controller sends BTN_GEAR_DOWN and BTN_GEAR_UP for the two "paddle" buttons * on the back. In this test, we make sure that BTN_GEAR_DOWN / BTN_WHEEL and BTN_GEAR_UP @@ -2772,6 +2811,110 @@ TEST_F(TouchIntegrationTest, NotifiesPolicyWhenStylusGestureStarted) { ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertStylusGestureNotified(mDeviceInfo.getId())); } +TEST_F(TouchIntegrationTest, StylusButtonsGenerateKeyEvents) { + mDevice->sendKey(BTN_STYLUS, 1); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled( + AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithSource(AINPUT_SOURCE_KEYBOARD), + WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY)))); + + mDevice->sendKey(BTN_STYLUS, 0); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled( + AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), WithSource(AINPUT_SOURCE_KEYBOARD), + WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY)))); +} + +TEST_F(TouchIntegrationTest, StylusButtonsSurroundingTouchGesture) { + const Point centerPoint = mDevice->getCenterPoint(); + + // Press the stylus button. + mDevice->sendKey(BTN_STYLUS, 1); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled( + AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithSource(AINPUT_SOURCE_KEYBOARD), + WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY)))); + + // Start and finish a stylus gesture. + mDevice->sendSlot(FIRST_SLOT); + mDevice->sendTrackingId(FIRST_TRACKING_ID); + mDevice->sendToolType(MT_TOOL_PEN); + mDevice->sendDown(centerPoint); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + + mDevice->sendTrackingId(INVALID_TRACKING_ID); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0)))); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0)))); + + // Release the stylus button. + mDevice->sendKey(BTN_STYLUS, 0); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled( + AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), WithSource(AINPUT_SOURCE_KEYBOARD), + WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY)))); +} + +TEST_F(TouchIntegrationTest, StylusButtonsWithinTouchGesture) { + const Point centerPoint = mDevice->getCenterPoint(); + + // Start a stylus gesture. + mDevice->sendSlot(FIRST_SLOT); + mDevice->sendTrackingId(FIRST_TRACKING_ID); + mDevice->sendToolType(MT_TOOL_PEN); + mDevice->sendDown(centerPoint); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0)))); + + // Press and release a stylus button. Each change in button state also generates a MOVE event. + mDevice->sendKey(BTN_STYLUS, 1); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled( + AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithSource(AINPUT_SOURCE_KEYBOARD), + WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY)))); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + + mDevice->sendKey(BTN_STYLUS, 0); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled( + AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), WithSource(AINPUT_SOURCE_KEYBOARD), + WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY)))); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0)))); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0)))); + + // Finish the stylus gesture. + mDevice->sendTrackingId(INVALID_TRACKING_ID); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0)))); +} + // --- InputDeviceTest --- class InputDeviceTest : public testing::Test { protected: @@ -5613,7 +5756,7 @@ TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsNotSpecifiedAndNot prepareAxes(POSITION); SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); - ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources()); + ASSERT_EQ(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, mapper.getSources()); } TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsTouchScreen_ReturnsTouchScreen) { @@ -7112,6 +7255,44 @@ TEST_F(SingleTouchInputMapperTest, ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasNotCalled()); } +TEST_F(SingleTouchInputMapperTest, ButtonIsReleasedOnTouchUp) { + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareDisplay(DISPLAY_ORIENTATION_0); + prepareButtons(); + prepareAxes(POSITION); + SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled()); + + // Press a stylus button. + processKey(mapper, BTN_STYLUS, 1); + processSync(mapper); + + // Start a touch gesture and ensure the BUTTON_PRESS event is generated. + processDown(mapper, 100, 200); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithCoords(toDisplayX(100), toDisplayY(200)), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithCoords(toDisplayX(100), toDisplayY(200)), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + + // Release the touch gesture. Ensure that the BUTTON_RELEASE event is generated even though + // the button has not actually been released, since there will be no pointers through which the + // button state can be reported. The event is generated at the location of the pointer before + // it went up. + processUp(mapper); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithCoords(toDisplayX(100), toDisplayY(200)), WithButtonState(0)))); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(toDisplayX(100), toDisplayY(200)), WithButtonState(0)))); +} + // --- TouchDisplayProjectionTest --- class TouchDisplayProjectionTest : public SingleTouchInputMapperTest { @@ -7296,6 +7477,7 @@ protected: void processSlot(MultiTouchInputMapper& mapper, int32_t slot); void processToolType(MultiTouchInputMapper& mapper, int32_t toolType); void processKey(MultiTouchInputMapper& mapper, int32_t code, int32_t value); + void processHidUsage(MultiTouchInputMapper& mapper, int32_t usageCode, int32_t value); void processMTSync(MultiTouchInputMapper& mapper); void processSync(MultiTouchInputMapper& mapper); }; @@ -7400,6 +7582,12 @@ void MultiTouchInputMapperTest::processKey(MultiTouchInputMapper& mapper, int32_ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, code, value); } +void MultiTouchInputMapperTest::processHidUsage(MultiTouchInputMapper& mapper, int32_t usageCode, + int32_t value) { + process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_SCAN, usageCode); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UNKNOWN, value); +} + void MultiTouchInputMapperTest::processMTSync(MultiTouchInputMapper& mapper) { process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_MT_REPORT, 0); } @@ -8515,6 +8703,63 @@ TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleAllButtons) { ASSERT_EQ(0, motionArgs.buttonState); } +TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleMappedStylusButtons) { + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareDisplay(DISPLAY_ORIENTATION_0); + prepareAxes(POSITION | ID | SLOT); + MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); + + mFakeEventHub->addKey(EVENTHUB_ID, BTN_A, 0, AKEYCODE_STYLUS_BUTTON_PRIMARY, 0); + mFakeEventHub->addKey(EVENTHUB_ID, 0, 0xabcd, AKEYCODE_STYLUS_BUTTON_SECONDARY, 0); + + // Touch down. + processId(mapper, 1); + processPosition(mapper, 100, 200); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithButtonState(0)))); + + // Press and release button mapped to the primary stylus button. + processKey(mapper, BTN_A, 1); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)))); + + processKey(mapper, BTN_A, 0); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), WithButtonState(0)))); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithButtonState(0)))); + + // Press and release the HID usage mapped to the secondary stylus button. + processHidUsage(mapper, 0xabcd, 1); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_SECONDARY)))); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_SECONDARY)))); + + processHidUsage(mapper, 0xabcd, 0); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), WithButtonState(0)))); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithButtonState(0)))); + + // Release touch. + processId(mapper, -1); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0)))); +} + TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleAllToolTypes) { addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); @@ -8883,8 +9128,8 @@ TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShouldHandleDisplayId) { prepareAxes(POSITION); MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); - // Check source is mouse that would obtain the PointerController. - ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources()); + // Check source is a touchpad that would obtain the PointerController. + ASSERT_EQ(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, mapper.getSources()); NotifyMotionArgs motionArgs; processPosition(mapper, 100, 100); @@ -8927,6 +9172,7 @@ TEST_F(MultiTouchInputMapperTest, Process_SendsReadTime) { */ TEST_F(MultiTouchInputMapperTest, WhenViewportIsNotActive_TouchesAreDropped) { addConfigurationProperty("touch.deviceType", "touchScreen"); + // Don't set touch.enableForInactiveViewport to verify the default behavior. mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0, false /*isActive*/, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL); @@ -8941,8 +9187,31 @@ TEST_F(MultiTouchInputMapperTest, WhenViewportIsNotActive_TouchesAreDropped) { mFakeListener->assertNotifyMotionWasNotCalled(); } +/** + * When the viewport is not active (isActive=false) and touch.enableForInactiveViewport is true, + * the touch mapper can process the events and the events can be delivered to the listener. + */ +TEST_F(MultiTouchInputMapperTest, WhenViewportIsNotActive_TouchesAreProcessed) { + addConfigurationProperty("touch.deviceType", "touchScreen"); + addConfigurationProperty("touch.enableForInactiveViewport", "1"); + mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, + DISPLAY_ORIENTATION_0, false /*isActive*/, UNIQUE_ID, NO_PORT, + ViewportType::INTERNAL); + configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO); + prepareAxes(POSITION); + MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); + + NotifyMotionArgs motionArgs; + processPosition(mapper, 100, 100); + processSync(mapper); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); +} + TEST_F(MultiTouchInputMapperTest, Process_DeactivateViewport_AbortTouches) { addConfigurationProperty("touch.deviceType", "touchScreen"); + addConfigurationProperty("touch.enableForInactiveViewport", "0"); mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0, true /*isActive*/, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL); @@ -9887,11 +10156,11 @@ TEST_F(MultiTouchInputMapperTest, Process_TouchpadCapture) { ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action); - // non captured touchpad should be a mouse source + // A non captured touchpad should have a mouse and touchpad source. mFakePolicy->setPointerCapture(false); configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); - ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources()); + ASSERT_EQ(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, mapper.getSources()); } TEST_F(MultiTouchInputMapperTest, Process_UnCapturedTouchpadPointer) { @@ -9950,10 +10219,10 @@ TEST_F(MultiTouchInputMapperTest, WhenCapturedAndNotCaptured_GetSources) { mFakePolicy->setPointerCapture(false); MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); - // uncaptured touchpad should be a pointer device - ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources()); + // An uncaptured touchpad should be a pointer device, with additional touchpad source. + ASSERT_EQ(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, mapper.getSources()); - // captured touchpad should be a touchpad device + // A captured touchpad should just have a touchpad source. mFakePolicy->setPointerCapture(true); configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE); ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources()); @@ -10265,6 +10534,46 @@ TEST_F(MultiTouchPointerModeTest, TwoFingerSwipeOffsets) { ASSERT_GT(motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET), 0); } +TEST_F(MultiTouchPointerModeTest, WhenViewportActiveStatusChanged_PointerGestureIsReset) { + preparePointerMode(25 /*xResolution*/, 25 /*yResolution*/); + mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOOL_PEN, 0, AKEYCODE_UNKNOWN, 0); + MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled()); + + // Start a stylus gesture. + processKey(mapper, BTN_TOOL_PEN, 1); + processId(mapper, FIRST_TRACKING_ID); + processPosition(mapper, 100, 200); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithSource(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS)))); + // TODO(b/257078296): Pointer mode generates extra event. + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithSource(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS)))); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + + // Make the viewport inactive. This will put the device in disabled mode, and the ongoing stylus + // gesture should be disabled. + auto viewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL); + viewport->isActive = false; + mFakePolicy->updateViewport(*viewport); + configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_CANCEL), + WithSource(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS)))); + // TODO(b/257078296): Pointer mode generates extra event. + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_CANCEL), + WithSource(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS), + WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS)))); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); +} + // --- JoystickInputMapperTest --- class JoystickInputMapperTest : public InputMapperTest { diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp index 29093efc67..5e47b8019b 100644 --- a/services/inputflinger/tests/TestInputListener.cpp +++ b/services/inputflinger/tests/TestInputListener.cpp @@ -59,6 +59,12 @@ void TestInputListener::assertNotifyKeyWasCalled(NotifyKeyArgs* outEventArgs) { assertCalled<NotifyKeyArgs>(outEventArgs, "Expected notifyKey() to have been called.")); } +void TestInputListener::assertNotifyKeyWasCalled(const ::testing::Matcher<NotifyKeyArgs>& matcher) { + NotifyKeyArgs outEventArgs; + ASSERT_NO_FATAL_FAILURE(assertNotifyKeyWasCalled(&outEventArgs)); + ASSERT_THAT(outEventArgs, matcher); +} + void TestInputListener::assertNotifyKeyWasNotCalled() { ASSERT_NO_FATAL_FAILURE(assertNotCalled<NotifyKeyArgs>("notifyKey() should not be called.")); } diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h index 4ad1c4264d..87752e1487 100644 --- a/services/inputflinger/tests/TestInputListener.h +++ b/services/inputflinger/tests/TestInputListener.h @@ -44,6 +44,8 @@ public: void assertNotifyKeyWasCalled(NotifyKeyArgs* outEventArgs = nullptr); + void assertNotifyKeyWasCalled(const ::testing::Matcher<NotifyKeyArgs>& matcher); + void assertNotifyKeyWasNotCalled(); void assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs = nullptr); diff --git a/services/inputflinger/tests/TestInputListenerMatchers.h b/services/inputflinger/tests/TestInputListenerMatchers.h index e48f1d9082..5107af7e27 100644 --- a/services/inputflinger/tests/TestInputListenerMatchers.h +++ b/services/inputflinger/tests/TestInputListenerMatchers.h @@ -23,7 +23,7 @@ namespace android { -MATCHER_P(WithMotionAction, action, "InputEvent with specified action") { +MATCHER_P(WithMotionAction, action, "MotionEvent with specified action") { bool matches = action == arg.action; if (!matches) { *result_listener << "expected action " << MotionEvent::actionToString(action) @@ -39,6 +39,12 @@ MATCHER_P(WithMotionAction, action, "InputEvent with specified action") { return matches; } +MATCHER_P(WithKeyAction, action, "KeyEvent with specified action") { + *result_listener << "expected action " << KeyEvent::actionToString(action) << ", but got " + << KeyEvent::actionToString(arg.action); + return arg.action == action; +} + MATCHER_P(WithSource, source, "InputEvent with specified source") { *result_listener << "expected source " << source << ", but got " << arg.source; return arg.source == source; @@ -49,6 +55,11 @@ MATCHER_P(WithDisplayId, displayId, "InputEvent with specified displayId") { return arg.displayId == displayId; } +MATCHER_P(WithKeyCode, keyCode, "KeyEvent with specified key code") { + *result_listener << "expected key code " << keyCode << ", but got " << arg.keyCode; + return arg.keyCode == keyCode; +} + MATCHER_P2(WithCoords, x, y, "InputEvent with specified coords") { const auto argX = arg.pointerCoords[0].getX(); const auto argY = arg.pointerCoords[0].getY(); @@ -75,4 +86,9 @@ MATCHER_P(WithFlags, flags, "InputEvent with specified flags") { return arg.flags == flags; } +MATCHER_P(WithButtonState, buttons, "InputEvent with specified button state") { + *result_listener << "expected button state " << buttons << ", but got " << arg.buttonState; + return arg.buttonState == buttons; +} + } // namespace android diff --git a/services/inputflinger/tests/UinputDevice.cpp b/services/inputflinger/tests/UinputDevice.cpp index 626ad67c71..bc695b8bd8 100644 --- a/services/inputflinger/tests/UinputDevice.cpp +++ b/services/inputflinger/tests/UinputDevice.cpp @@ -24,7 +24,8 @@ namespace android { // --- UinputDevice --- -UinputDevice::UinputDevice(const char* name) : mName(name) {} +UinputDevice::UinputDevice(const char* name, int16_t productId) + : mName(name), mProductId(productId) {} UinputDevice::~UinputDevice() { if (ioctl(mDeviceFd, UI_DEV_DESTROY)) { @@ -43,7 +44,7 @@ void UinputDevice::init() { strlcpy(device.name, mName, UINPUT_MAX_NAME_SIZE); device.id.bustype = BUS_USB; device.id.vendor = 0x01; - device.id.product = 0x01; + device.id.product = mProductId; device.id.version = 1; ASSERT_NO_FATAL_FAILURE(configureDevice(mDeviceFd, &device)); @@ -76,8 +77,8 @@ void UinputDevice::injectEvent(uint16_t type, uint16_t code, int32_t value) { // --- UinputKeyboard --- -UinputKeyboard::UinputKeyboard(std::initializer_list<int> keys) - : UinputDevice(UinputKeyboard::KEYBOARD_NAME), mKeys(keys.begin(), keys.end()) {} +UinputKeyboard::UinputKeyboard(const char* name, int16_t productId, std::initializer_list<int> keys) + : UinputDevice(name, productId), mKeys(keys.begin(), keys.end()) {} void UinputKeyboard::configureDevice(int fd, uinput_user_dev* device) { // enable key press/release event @@ -121,18 +122,26 @@ void UinputKeyboard::pressAndReleaseKey(int key) { // --- UinputHomeKey --- -UinputHomeKey::UinputHomeKey() : UinputKeyboard({KEY_HOME}) {} +UinputHomeKey::UinputHomeKey() : UinputKeyboard(DEVICE_NAME, PRODUCT_ID, {KEY_HOME}) {} void UinputHomeKey::pressAndReleaseHomeKey() { pressAndReleaseKey(KEY_HOME); } -// --- UinputSteamController -UinputSteamController::UinputSteamController() : UinputKeyboard({BTN_GEAR_DOWN, BTN_GEAR_UP}) {} +// --- UinputSteamController --- + +UinputSteamController::UinputSteamController() + : UinputKeyboard(DEVICE_NAME, PRODUCT_ID, {BTN_GEAR_DOWN, BTN_GEAR_UP}) {} + +// --- UinputExternalStylus --- + +UinputExternalStylus::UinputExternalStylus() + : UinputKeyboard(DEVICE_NAME, PRODUCT_ID, {BTN_STYLUS, BTN_STYLUS2, BTN_STYLUS3}) {} // --- UinputTouchScreen --- -UinputTouchScreen::UinputTouchScreen(const Rect* size) - : UinputDevice(UinputTouchScreen::DEVICE_NAME), mSize(*size) {} + +UinputTouchScreen::UinputTouchScreen(const Rect& size) + : UinputDevice(DEVICE_NAME, PRODUCT_ID), mSize(size) {} void UinputTouchScreen::configureDevice(int fd, uinput_user_dev* device) { // Setup the touch screen device @@ -147,6 +156,9 @@ void UinputTouchScreen::configureDevice(int fd, uinput_user_dev* device) { ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOOL_TYPE); ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT); ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH); + ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS); + ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS2); + ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS3); device->absmin[ABS_MT_SLOT] = RAW_SLOT_MIN; device->absmax[ABS_MT_SLOT] = RAW_SLOT_MAX; @@ -198,6 +210,10 @@ void UinputTouchScreen::sendSync() { injectEvent(EV_SYN, SYN_REPORT, 0); } +void UinputTouchScreen::sendKey(int32_t scanCode, int32_t value) { + injectEvent(EV_KEY, scanCode, value); +} + // Get the center x, y base on the range definition. const Point UinputTouchScreen::getCenterPoint() { return Point(mSize.left + mSize.width() / 2, mSize.top + mSize.height() / 2); diff --git a/services/inputflinger/tests/UinputDevice.h b/services/inputflinger/tests/UinputDevice.h index e0ff8c3d4c..d661bd36d3 100644 --- a/services/inputflinger/tests/UinputDevice.h +++ b/services/inputflinger/tests/UinputDevice.h @@ -32,7 +32,7 @@ namespace android { template <class D, class... Ts> std::unique_ptr<D> createUinputDevice(Ts... args) { // Using `new` to access non-public constructors. - std::unique_ptr<D> dev(new D(&args...)); + std::unique_ptr<D> dev(new D(args...)); EXPECT_NO_FATAL_FAILURE(dev->init()); return dev; } @@ -51,8 +51,9 @@ public: protected: const char* mName; + const int16_t mProductId; - UinputDevice(const char* name); + explicit UinputDevice(const char* name, int16_t productId); // Signals which types of events this device supports before it is created. // This must be overridden by subclasses. @@ -71,7 +72,8 @@ private: class UinputKeyboard : public UinputDevice { public: - static constexpr const char* KEYBOARD_NAME = "Test Keyboard Device"; + static constexpr const char* KEYBOARD_NAME = "Test Uinput Keyboard Device"; + static constexpr int16_t PRODUCT_ID = 42; // Injects key press and sync. void pressKey(int key); @@ -84,7 +86,8 @@ public: friend std::unique_ptr<D> createUinputDevice(Ts... args); protected: - UinputKeyboard(std::initializer_list<int> keys = {}); + explicit UinputKeyboard(const char* name, int16_t productId = PRODUCT_ID, + std::initializer_list<int> keys = {}); private: void configureDevice(int fd, uinput_user_dev* device) override; @@ -97,6 +100,9 @@ private: // A keyboard device that has a single HOME key. class UinputHomeKey : public UinputKeyboard { public: + static constexpr const char* DEVICE_NAME = "Test Uinput Home Key"; + static constexpr int16_t PRODUCT_ID = 43; + // Injects 4 events: key press, sync, key release, and sync. void pressAndReleaseHomeKey(); @@ -104,24 +110,47 @@ public: friend std::unique_ptr<D> createUinputDevice(Ts... args); private: - UinputHomeKey(); + explicit UinputHomeKey(); }; +// --- UinputSteamController --- + // A joystick device that sends a BTN_GEAR_DOWN / BTN_WHEEL key. class UinputSteamController : public UinputKeyboard { public: + static constexpr const char* DEVICE_NAME = "Test Uinput Steam Controller"; + static constexpr int16_t PRODUCT_ID = 44; + + template <class D, class... Ts> + friend std::unique_ptr<D> createUinputDevice(Ts... args); + +private: + explicit UinputSteamController(); +}; + +// --- UinputExternalStylus --- + +// A stylus that reports button presses. +class UinputExternalStylus : public UinputKeyboard { +public: + static constexpr const char* DEVICE_NAME = "Test Uinput External Stylus"; + static constexpr int16_t PRODUCT_ID = 45; + template <class D, class... Ts> friend std::unique_ptr<D> createUinputDevice(Ts... args); private: - UinputSteamController(); + explicit UinputExternalStylus(); }; // --- UinputTouchScreen --- -// A touch screen device with specific size. + +// A multi-touch touchscreen device with specific size that also supports styluses. class UinputTouchScreen : public UinputDevice { public: - static constexpr const char* DEVICE_NAME = "Test Touch Screen"; + static constexpr const char* DEVICE_NAME = "Test Uinput Touch Screen"; + static constexpr int16_t PRODUCT_ID = 46; + static const int32_t RAW_TOUCH_MIN = 0; static const int32_t RAW_TOUCH_MAX = 31; static const int32_t RAW_ID_MIN = 0; @@ -142,11 +171,12 @@ public: void sendUp(); void sendToolType(int32_t toolType); void sendSync(); + void sendKey(int32_t scanCode, int32_t value); const Point getCenterPoint(); protected: - UinputTouchScreen(const Rect* size); + explicit UinputTouchScreen(const Rect& size); private: void configureDevice(int fd, uinput_user_dev* device) override; diff --git a/services/stats/StatsAidl.cpp b/services/stats/StatsAidl.cpp index 3de51a437d..9e13849485 100644 --- a/services/stats/StatsAidl.cpp +++ b/services/stats/StatsAidl.cpp @@ -69,6 +69,10 @@ ndk::ScopedAStatus StatsHal::reportVendorAtom(const VendorAtom& vendorAtom) { case VendorAtomValue::repeatedIntValue: { const std::optional<std::vector<int>>& repeatedIntValue = atomValue.get<VendorAtomValue::repeatedIntValue>(); + if (!repeatedIntValue) { + AStatsEvent_writeInt32Array(event, {}, 0); + break; + } AStatsEvent_writeInt32Array(event, repeatedIntValue->data(), repeatedIntValue->size()); break; @@ -76,6 +80,10 @@ ndk::ScopedAStatus StatsHal::reportVendorAtom(const VendorAtom& vendorAtom) { case VendorAtomValue::repeatedLongValue: { const std::optional<std::vector<int64_t>>& repeatedLongValue = atomValue.get<VendorAtomValue::repeatedLongValue>(); + if (!repeatedLongValue) { + AStatsEvent_writeInt64Array(event, {}, 0); + break; + } AStatsEvent_writeInt64Array(event, repeatedLongValue->data(), repeatedLongValue->size()); break; @@ -83,6 +91,10 @@ ndk::ScopedAStatus StatsHal::reportVendorAtom(const VendorAtom& vendorAtom) { case VendorAtomValue::repeatedFloatValue: { const std::optional<std::vector<float>>& repeatedFloatValue = atomValue.get<VendorAtomValue::repeatedFloatValue>(); + if (!repeatedFloatValue) { + AStatsEvent_writeFloatArray(event, {}, 0); + break; + } AStatsEvent_writeFloatArray(event, repeatedFloatValue->data(), repeatedFloatValue->size()); break; @@ -90,12 +102,18 @@ ndk::ScopedAStatus StatsHal::reportVendorAtom(const VendorAtom& vendorAtom) { case VendorAtomValue::repeatedStringValue: { const std::optional<std::vector<std::optional<std::string>>>& repeatedStringValue = atomValue.get<VendorAtomValue::repeatedStringValue>(); + if (!repeatedStringValue) { + AStatsEvent_writeStringArray(event, {}, 0); + break; + } const std::vector<std::optional<std::string>>& repeatedStringVector = *repeatedStringValue; const char* cStringArray[repeatedStringVector.size()]; for (int i = 0; i < repeatedStringVector.size(); ++i) { - cStringArray[i] = repeatedStringVector[i]->c_str(); + cStringArray[i] = repeatedStringVector[i].has_value() + ? repeatedStringVector[i]->c_str() + : ""; } AStatsEvent_writeStringArray(event, cStringArray, repeatedStringVector.size()); @@ -104,6 +122,10 @@ ndk::ScopedAStatus StatsHal::reportVendorAtom(const VendorAtom& vendorAtom) { case VendorAtomValue::repeatedBoolValue: { const std::optional<std::vector<bool>>& repeatedBoolValue = atomValue.get<VendorAtomValue::repeatedBoolValue>(); + if (!repeatedBoolValue) { + AStatsEvent_writeBoolArray(event, {}, 0); + break; + } const std::vector<bool>& repeatedBoolVector = *repeatedBoolValue; bool boolArray[repeatedBoolValue->size()]; @@ -117,7 +139,10 @@ ndk::ScopedAStatus StatsHal::reportVendorAtom(const VendorAtom& vendorAtom) { case VendorAtomValue::byteArrayValue: { const std::optional<std::vector<uint8_t>>& byteArrayValue = atomValue.get<VendorAtomValue::byteArrayValue>(); - + if (!byteArrayValue) { + AStatsEvent_writeByteArray(event, {}, 0); + break; + } AStatsEvent_writeByteArray(event, byteArrayValue->data(), byteArrayValue->size()); break; } diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index e76b191807..999c03f96b 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -49,7 +49,7 @@ cc_defaults { "android.hardware.graphics.composer@2.4", "android.hardware.power@1.0", "android.hardware.power@1.3", - "android.hardware.power-V2-cpp", + "android.hardware.power-V4-cpp", "libbase", "libbinder", "libbinder_ndk", @@ -181,7 +181,7 @@ filegroup { "Scheduler/LayerHistory.cpp", "Scheduler/LayerInfo.cpp", "Scheduler/MessageQueue.cpp", - "Scheduler/RefreshRateConfigs.cpp", + "Scheduler/RefreshRateSelector.cpp", "Scheduler/Scheduler.cpp", "Scheduler/VSyncDispatchTimerQueue.cpp", "Scheduler/VSyncPredictor.cpp", diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp index 7202bef606..bdbc79b8e1 100644 --- a/services/surfaceflinger/Client.cpp +++ b/services/surfaceflinger/Client.cpp @@ -25,6 +25,7 @@ #include "Client.h" #include "FrontEnd/LayerCreationArgs.h" +#include "FrontEnd/LayerHandle.h" #include "Layer.h" #include "SurfaceFlinger.h" @@ -47,36 +48,6 @@ status_t Client::initCheck() const { return NO_ERROR; } -void Client::attachLayer(const sp<IBinder>& handle, const sp<Layer>& layer) -{ - Mutex::Autolock _l(mLock); - mLayers.add(handle, layer); -} - -void Client::detachLayer(const Layer* layer) -{ - Mutex::Autolock _l(mLock); - // we do a linear search here, because this doesn't happen often - const size_t count = mLayers.size(); - for (size_t i=0 ; i<count ; i++) { - if (mLayers.valueAt(i) == layer) { - mLayers.removeItemsAt(i, 1); - break; - } - } -} -sp<Layer> Client::getLayerUser(const sp<IBinder>& handle) const -{ - Mutex::Autolock _l(mLock); - sp<Layer> lbc; - wp<Layer> layer(mLayers.valueFor(handle)); - if (layer != 0) { - lbc = layer.promote(); - ALOGE_IF(lbc==0, "getLayerUser(name=%p) is dead", handle.get()); - } - return lbc; -} - binder::Status Client::createSurface(const std::string& name, int32_t flags, const sp<IBinder>& parent, const gui::LayerMetadata& metadata, gui::CreateSurfaceResult* outResult) { @@ -91,7 +62,7 @@ binder::Status Client::createSurface(const std::string& name, int32_t flags, binder::Status Client::clearLayerFrameStats(const sp<IBinder>& handle) { status_t status; - sp<Layer> layer = getLayerUser(handle); + sp<Layer> layer = LayerHandle::getLayer(handle); if (layer == nullptr) { status = NAME_NOT_FOUND; } else { @@ -103,7 +74,7 @@ binder::Status Client::clearLayerFrameStats(const sp<IBinder>& handle) { binder::Status Client::getLayerFrameStats(const sp<IBinder>& handle, gui::FrameStats* outStats) { status_t status; - sp<Layer> layer = getLayerUser(handle); + sp<Layer> layer = LayerHandle::getLayer(handle); if (layer == nullptr) { status = NAME_NOT_FOUND; } else { diff --git a/services/surfaceflinger/Client.h b/services/surfaceflinger/Client.h index 02079a3b3f..af410ea19c 100644 --- a/services/surfaceflinger/Client.h +++ b/services/surfaceflinger/Client.h @@ -38,12 +38,6 @@ public: status_t initCheck() const; - // protected by SurfaceFlinger::mStateLock - void attachLayer(const sp<IBinder>& handle, const sp<Layer>& layer); - void detachLayer(const Layer* layer); - - sp<Layer> getLayerUser(const sp<IBinder>& handle) const; - private: // ISurfaceComposerClient interface @@ -64,9 +58,6 @@ private: // constant sp<SurfaceFlinger> mFlinger; - // protected by mLock - DefaultKeyedVector< wp<IBinder>, wp<Layer> > mLayers; - // thread-safe mutable Mutex mLock; }; diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp index 0ae8bf98bf..c1460cfc95 100644 --- a/services/surfaceflinger/CompositionEngine/Android.bp +++ b/services/surfaceflinger/CompositionEngine/Android.bp @@ -25,7 +25,7 @@ cc_defaults { "android.hardware.graphics.composer@2.4", "android.hardware.power@1.0", "android.hardware.power@1.3", - "android.hardware.power-V2-cpp", + "android.hardware.power-V4-cpp", "libbase", "libcutils", "libgui", diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h index 6832ae12df..7c10fa57ec 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h @@ -56,9 +56,9 @@ public: virtual void setHwComposer(std::unique_ptr<HWComposer>) = 0; virtual renderengine::RenderEngine& getRenderEngine() const = 0; - virtual void setRenderEngine(std::unique_ptr<renderengine::RenderEngine>) = 0; + virtual void setRenderEngine(renderengine::RenderEngine*) = 0; - virtual TimeStats& getTimeStats() const = 0; + virtual TimeStats* getTimeStats() const = 0; virtual void setTimeStats(const std::shared_ptr<TimeStats>&) = 0; virtual bool needsAnotherUpdate() const = 0; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h index dd4dbe9318..c6995576a1 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h @@ -34,9 +34,9 @@ public: void setHwComposer(std::unique_ptr<HWComposer>) override; renderengine::RenderEngine& getRenderEngine() const override; - void setRenderEngine(std::unique_ptr<renderengine::RenderEngine>) override; + void setRenderEngine(renderengine::RenderEngine*) override; - TimeStats& getTimeStats() const override; + TimeStats* getTimeStats() const override; void setTimeStats(const std::shared_ptr<TimeStats>&) override; bool needsAnotherUpdate() const override; @@ -58,7 +58,7 @@ public: private: std::unique_ptr<HWComposer> mHwComposer; - std::unique_ptr<renderengine::RenderEngine> mRenderEngine; + renderengine::RenderEngine* mRenderEngine; std::shared_ptr<TimeStats> mTimeStats; bool mNeedsAnotherUpdate = false; nsecs_t mRefreshStartTime = 0; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h index a48cc6f975..9b2387b966 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h @@ -40,9 +40,9 @@ public: MOCK_METHOD1(setHwComposer, void(std::unique_ptr<HWComposer>)); MOCK_CONST_METHOD0(getRenderEngine, renderengine::RenderEngine&()); - MOCK_METHOD1(setRenderEngine, void(std::unique_ptr<renderengine::RenderEngine>)); + MOCK_METHOD1(setRenderEngine, void(renderengine::RenderEngine*)); - MOCK_CONST_METHOD0(getTimeStats, TimeStats&()); + MOCK_CONST_METHOD0(getTimeStats, TimeStats*()); MOCK_METHOD1(setTimeStats, void(const std::shared_ptr<TimeStats>&)); MOCK_CONST_METHOD0(needsAnotherUpdate, bool()); diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp index a4e1fff2ad..15fadbc8ee 100644 --- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp +++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp @@ -65,15 +65,15 @@ void CompositionEngine::setHwComposer(std::unique_ptr<HWComposer> hwComposer) { } renderengine::RenderEngine& CompositionEngine::getRenderEngine() const { - return *mRenderEngine.get(); + return *mRenderEngine; } -void CompositionEngine::setRenderEngine(std::unique_ptr<renderengine::RenderEngine> renderEngine) { - mRenderEngine = std::move(renderEngine); +void CompositionEngine::setRenderEngine(renderengine::RenderEngine* renderEngine) { + mRenderEngine = renderEngine; } -TimeStats& CompositionEngine::getTimeStats() const { - return *mTimeStats.get(); +TimeStats* CompositionEngine::getTimeStats() const { + return mTimeStats.get(); } void CompositionEngine::setTimeStats(const std::shared_ptr<TimeStats>& timeStats) { diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp index 0b69d44ac4..1c5cbedd88 100644 --- a/services/surfaceflinger/CompositionEngine/src/Display.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp @@ -248,12 +248,17 @@ bool Display::chooseCompositionStrategy( return false; } - const TimePoint startTime = TimePoint::now(); - // Get any composition changes requested by the HWC device, and apply them. std::optional<android::HWComposer::DeviceRequestedChanges> changes; auto& hwc = getCompositionEngine().getHwComposer(); const bool requiresClientComposition = anyLayersRequireClientComposition(); + + if (isPowerHintSessionEnabled()) { + mPowerAdvisor->setRequiresClientComposition(mId, requiresClientComposition); + } + + const TimePoint hwcValidateStartTime = TimePoint::now(); + if (status_t result = hwc.getDeviceCompositionChanges(*halDisplayId, requiresClientComposition, getState().earliestPresentTime, @@ -266,8 +271,10 @@ bool Display::chooseCompositionStrategy( } if (isPowerHintSessionEnabled()) { - mPowerAdvisor->setHwcValidateTiming(mId, startTime, TimePoint::now()); - mPowerAdvisor->setRequiresClientComposition(mId, requiresClientComposition); + mPowerAdvisor->setHwcValidateTiming(mId, hwcValidateStartTime, TimePoint::now()); + if (auto halDisplayId = HalDisplayId::tryCast(mId)) { + mPowerAdvisor->setSkippedValidate(mId, hwc.getValidateSkipped(*halDisplayId)); + } } return true; @@ -432,13 +439,6 @@ void Display::finishFrame(const compositionengine::CompositionRefreshArgs& refre } impl::Output::finishFrame(refreshArgs, std::move(result)); - - if (isPowerHintSessionEnabled()) { - auto& hwc = getCompositionEngine().getHwComposer(); - if (auto halDisplayId = HalDisplayId::tryCast(mId)) { - mPowerAdvisor->setSkippedValidate(mId, hwc.getValidateSkipped(*halDisplayId)); - } - } } } // namespace android::compositionengine::impl diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index 0622534491..c2b1f0679c 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -1178,15 +1178,9 @@ void Output::updateProtectedContentState() { bool needsProtected = std::any_of(layers.begin(), layers.end(), [](auto* layer) { return layer->getLayerFE().getCompositionState()->hasProtectedContent; }); - if (needsProtected != renderEngine.isProtected()) { - renderEngine.useProtectedContext(needsProtected); - } - if (needsProtected != mRenderSurface->isProtected() && - needsProtected == renderEngine.isProtected()) { + if (needsProtected != mRenderSurface->isProtected()) { mRenderSurface->setProtected(needsProtected); } - } else if (!outputState.isSecure && renderEngine.isProtected()) { - renderEngine.useProtectedContext(false); } } @@ -1340,10 +1334,13 @@ std::optional<base::unique_fd> Output::composeSurfaces( const auto fence = std::move(fenceResult).value_or(Fence::NO_FENCE); - if (auto& timeStats = getCompositionEngine().getTimeStats(); fence->isValid()) { - timeStats.recordRenderEngineDuration(renderEngineStart, std::make_shared<FenceTime>(fence)); - } else { - timeStats.recordRenderEngineDuration(renderEngineStart, systemTime()); + if (auto timeStats = getCompositionEngine().getTimeStats()) { + if (fence->isValid()) { + timeStats->recordRenderEngineDuration(renderEngineStart, + std::make_shared<FenceTime>(fence)); + } else { + timeStats->recordRenderEngineDuration(renderEngineStart, systemTime()); + } } for (auto* clientComposedLayer : clientCompositionLayersFE) { diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp index b570979f1f..60ed660c7a 100644 --- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp @@ -62,17 +62,16 @@ TEST_F(CompositionEngineTest, canSetHWComposer) { } TEST_F(CompositionEngineTest, canSetRenderEngine) { - renderengine::mock::RenderEngine* renderEngine = - new StrictMock<renderengine::mock::RenderEngine>(); - mEngine.setRenderEngine(std::unique_ptr<renderengine::RenderEngine>(renderEngine)); + auto renderEngine = std::make_unique<StrictMock<renderengine::mock::RenderEngine>>(); + mEngine.setRenderEngine(renderEngine.get()); - EXPECT_EQ(renderEngine, &mEngine.getRenderEngine()); + EXPECT_EQ(renderEngine.get(), &mEngine.getRenderEngine()); } TEST_F(CompositionEngineTest, canSetTimeStats) { mEngine.setTimeStats(mTimeStats); - EXPECT_EQ(mTimeStats.get(), &mEngine.getTimeStats()); + EXPECT_EQ(mTimeStats.get(), mEngine.getTimeStats()); } /* diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp index 514a8ff8fc..21099876f4 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -3332,8 +3332,7 @@ struct OutputComposeSurfacesTest : public testing::Test { EXPECT_CALL(mOutput, getCompositionEngine()).WillRepeatedly(ReturnRef(mCompositionEngine)); EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine)); - EXPECT_CALL(mCompositionEngine, getTimeStats()) - .WillRepeatedly(ReturnRef(*mTimeStats.get())); + EXPECT_CALL(mCompositionEngine, getTimeStats()).WillRepeatedly(Return(mTimeStats.get())); EXPECT_CALL(*mDisplayColorProfile, getHdrCapabilities()) .WillRepeatedly(ReturnRef(kHdrCapabilities)); } @@ -4010,39 +4009,11 @@ struct OutputComposeSurfacesTest_HandlesProtectedContent : public OutputComposeS Layer mLayer2; }; -TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifDisplayIsNotSecure) { - mOutput.mState.isSecure = false; - mLayer2.mLayerFEState.hasProtectedContent = true; - EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true)); - EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true)); - EXPECT_CALL(mRenderEngine, useProtectedContext(false)); - - base::unique_fd fd; - std::shared_ptr<renderengine::ExternalTexture> tex; - mOutput.updateProtectedContentState(); - mOutput.dequeueRenderBuffer(&fd, &tex); - mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd); -} - -TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifRenderEngineDoesNotSupportIt) { - mOutput.mState.isSecure = true; - mLayer2.mLayerFEState.hasProtectedContent = true; - EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); - - base::unique_fd fd; - std::shared_ptr<renderengine::ExternalTexture> tex; - mOutput.updateProtectedContentState(); - mOutput.dequeueRenderBuffer(&fd, &tex); - mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd); -} - TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNoProtectedContentLayers) { mOutput.mState.isSecure = true; mLayer2.mLayerFEState.hasProtectedContent = false; EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true)); - EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true)).WillOnce(Return(false)); EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true)); - EXPECT_CALL(mRenderEngine, useProtectedContext(false)); EXPECT_CALL(*mRenderSurface, setProtected(false)); base::unique_fd fd; @@ -4060,10 +4031,7 @@ TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNotEnabled) { // For this test, we also check the call order of key functions. InSequence seq; - EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(false)); - EXPECT_CALL(mRenderEngine, useProtectedContext(true)); EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false)); - EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true)); EXPECT_CALL(*mRenderSurface, setProtected(true)); // Must happen after setting the protected content state. EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer)); @@ -4081,7 +4049,6 @@ TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledEveryw mOutput.mState.isSecure = true; mLayer2.mLayerFEState.hasProtectedContent = true; EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true)); - EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true)); EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true)); base::unique_fd fd; @@ -4091,43 +4058,11 @@ TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledEveryw mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd); } -TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifFailsToEnableInRenderEngine) { - mOutput.mState.isSecure = true; - mLayer2.mLayerFEState.hasProtectedContent = true; - EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true)); - EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(false)).WillOnce(Return(false)); - EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false)); - EXPECT_CALL(mRenderEngine, useProtectedContext(true)); - - base::unique_fd fd; - std::shared_ptr<renderengine::ExternalTexture> tex; - mOutput.updateProtectedContentState(); - mOutput.dequeueRenderBuffer(&fd, &tex); - mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd); -} - -TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRenderEngine) { - mOutput.mState.isSecure = true; - mLayer2.mLayerFEState.hasProtectedContent = true; - EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true)); - EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true)).WillOnce(Return(true)); - EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false)); - EXPECT_CALL(*mRenderSurface, setProtected(true)); - - base::unique_fd fd; - std::shared_ptr<renderengine::ExternalTexture> tex; - mOutput.updateProtectedContentState(); - mOutput.dequeueRenderBuffer(&fd, &tex); - mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd); -} - TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRenderSurface) { mOutput.mState.isSecure = true; mLayer2.mLayerFEState.hasProtectedContent = true; EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true)); - EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(false)); EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true)); - EXPECT_CALL(mRenderEngine, useProtectedContext(true)); base::unique_fd fd; std::shared_ptr<renderengine::ExternalTexture> tex; diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index c63d57f6ee..18ddfbca6c 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -69,7 +69,7 @@ DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs& args) mActiveModeFPSHwcTrace("ActiveModeFPS_HWC -" + to_string(getId())), mPhysicalOrientation(args.physicalOrientation), mIsPrimary(args.isPrimary), - mRefreshRateConfigs(std::move(args.refreshRateConfigs)) { + mRefreshRateSelector(std::move(args.refreshRateSelector)) { mCompositionDisplay->editState().isSecure = args.isSecure; mCompositionDisplay->createRenderSurface( compositionengine::RenderSurfaceCreationArgsBuilder() @@ -200,7 +200,7 @@ void DisplayDevice::setActiveMode(DisplayModeId modeId, const display::DisplaySn ATRACE_INT(mActiveModeFPSTrace.c_str(), fps.getIntValue()); - mRefreshRateConfigs->setActiveModeId(modeId); + mRefreshRateSelector->setActiveModeId(modeId); if (mRefreshRateOverlay) { mRefreshRateOverlay->changeRefreshRate(fps); @@ -234,7 +234,7 @@ nsecs_t DisplayDevice::getVsyncPeriodFromHWC() const { return vsyncPeriod; } - return refreshRateConfigs().getActiveModePtr()->getVsyncPeriod(); + return refreshRateSelector().getActiveModePtr()->getVsyncPeriod(); } ui::Dataspace DisplayDevice::getCompositionDataSpace() const { @@ -335,8 +335,8 @@ void DisplayDevice::dump(utils::Dumper& dumper) const { utils::Dumper::Indent indent(dumper); dumper.dump("powerMode"sv, mPowerMode); - if (mRefreshRateConfigs) { - mRefreshRateConfigs->dump(dumper); + if (mRefreshRateSelector) { + mRefreshRateSelector->dump(dumper); } } @@ -430,7 +430,7 @@ void DisplayDevice::enableRefreshRateOverlay(bool enable, bool showSpinnner) { return; } - const auto fpsRange = mRefreshRateConfigs->getSupportedRefreshRateRange(); + const auto fpsRange = mRefreshRateSelector->getSupportedRefreshRateRange(); mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(fpsRange, showSpinnner); mRefreshRateOverlay->setLayerStack(getLayerStack()); mRefreshRateOverlay->setViewport(getSize()); @@ -439,9 +439,9 @@ void DisplayDevice::enableRefreshRateOverlay(bool enable, bool showSpinnner) { bool DisplayDevice::onKernelTimerChanged(std::optional<DisplayModeId> desiredModeId, bool timerExpired) { - if (mRefreshRateConfigs && mRefreshRateOverlay) { + if (mRefreshRateSelector && mRefreshRateOverlay) { const auto newRefreshRate = - mRefreshRateConfigs->onKernelTimerChanged(desiredModeId, timerExpired); + mRefreshRateSelector->onKernelTimerChanged(desiredModeId, timerExpired); if (newRefreshRate) { mRefreshRateOverlay->changeRefreshRate(*newRefreshRate); return true; @@ -475,7 +475,7 @@ bool DisplayDevice::setDesiredActiveMode(const ActiveModeInfo& info) { } // Check if we are already at the desired mode - if (refreshRateConfigs().getActiveModePtr()->getId() == info.mode->getId()) { + if (refreshRateSelector().getActiveModePtr()->getId() == info.mode->getId()) { return false; } diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index 7abb94b84f..6c848bb100 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -45,7 +45,7 @@ #include "DisplayHardware/DisplayMode.h" #include "DisplayHardware/Hal.h" #include "DisplayHardware/PowerAdvisor.h" -#include "Scheduler/RefreshRateConfigs.h" +#include "Scheduler/RefreshRateSelector.h" #include "ThreadContext.h" #include "TracedOrdinal.h" #include "Utils/Dumper.h" @@ -219,7 +219,7 @@ public: } const DisplayMode& getActiveMode() const REQUIRES(kMainThreadContext) { - return mRefreshRateConfigs->getActiveMode(); + return mRefreshRateSelector->getActiveMode(); } // Precondition: DisplaySnapshot must contain a mode with DisplayModeId. @@ -230,14 +230,11 @@ public: hal::VsyncPeriodChangeTimeline* outTimeline) REQUIRES(kMainThreadContext); - // Returns the refresh rate configs for this display. - scheduler::RefreshRateConfigs& refreshRateConfigs() const { return *mRefreshRateConfigs; } + scheduler::RefreshRateSelector& refreshRateSelector() const { return *mRefreshRateSelector; } - // Returns a shared pointer to the refresh rate configs for this display. - // Clients can store this refresh rate configs and use it even if the DisplayDevice - // is destroyed. - std::shared_ptr<scheduler::RefreshRateConfigs> holdRefreshRateConfigs() const { - return mRefreshRateConfigs; + // Extends the lifetime of the RefreshRateSelector, so it can outlive this DisplayDevice. + std::shared_ptr<scheduler::RefreshRateSelector> holdRefreshRateSelector() const { + return mRefreshRateSelector; } // Enables an overlay to be displayed with the current refresh rate @@ -287,7 +284,7 @@ private: std::vector<ui::Hdr> mOverrideHdrTypes; - std::shared_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs; + std::shared_ptr<scheduler::RefreshRateSelector> mRefreshRateSelector; std::unique_ptr<RefreshRateOverlay> mRefreshRateOverlay; mutable std::mutex mActiveModeLock; @@ -337,7 +334,7 @@ struct DisplayDeviceCreationArgs { HWComposer& hwComposer; const wp<IBinder> displayToken; const std::shared_ptr<compositionengine::Display> compositionDisplay; - std::shared_ptr<scheduler::RefreshRateConfigs> refreshRateConfigs; + std::shared_ptr<scheduler::RefreshRateSelector> refreshRateSelector; int32_t sequenceId{0}; bool isSecure{false}; diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 977709286b..d1e61a3273 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -196,7 +196,7 @@ Layer::Layer(const LayerCreationArgs& args) } mFrameTracker.setDisplayRefreshPeriod( - args.flinger->mScheduler->getVsyncPeriodFromRefreshRateConfigs()); + args.flinger->mScheduler->getVsyncPeriodFromRefreshRateSelector()); mOwnerUid = args.ownerUid; mOwnerPid = args.ownerPid; @@ -240,11 +240,6 @@ Layer::~Layer() { mFlinger->mTimeStats->onDestroy(layerId); mFlinger->mFrameTracer->onDestroy(layerId); - sp<Client> c(mClientRef.promote()); - if (c != 0) { - c->detachLayer(this); - } - mFrameTracker.logAndResetStats(mName); mFlinger->onLayerDestroyed(this); @@ -477,7 +472,7 @@ void Layer::prepareBasicGeometryCompositionState() { snapshot->geomLayerTransform = getTransform(); snapshot->geomInverseLayerTransform = snapshot->geomLayerTransform.inverse(); snapshot->transparentRegionHint = getActiveTransparentRegion(drawingState); - + snapshot->blurRegionTransform = getActiveTransform(drawingState).inverse(); snapshot->blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode); snapshot->alpha = alpha; snapshot->backgroundBlurRadius = drawingState.backgroundBlurRadius; @@ -1129,7 +1124,7 @@ bool Layer::propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool* tran // We return whether this layer ot its children has a vote. We ignore ExactOrMultiple votes for // the same reason we are allowing touch boost for those layers. See - // RefreshRateConfigs::getBestRefreshRate for more details. + // RefreshRateSelector::rankRefreshRates for details. const auto layerVotedWithDefaultCompatibility = frameRate.rate.isValid() && frameRate.type == FrameRateCompatibility::Default; const auto layerVotedWithNoVote = frameRate.type == FrameRateCompatibility::NoVote; @@ -3598,7 +3593,7 @@ void Layer::onPostComposition(const DisplayDevice* display, } if (display) { - const Fps refreshRate = display->refreshRateConfigs().getActiveModePtr()->getFps(); + const Fps refreshRate = display->refreshRateSelector().getActiveModePtr()->getFps(); const std::optional<Fps> renderRate = mFlinger->mScheduler->getFrameRateOverride(getOwnerUid()); diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp index 3bdb521d0e..363adc641e 100644 --- a/services/surfaceflinger/LayerFE.cpp +++ b/services/surfaceflinger/LayerFE.cpp @@ -148,14 +148,14 @@ std::optional<compositionengine::LayerFE::LayerSettings> LayerFE::prepareClientC case LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled: layerSettings.backgroundBlurRadius = mSnapshot->backgroundBlurRadius; layerSettings.blurRegions = mSnapshot->blurRegions; - layerSettings.blurRegionTransform = mSnapshot->geomInverseLayerTransform.asMatrix4(); + layerSettings.blurRegionTransform = mSnapshot->blurRegionTransform.asMatrix4(); break; case LayerFE::ClientCompositionTargetSettings::BlurSetting::BackgroundBlurOnly: layerSettings.backgroundBlurRadius = mSnapshot->backgroundBlurRadius; break; case LayerFE::ClientCompositionTargetSettings::BlurSetting::BlurRegionsOnly: layerSettings.blurRegions = mSnapshot->blurRegions; - layerSettings.blurRegionTransform = mSnapshot->geomInverseLayerTransform.asMatrix4(); + layerSettings.blurRegionTransform = mSnapshot->blurRegionTransform.asMatrix4(); break; case LayerFE::ClientCompositionTargetSettings::BlurSetting::Disabled: default: diff --git a/services/surfaceflinger/LayerFE.h b/services/surfaceflinger/LayerFE.h index e4f6889762..822bcb7a94 100644 --- a/services/surfaceflinger/LayerFE.h +++ b/services/surfaceflinger/LayerFE.h @@ -57,6 +57,7 @@ struct LayerSnapshot : public compositionengine::LayerFECompositionState { gui::LayerMetadata relativeLayerMetadata; bool contentDirty; bool hasReadyFrame; + ui::Transform blurRegionTransform; }; struct CompositionResult { diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp index 8dd3b0f59d..6e64e0a13b 100644 --- a/services/surfaceflinger/RegionSamplingThread.cpp +++ b/services/surfaceflinger/RegionSamplingThread.cpp @@ -40,6 +40,7 @@ #include "DisplayDevice.h" #include "DisplayRenderArea.h" +#include "FrontEnd/LayerCreationArgs.h" #include "Layer.h" #include "Scheduler/VsyncController.h" #include "SurfaceFlinger.h" @@ -129,12 +130,12 @@ RegionSamplingThread::~RegionSamplingThread() { } } -void RegionSamplingThread::addListener(const Rect& samplingArea, const wp<Layer>& stopLayer, +void RegionSamplingThread::addListener(const Rect& samplingArea, uint32_t stopLayerId, const sp<IRegionSamplingListener>& listener) { sp<IBinder> asBinder = IInterface::asBinder(listener); asBinder->linkToDeath(sp<DeathRecipient>::fromExisting(this)); std::lock_guard lock(mSamplingMutex); - mDescriptors.emplace(wp<IBinder>(asBinder), Descriptor{samplingArea, stopLayer, listener}); + mDescriptors.emplace(wp<IBinder>(asBinder), Descriptor{samplingArea, stopLayerId, listener}); } void RegionSamplingThread::removeListener(const sp<IRegionSamplingListener>& listener) { @@ -291,8 +292,8 @@ void RegionSamplingThread::captureSample() { if (stopLayerFound) return; // Likewise if we just found a stop layer, set the flag and abort - for (const auto& [area, stopLayer, listener] : descriptors) { - if (layer == stopLayer.promote().get()) { + for (const auto& [area, stopLayerId, listener] : descriptors) { + if (stopLayerId != UNASSIGNED_LAYER_ID && layer->getSequence() == stopLayerId) { stopLayerFound = true; return; } diff --git a/services/surfaceflinger/RegionSamplingThread.h b/services/surfaceflinger/RegionSamplingThread.h index 686b4b1e1f..b62b15cb6d 100644 --- a/services/surfaceflinger/RegionSamplingThread.h +++ b/services/surfaceflinger/RegionSamplingThread.h @@ -26,6 +26,7 @@ #include <chrono> #include <condition_variable> +#include <cstdint> #include <mutex> #include <thread> #include <unordered_map> @@ -73,7 +74,7 @@ public: // Add a listener to receive luma notifications. The luma reported via listener will // report the median luma for the layers under the stopLayerHandle, in the samplingArea region. - void addListener(const Rect& samplingArea, const wp<Layer>& stopLayer, + void addListener(const Rect& samplingArea, uint32_t stopLayerId, const sp<IRegionSamplingListener>& listener); // Remove the listener to stop receiving median luma notifications. void removeListener(const sp<IRegionSamplingListener>& listener); @@ -87,7 +88,7 @@ public: private: struct Descriptor { Rect area = Rect::EMPTY_RECT; - wp<Layer> stopLayer; + uint32_t stopLayerId; sp<IRegionSamplingListener> listener; }; diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp index ae111c3d45..b884dc873d 100644 --- a/services/surfaceflinger/Scheduler/LayerHistory.cpp +++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp @@ -164,7 +164,7 @@ void LayerHistory::setDefaultFrameRateCompatibility(Layer* layer, bool contentDe getVoteType(layer->getDefaultFrameRateCompatibility(), contentDetectionEnabled)); } -auto LayerHistory::summarize(const RefreshRateConfigs& configs, nsecs_t now) -> Summary { +auto LayerHistory::summarize(const RefreshRateSelector& selector, nsecs_t now) -> Summary { Summary summary; std::lock_guard lock(mLock); @@ -178,7 +178,7 @@ auto LayerHistory::summarize(const RefreshRateConfigs& configs, nsecs_t now) -> ALOGV("%s has priority: %d %s focused", info->getName().c_str(), frameRateSelectionPriority, layerFocused ? "" : "not"); - const auto vote = info->getRefreshRateVote(configs, now); + const auto vote = info->getRefreshRateVote(selector, now); // Skip NoVote layer as those don't have any requirements if (vote.type == LayerVoteType::NoVote) { continue; diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h index 12bec8dfc1..5022906ff9 100644 --- a/services/surfaceflinger/Scheduler/LayerHistory.h +++ b/services/surfaceflinger/Scheduler/LayerHistory.h @@ -27,7 +27,7 @@ #include <utility> #include <vector> -#include "RefreshRateConfigs.h" +#include "RefreshRateSelector.h" namespace android { @@ -39,7 +39,7 @@ class LayerInfo; class LayerHistory { public: - using LayerVoteType = RefreshRateConfigs::LayerVoteType; + using LayerVoteType = RefreshRateSelector::LayerVoteType; LayerHistory(); ~LayerHistory(); @@ -67,10 +67,10 @@ public: // does not set a preference for refresh rate. void setDefaultFrameRateCompatibility(Layer*, bool contentDetectionEnabled); - using Summary = std::vector<RefreshRateConfigs::LayerRequirement>; + using Summary = std::vector<RefreshRateSelector::LayerRequirement>; // Rebuilds sets of active/inactive layers, and accumulates stats for active layers. - Summary summarize(const RefreshRateConfigs&, nsecs_t now); + Summary summarize(const RefreshRateSelector&, nsecs_t now); void clear(); diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp index 943615c45c..7247e4b260 100644 --- a/services/surfaceflinger/Scheduler/LayerInfo.cpp +++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp @@ -187,8 +187,8 @@ std::optional<nsecs_t> LayerInfo::calculateAverageFrameTime() const { return static_cast<nsecs_t>(averageFrameTime); } -std::optional<Fps> LayerInfo::calculateRefreshRateIfPossible( - const RefreshRateConfigs& refreshRateConfigs, nsecs_t now) { +std::optional<Fps> LayerInfo::calculateRefreshRateIfPossible(const RefreshRateSelector& selector, + nsecs_t now) { static constexpr float MARGIN = 1.0f; // 1Hz if (!hasEnoughDataForHeuristic()) { ALOGV("Not enough data"); @@ -199,7 +199,7 @@ std::optional<Fps> LayerInfo::calculateRefreshRateIfPossible( const auto refreshRate = Fps::fromPeriodNsecs(*averageFrameTime); const bool refreshRateConsistent = mRefreshRateHistory.add(refreshRate, now); if (refreshRateConsistent) { - const auto knownRefreshRate = refreshRateConfigs.findClosestKnownFrameRate(refreshRate); + const auto knownRefreshRate = selector.findClosestKnownFrameRate(refreshRate); using fps_approx_ops::operator!=; // To avoid oscillation, use the last calculated refresh rate if it is close enough. @@ -222,7 +222,7 @@ std::optional<Fps> LayerInfo::calculateRefreshRateIfPossible( : std::nullopt; } -LayerInfo::LayerVote LayerInfo::getRefreshRateVote(const RefreshRateConfigs& refreshRateConfigs, +LayerInfo::LayerVote LayerInfo::getRefreshRateVote(const RefreshRateSelector& selector, nsecs_t now) { if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) { ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type)); @@ -250,7 +250,7 @@ LayerInfo::LayerVote LayerInfo::getRefreshRateVote(const RefreshRateConfigs& ref clearHistory(now); } - auto refreshRate = calculateRefreshRateIfPossible(refreshRateConfigs, now); + auto refreshRate = calculateRefreshRateIfPossible(selector, now); if (refreshRate.has_value()) { ALOGV("%s calculated refresh rate: %s", mName.c_str(), to_string(*refreshRate).c_str()); return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()}; diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h index 28cb24a64f..a5ffbbecb5 100644 --- a/services/surfaceflinger/Scheduler/LayerInfo.h +++ b/services/surfaceflinger/Scheduler/LayerInfo.h @@ -28,7 +28,7 @@ #include <scheduler/Seamlessness.h> #include "LayerHistory.h" -#include "RefreshRateConfigs.h" +#include "RefreshRateSelector.h" namespace android { @@ -162,7 +162,7 @@ public: uid_t getOwnerUid() const { return mOwnerUid; } - LayerVote getRefreshRateVote(const RefreshRateConfigs&, nsecs_t now); + LayerVote getRefreshRateVote(const RefreshRateSelector&, nsecs_t now); // Return the last updated time. If the present time is farther in the future than the // updated time, the updated time is the present time. @@ -261,7 +261,7 @@ private: bool isFrequent(nsecs_t now) const; bool isAnimating(nsecs_t now) const; bool hasEnoughDataForHeuristic() const; - std::optional<Fps> calculateRefreshRateIfPossible(const RefreshRateConfigs&, nsecs_t now); + std::optional<Fps> calculateRefreshRateIfPossible(const RefreshRateSelector&, nsecs_t now); std::optional<nsecs_t> calculateAverageFrameTime() const; bool isFrameTimeValid(const FrameTimeData&) const; diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp index 39850c7e1e..c913891b62 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp @@ -30,13 +30,14 @@ #include <ftl/enum.h> #include <ftl/fake_guard.h> #include <ftl/match.h> +#include <ftl/unit.h> #include <utils/Trace.h> #include "../SurfaceFlingerProperties.h" -#include "RefreshRateConfigs.h" +#include "RefreshRateSelector.h" #undef LOG_TAG -#define LOG_TAG "RefreshRateConfigs" +#define LOG_TAG "RefreshRateSelector" namespace android::scheduler { namespace { @@ -50,9 +51,9 @@ struct RefreshRateScore { } fixedRateBelowThresholdLayersScore; }; -constexpr RefreshRateConfigs::GlobalSignals kNoSignals; +constexpr RefreshRateSelector::GlobalSignals kNoSignals; -std::string formatLayerInfo(const RefreshRateConfigs::LayerRequirement& layer, float weight) { +std::string formatLayerInfo(const RefreshRateSelector::LayerRequirement& layer, float weight) { return base::StringPrintf("%s (type=%s, weight=%.2f, seamlessness=%s) %s", layer.name.c_str(), ftl::enum_string(layer.vote).c_str(), weight, ftl::enum_string(layer.seamlessness).c_str(), @@ -105,13 +106,13 @@ std::vector<DisplayModeIterator> sortByRefreshRate(const DisplayModes& modes, Fi return sortedModes; } -bool canModesSupportFrameRateOverride(const std::vector<DisplayModeIterator>& sortedModes) { +bool shouldEnableFrameRateOverride(const std::vector<DisplayModeIterator>& sortedModes) { for (const auto it1 : sortedModes) { const auto& mode1 = it1->second; for (const auto it2 : sortedModes) { const auto& mode2 = it2->second; - if (RefreshRateConfigs::getFrameRateDivisor(mode1->getFps(), mode2->getFps()) >= 2) { + if (RefreshRateSelector::getFrameRateDivisor(mode1->getFps(), mode2->getFps()) >= 2) { return true; } } @@ -119,23 +120,23 @@ bool canModesSupportFrameRateOverride(const std::vector<DisplayModeIterator>& so return false; } -std::string toString(const RefreshRateConfigs::PolicyVariant& policy) { +std::string toString(const RefreshRateSelector::PolicyVariant& policy) { using namespace std::string_literals; return ftl::match( policy, - [](const RefreshRateConfigs::DisplayManagerPolicy& policy) { + [](const RefreshRateSelector::DisplayManagerPolicy& policy) { return "DisplayManagerPolicy"s + policy.toString(); }, - [](const RefreshRateConfigs::OverridePolicy& policy) { + [](const RefreshRateSelector::OverridePolicy& policy) { return "OverridePolicy"s + policy.toString(); }, - [](RefreshRateConfigs::NoOverridePolicy) { return "NoOverridePolicy"s; }); + [](RefreshRateSelector::NoOverridePolicy) { return "NoOverridePolicy"s; }); } } // namespace -struct RefreshRateConfigs::RefreshRateScoreComparator { +struct RefreshRateSelector::RefreshRateScoreComparator { bool operator()(const RefreshRateScore& lhs, const RefreshRateScore& rhs) const { const auto& [modeIt, overallScore, _] = lhs; @@ -162,15 +163,16 @@ struct RefreshRateConfigs::RefreshRateScoreComparator { const RefreshRateOrder refreshRateOrder; }; -std::string RefreshRateConfigs::Policy::toString() const { +std::string RefreshRateSelector::Policy::toString() const { return base::StringPrintf("{defaultModeId=%d, allowGroupSwitching=%s" - ", primaryRange=%s, appRequestRange=%s}", + ", primaryRanges=%s, appRequestRanges=%s}", defaultMode.value(), allowGroupSwitching ? "true" : "false", - to_string(primaryRange).c_str(), to_string(appRequestRange).c_str()); + to_string(primaryRanges).c_str(), + to_string(appRequestRanges).c_str()); } -std::pair<nsecs_t, nsecs_t> RefreshRateConfigs::getDisplayFrames(nsecs_t layerPeriod, - nsecs_t displayPeriod) const { +std::pair<nsecs_t, nsecs_t> RefreshRateSelector::getDisplayFrames(nsecs_t layerPeriod, + nsecs_t displayPeriod) const { auto [quotient, remainder] = std::div(layerPeriod, displayPeriod); if (remainder <= MARGIN_FOR_PERIOD_CALCULATION || std::abs(remainder - displayPeriod) <= MARGIN_FOR_PERIOD_CALCULATION) { @@ -181,8 +183,8 @@ std::pair<nsecs_t, nsecs_t> RefreshRateConfigs::getDisplayFrames(nsecs_t layerPe return {quotient, remainder}; } -float RefreshRateConfigs::calculateNonExactMatchingLayerScoreLocked(const LayerRequirement& layer, - Fps refreshRate) const { +float RefreshRateSelector::calculateNonExactMatchingLayerScoreLocked(const LayerRequirement& layer, + Fps refreshRate) const { constexpr float kScoreForFractionalPairs = .8f; const auto displayPeriod = refreshRate.getPeriodNsecs(); @@ -243,15 +245,15 @@ float RefreshRateConfigs::calculateNonExactMatchingLayerScoreLocked(const LayerR return 0; } -float RefreshRateConfigs::calculateRefreshRateScoreForFps(Fps refreshRate) const { +float RefreshRateSelector::calculateRefreshRateScoreForFps(Fps refreshRate) const { const float ratio = refreshRate.getValue() / mAppRequestRefreshRates.back()->second->getFps().getValue(); // Use ratio^2 to get a lower score the more we get further from peak return ratio * ratio; } -float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& layer, Fps refreshRate, - bool isSeamlessSwitch) const { +float RefreshRateSelector::calculateLayerScoreLocked(const LayerRequirement& layer, Fps refreshRate, + bool isSeamlessSwitch) const { // Slightly prefer seamless switches. constexpr float kSeamedSwitchPenalty = 0.95f; const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty; @@ -263,7 +265,7 @@ float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& laye if (layer.vote == LayerVoteType::ExplicitExact) { const int divisor = getFrameRateDivisor(refreshRate, layer.desiredRefreshRate); - if (mSupportsFrameRateOverrideByContent) { + if (supportsFrameRateOverrideByContent()) { // 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. @@ -287,8 +289,8 @@ float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& laye kNonExactMatchingPenalty; } -auto RefreshRateConfigs::getRankedRefreshRates(const std::vector<LayerRequirement>& layers, - GlobalSignals signals) const -> RankedRefreshRates { +auto RefreshRateSelector::getRankedRefreshRates(const std::vector<LayerRequirement>& layers, + GlobalSignals signals) const -> RankedRefreshRates { std::lock_guard lock(mLock); if (mGetRankedRefreshRatesCache && @@ -301,8 +303,8 @@ auto RefreshRateConfigs::getRankedRefreshRates(const std::vector<LayerRequiremen return result; } -auto RefreshRateConfigs::getRankedRefreshRatesLocked(const std::vector<LayerRequirement>& layers, - GlobalSignals signals) const +auto RefreshRateSelector::getRankedRefreshRatesLocked(const std::vector<LayerRequirement>& layers, + GlobalSignals signals) const -> RankedRefreshRates { using namespace fps_approx_ops; ATRACE_CALL(); @@ -381,7 +383,7 @@ auto RefreshRateConfigs::getRankedRefreshRatesLocked(const std::vector<LayerRequ // move out the of range if layers explicitly request a different refresh // rate. const bool primaryRangeIsSingleRate = - isApproxEqual(policy->primaryRange.min, policy->primaryRange.max); + isApproxEqual(policy->primaryRanges.physical.min, policy->primaryRanges.physical.max); if (!signals.touch && signals.idle && !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) { ALOGV("Idle"); @@ -450,7 +452,7 @@ auto RefreshRateConfigs::getRankedRefreshRatesLocked(const std::vector<LayerRequ continue; } - const bool inPrimaryRange = policy->primaryRange.includes(mode->getFps()); + const bool inPrimaryRange = policy->primaryRanges.physical.includes(mode->getFps()); if ((primaryRangeIsSingleRate || !inPrimaryRange) && !(layer.focused && (layer.vote == LayerVoteType::ExplicitDefault || @@ -578,7 +580,7 @@ auto RefreshRateConfigs::getRankedRefreshRatesLocked(const std::vector<LayerRequ // vote we should not change it if we get a touch event. Only apply touch boost if it will // actually increase the refresh rate over the normal selection. const bool touchBoostForExplicitExact = [&] { - if (mSupportsFrameRateOverrideByContent) { + if (supportsFrameRateOverrideByContent()) { // Enable touch boost if there are other layers besides exact return explicitExact + noVoteLayers != layers.size(); } else { @@ -608,61 +610,84 @@ auto RefreshRateConfigs::getRankedRefreshRatesLocked(const std::vector<LayerRequ return {ranking, kNoSignals}; } -std::unordered_map<uid_t, std::vector<const RefreshRateConfigs::LayerRequirement*>> -groupLayersByUid(const std::vector<RefreshRateConfigs::LayerRequirement>& layers) { - std::unordered_map<uid_t, std::vector<const RefreshRateConfigs::LayerRequirement*>> layersByUid; +using LayerRequirementPtrs = std::vector<const RefreshRateSelector::LayerRequirement*>; +using PerUidLayerRequirements = std::unordered_map<uid_t, LayerRequirementPtrs>; + +PerUidLayerRequirements groupLayersByUid( + const std::vector<RefreshRateSelector::LayerRequirement>& layers) { + PerUidLayerRequirements layersByUid; for (const auto& layer : layers) { - auto iter = layersByUid.emplace(layer.ownerUid, - std::vector<const RefreshRateConfigs::LayerRequirement*>()); - auto& layersWithSameUid = iter.first->second; + const auto it = layersByUid.emplace(layer.ownerUid, LayerRequirementPtrs()).first; + auto& layersWithSameUid = it->second; layersWithSameUid.push_back(&layer); } // Remove uids that can't have a frame rate override - for (auto iter = layersByUid.begin(); iter != layersByUid.end();) { - const auto& layersWithSameUid = iter->second; + for (auto it = layersByUid.begin(); it != layersByUid.end();) { + const auto& layersWithSameUid = it->second; bool skipUid = false; for (const auto& layer : layersWithSameUid) { - if (layer->vote == RefreshRateConfigs::LayerVoteType::Max || - layer->vote == RefreshRateConfigs::LayerVoteType::Heuristic) { + using LayerVoteType = RefreshRateSelector::LayerVoteType; + + if (layer->vote == LayerVoteType::Max || layer->vote == LayerVoteType::Heuristic) { skipUid = true; break; } } if (skipUid) { - iter = layersByUid.erase(iter); + it = layersByUid.erase(it); } else { - ++iter; + ++it; } } return layersByUid; } -RefreshRateConfigs::UidToFrameRateOverride RefreshRateConfigs::getFrameRateOverrides( - const std::vector<LayerRequirement>& layers, Fps displayRefreshRate, - GlobalSignals globalSignals) const { +auto RefreshRateSelector::getFrameRateOverrides(const std::vector<LayerRequirement>& layers, + Fps displayRefreshRate, + GlobalSignals globalSignals) const + -> UidToFrameRateOverride { ATRACE_CALL(); - ALOGV("%s: %zu layers", __func__, layers.size()); std::lock_guard lock(mLock); - std::vector<RefreshRateScore> scores; - scores.reserve(mDisplayModes.size()); - - for (auto it = mDisplayModes.begin(); it != mDisplayModes.end(); ++it) { - scores.emplace_back(RefreshRateScore{it, 0.0f}); + // Prepare a set of supported display refresh rates for easy lookup + constexpr size_t kStaticCapacity = 8; + ftl::SmallMap<Fps, ftl::Unit, kStaticCapacity, FpsApproxEqual> supportedDisplayRefreshRates; + if (mConfig.enableFrameRateOverride == + Config::FrameRateOverride::EnabledForNativeRefreshRates) { + for (const auto& [_, modePtr] : mDisplayModes) { + supportedDisplayRefreshRates.try_emplace(modePtr->getFps(), ftl::unit); + } } - std::sort(scores.begin(), scores.end(), [](const auto& lhs, const auto& rhs) { - const auto& mode1 = lhs.modeIt->second; - const auto& mode2 = rhs.modeIt->second; - return isStrictlyLess(mode1->getFps(), mode2->getFps()); - }); + const auto* policyPtr = getCurrentPolicyLocked(); + // We don't want to run lower than 30fps + const Fps minFrameRate = std::max(policyPtr->appRequestRanges.render.min, 30_Hz, isApproxLess); + + using fps_approx_ops::operator/; + const unsigned numMultiples = displayRefreshRate / minFrameRate; + + std::vector<std::pair<Fps, float>> scoredFrameRates; + scoredFrameRates.reserve(numMultiples); - std::unordered_map<uid_t, std::vector<const LayerRequirement*>> layersByUid = - groupLayersByUid(layers); + for (unsigned n = numMultiples; n > 0; n--) { + const Fps divisor = displayRefreshRate / n; + if (mConfig.enableFrameRateOverride == + Config::FrameRateOverride::EnabledForNativeRefreshRates && + !supportedDisplayRefreshRates.contains(divisor)) { + continue; + } + + if (policyPtr->appRequestRanges.render.includes(divisor)) { + ALOGV("%s: adding %s as a potential frame rate", __func__, to_string(divisor).c_str()); + scoredFrameRates.emplace_back(divisor, 0); + } + } + + const auto layersByUid = groupLayersByUid(layers); UidToFrameRateOverride frameRateOverrides; for (const auto& [uid, layersWithSameUid] : layersByUid) { // Layers with ExplicitExactOrMultiple expect touch boost @@ -676,7 +701,7 @@ RefreshRateConfigs::UidToFrameRateOverride RefreshRateConfigs::getFrameRateOverr continue; } - for (auto& [_, score, _1] : scores) { + for (auto& [_, score] : scoredFrameRates) { score = 0; } @@ -688,42 +713,39 @@ RefreshRateConfigs::UidToFrameRateOverride RefreshRateConfigs::getFrameRateOverr LOG_ALWAYS_FATAL_IF(layer->vote != LayerVoteType::ExplicitDefault && layer->vote != LayerVoteType::ExplicitExactOrMultiple && layer->vote != LayerVoteType::ExplicitExact); - for (auto& [modeIt, score, _] : scores) { + for (auto& [fps, score] : scoredFrameRates) { constexpr bool isSeamlessSwitch = true; - const auto layerScore = calculateLayerScoreLocked(*layer, modeIt->second->getFps(), - isSeamlessSwitch); + const auto layerScore = calculateLayerScoreLocked(*layer, fps, isSeamlessSwitch); score += layer->weight * layerScore; } } - // We just care about the refresh rates which are a divisor of the - // display refresh rate - const auto it = std::remove_if(scores.begin(), scores.end(), [&](RefreshRateScore score) { - const auto& [id, mode] = *score.modeIt; - return getFrameRateDivisor(displayRefreshRate, mode->getFps()) == 0; - }); - scores.erase(it, scores.end()); - // If we never scored any layers, we don't have a preferred frame rate - if (std::all_of(scores.begin(), scores.end(), - [](RefreshRateScore score) { return score.overallScore == 0; })) { + if (std::all_of(scoredFrameRates.begin(), scoredFrameRates.end(), + [](const auto& scoredFrameRate) { + const auto [_, score] = scoredFrameRate; + return score == 0; + })) { continue; } // Now that we scored all the refresh rates we need to pick the lowest refresh rate // that got the highest score. - const DisplayModePtr& bestRefreshRate = - std::min_element(scores.begin(), scores.end(), - RefreshRateScoreComparator{.refreshRateOrder = - RefreshRateOrder::Ascending}) - ->modeIt->second; - frameRateOverrides.emplace(uid, bestRefreshRate->getFps()); + const auto [overrideFps, _] = + *std::max_element(scoredFrameRates.begin(), scoredFrameRates.end(), + [](const auto& lhsPair, const auto& rhsPair) { + const float lhs = lhsPair.second; + const float rhs = rhsPair.second; + return lhs < rhs && !ScoredRefreshRate::scoresEqual(lhs, rhs); + }); + ALOGV("%s: overriding to %s for uid=%d", __func__, to_string(overrideFps).c_str(), uid); + frameRateOverrides.emplace(uid, overrideFps); } return frameRateOverrides; } -std::optional<Fps> RefreshRateConfigs::onKernelTimerChanged( +std::optional<Fps> RefreshRateSelector::onKernelTimerChanged( std::optional<DisplayModeId> desiredActiveModeId, bool timerExpired) const { std::lock_guard lock(mLock); @@ -740,7 +762,7 @@ std::optional<Fps> RefreshRateConfigs::onKernelTimerChanged( return mode->getFps(); } -const DisplayModePtr& RefreshRateConfigs::getMinRefreshRateByPolicyLocked() const { +const DisplayModePtr& RefreshRateSelector::getMinRefreshRateByPolicyLocked() const { const auto& activeMode = *getActiveModeItLocked()->second; for (const DisplayModeIterator modeIt : mPrimaryRefreshRates) { @@ -757,7 +779,7 @@ const DisplayModePtr& RefreshRateConfigs::getMinRefreshRateByPolicyLocked() cons return mPrimaryRefreshRates.front()->second; } -const DisplayModePtr& RefreshRateConfigs::getMaxRefreshRateByPolicyLocked(int anchorGroup) const { +const DisplayModePtr& RefreshRateSelector::getMaxRefreshRateByPolicyLocked(int anchorGroup) const { for (auto it = mPrimaryRefreshRates.rbegin(); it != mPrimaryRefreshRates.rend(); ++it) { const auto& mode = (*it)->second; if (anchorGroup == mode->getGroup()) { @@ -771,7 +793,7 @@ const DisplayModePtr& RefreshRateConfigs::getMaxRefreshRateByPolicyLocked(int an return mPrimaryRefreshRates.back()->second; } -auto RefreshRateConfigs::rankRefreshRates( +auto RefreshRateSelector::rankRefreshRates( std::optional<int> anchorGroupOpt, RefreshRateOrder refreshRateOrder, std::optional<DisplayModeId> preferredDisplayModeOpt) const -> RefreshRateRanking { std::deque<ScoredRefreshRate> ranking; @@ -817,23 +839,23 @@ auto RefreshRateConfigs::rankRefreshRates( return rankRefreshRates(kNoAnchorGroup, refreshRateOrder, preferredDisplayModeOpt); } -DisplayModePtr RefreshRateConfigs::getActiveModePtr() const { +DisplayModePtr RefreshRateSelector::getActiveModePtr() const { std::lock_guard lock(mLock); return getActiveModeItLocked()->second; } -const DisplayMode& RefreshRateConfigs::getActiveMode() const { +const DisplayMode& RefreshRateSelector::getActiveMode() const { // Reads from kMainThreadContext do not require mLock. ftl::FakeGuard guard(mLock); return *mActiveModeIt->second; } -DisplayModeIterator RefreshRateConfigs::getActiveModeItLocked() const { +DisplayModeIterator RefreshRateSelector::getActiveModeItLocked() const { // Reads under mLock do not require kMainThreadContext. return FTL_FAKE_GUARD(kMainThreadContext, mActiveModeIt); } -void RefreshRateConfigs::setActiveModeId(DisplayModeId modeId) { +void RefreshRateSelector::setActiveModeId(DisplayModeId modeId) { std::lock_guard lock(mLock); // Invalidate the cached invocation to getRankedRefreshRates. This forces @@ -844,14 +866,14 @@ void RefreshRateConfigs::setActiveModeId(DisplayModeId modeId) { LOG_ALWAYS_FATAL_IF(mActiveModeIt == mDisplayModes.end()); } -RefreshRateConfigs::RefreshRateConfigs(DisplayModes modes, DisplayModeId activeModeId, - Config config) +RefreshRateSelector::RefreshRateSelector(DisplayModes modes, DisplayModeId activeModeId, + Config config) : mKnownFrameRates(constructKnownFrameRates(modes)), mConfig(config) { initializeIdleTimer(); FTL_FAKE_GUARD(kMainThreadContext, updateDisplayModes(std::move(modes), activeModeId)); } -void RefreshRateConfigs::initializeIdleTimer() { +void RefreshRateSelector::initializeIdleTimer() { if (mConfig.idleTimerTimeout > 0ms) { mIdleTimer.emplace( "IdleTimer", mConfig.idleTimerTimeout, @@ -870,7 +892,7 @@ void RefreshRateConfigs::initializeIdleTimer() { } } -void RefreshRateConfigs::updateDisplayModes(DisplayModes modes, DisplayModeId activeModeId) { +void RefreshRateSelector::updateDisplayModes(DisplayModes modes, DisplayModeId activeModeId) { std::lock_guard lock(mLock); // Invalidate the cached invocation to getRankedRefreshRates. This forces @@ -890,16 +912,25 @@ void RefreshRateConfigs::updateDisplayModes(DisplayModes modes, DisplayModeId ac mDisplayManagerPolicy = {}; mDisplayManagerPolicy.defaultMode = activeModeId; - mSupportsFrameRateOverrideByContent = - mConfig.enableFrameRateOverride && canModesSupportFrameRateOverride(sortedModes); + mFrameRateOverrideConfig = [&] { + switch (mConfig.enableFrameRateOverride) { + case Config::FrameRateOverride::Disabled: + case Config::FrameRateOverride::Enabled: + return mConfig.enableFrameRateOverride; + case Config::FrameRateOverride::EnabledForNativeRefreshRates: + return shouldEnableFrameRateOverride(sortedModes) + ? Config::FrameRateOverride::EnabledForNativeRefreshRates + : Config::FrameRateOverride::Disabled; + } + }(); constructAvailableRefreshRates(); } -bool RefreshRateConfigs::isPolicyValidLocked(const Policy& policy) const { +bool RefreshRateSelector::isPolicyValidLocked(const Policy& policy) const { // defaultMode must be a valid mode, and within the given refresh rate range. if (const auto mode = mDisplayModes.get(policy.defaultMode)) { - if (!policy.primaryRange.includes(mode->get()->getFps())) { + if (!policy.primaryRanges.physical.includes(mode->get()->getFps())) { ALOGE("Default mode is not in the primary range."); return false; } @@ -909,11 +940,11 @@ bool RefreshRateConfigs::isPolicyValidLocked(const Policy& policy) const { } using namespace fps_approx_ops; - return policy.appRequestRange.min <= policy.primaryRange.min && - policy.appRequestRange.max >= policy.primaryRange.max; + return policy.appRequestRanges.physical.min <= policy.primaryRanges.physical.min && + policy.appRequestRanges.physical.max >= policy.primaryRanges.physical.max; } -auto RefreshRateConfigs::setPolicy(const PolicyVariant& policy) -> SetPolicyResult { +auto RefreshRateSelector::setPolicy(const PolicyVariant& policy) -> SetPolicyResult { Policy oldPolicy; { std::lock_guard lock(mLock); @@ -969,21 +1000,21 @@ auto RefreshRateConfigs::setPolicy(const PolicyVariant& policy) -> SetPolicyResu return SetPolicyResult::Changed; } -const RefreshRateConfigs::Policy* RefreshRateConfigs::getCurrentPolicyLocked() const { +auto RefreshRateSelector::getCurrentPolicyLocked() const -> const Policy* { return mOverridePolicy ? &mOverridePolicy.value() : &mDisplayManagerPolicy; } -RefreshRateConfigs::Policy RefreshRateConfigs::getCurrentPolicy() const { +auto RefreshRateSelector::getCurrentPolicy() const -> Policy { std::lock_guard lock(mLock); return *getCurrentPolicyLocked(); } -RefreshRateConfigs::Policy RefreshRateConfigs::getDisplayManagerPolicy() const { +auto RefreshRateSelector::getDisplayManagerPolicy() const -> Policy { std::lock_guard lock(mLock); return mDisplayManagerPolicy; } -bool RefreshRateConfigs::isModeAllowed(DisplayModeId modeId) const { +bool RefreshRateSelector::isModeAllowed(DisplayModeId modeId) const { std::lock_guard lock(mLock); return std::any_of(mAppRequestRefreshRates.begin(), mAppRequestRefreshRates.end(), [modeId](DisplayModeIterator modeIt) { @@ -991,7 +1022,7 @@ bool RefreshRateConfigs::isModeAllowed(DisplayModeId modeId) const { }); } -void RefreshRateConfigs::constructAvailableRefreshRates() { +void RefreshRateSelector::constructAvailableRefreshRates() { // Filter modes based on current policy and sort on refresh rate. const Policy* policy = getCurrentPolicyLocked(); ALOGV("%s: %s ", __func__, policy->toString().c_str()); @@ -1023,11 +1054,11 @@ void RefreshRateConfigs::constructAvailableRefreshRates() { return modes; }; - mPrimaryRefreshRates = filterRefreshRates(policy->primaryRange, "primary"); - mAppRequestRefreshRates = filterRefreshRates(policy->appRequestRange, "app request"); + mPrimaryRefreshRates = filterRefreshRates(policy->primaryRanges.physical, "primary"); + mAppRequestRefreshRates = filterRefreshRates(policy->appRequestRanges.physical, "app request"); } -Fps RefreshRateConfigs::findClosestKnownFrameRate(Fps frameRate) const { +Fps RefreshRateSelector::findClosestKnownFrameRate(Fps frameRate) const { using namespace fps_approx_ops; if (frameRate <= mKnownFrameRates.front()) { @@ -1046,7 +1077,7 @@ Fps RefreshRateConfigs::findClosestKnownFrameRate(Fps frameRate) const { return distance1 < distance2 ? *lowerBound : *std::prev(lowerBound); } -RefreshRateConfigs::KernelIdleTimerAction RefreshRateConfigs::getIdleTimerAction() const { +auto RefreshRateSelector::getIdleTimerAction() const -> KernelIdleTimerAction { std::lock_guard lock(mLock); const Fps deviceMinFps = mMinRefreshRateModeIt->second->getFps(); @@ -1064,7 +1095,7 @@ RefreshRateConfigs::KernelIdleTimerAction RefreshRateConfigs::getIdleTimerAction if (minByPolicy == maxByPolicy) { // Turn on the timer when the min of the primary range is below the device min. if (const Policy* currentPolicy = getCurrentPolicyLocked(); - isApproxLess(currentPolicy->primaryRange.min, deviceMinFps)) { + isApproxLess(currentPolicy->primaryRanges.physical.min, deviceMinFps)) { return KernelIdleTimerAction::TurnOn; } return KernelIdleTimerAction::TurnOff; @@ -1074,7 +1105,7 @@ RefreshRateConfigs::KernelIdleTimerAction RefreshRateConfigs::getIdleTimerAction return KernelIdleTimerAction::TurnOn; } -int RefreshRateConfigs::getFrameRateDivisor(Fps displayRefreshRate, Fps layerFrameRate) { +int RefreshRateSelector::getFrameRateDivisor(Fps displayRefreshRate, Fps layerFrameRate) { // This calculation needs to be in sync with the java code // in DisplayManagerService.getDisplayInfoForFrameRateOverride @@ -1090,7 +1121,7 @@ int RefreshRateConfigs::getFrameRateDivisor(Fps displayRefreshRate, Fps layerFra return static_cast<int>(numPeriodsRounded); } -bool RefreshRateConfigs::isFractionalPairOrMultiple(Fps smaller, Fps bigger) { +bool RefreshRateSelector::isFractionalPairOrMultiple(Fps smaller, Fps bigger) { if (isStrictlyLess(bigger, smaller)) { return isFractionalPairOrMultiple(bigger, smaller); } @@ -1101,7 +1132,7 @@ bool RefreshRateConfigs::isFractionalPairOrMultiple(Fps smaller, Fps bigger) { isApproxEqual(bigger, Fps::fromValue(smaller.getValue() * multiplier * kCoef)); } -void RefreshRateConfigs::dump(utils::Dumper& dumper) const { +void RefreshRateSelector::dump(utils::Dumper& dumper) const { using namespace std::string_view_literals; std::lock_guard lock(mLock); @@ -1124,7 +1155,7 @@ void RefreshRateConfigs::dump(utils::Dumper& dumper) const { dumper.dump("overridePolicy"sv, currentPolicy.toString()); } - dumper.dump("supportsFrameRateOverrideByContent"sv, mSupportsFrameRateOverrideByContent); + dumper.dump("frameRateOverrideConfig"sv, *ftl::enum_name(mFrameRateOverrideConfig)); std::string idleTimer; if (mIdleTimer) { @@ -1142,7 +1173,7 @@ void RefreshRateConfigs::dump(utils::Dumper& dumper) const { dumper.dump("idleTimer"sv, idleTimer); } -std::chrono::milliseconds RefreshRateConfigs::getIdleTimerTimeout() { +std::chrono::milliseconds RefreshRateSelector::getIdleTimerTimeout() { return mConfig.idleTimerTimeout; } diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h index 99f81aa75e..abbd30465a 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h @@ -49,12 +49,10 @@ inline DisplayModeEvent operator|(DisplayModeEvent lhs, DisplayModeEvent rhs) { using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride; -/** - * This class is used to encapsulate configuration for refresh rates. It holds information - * about available refresh rates on the device, and the mapping between the numbers and human - * readable names. - */ -class RefreshRateConfigs { +// Selects the refresh rate of a display by ranking its `DisplayModes` in accordance with +// the DisplayManager (or override) `Policy`, the `LayerRequirement` of each active layer, +// and `GlobalSignals`. +class RefreshRateSelector { public: // Margin used when matching refresh rates to the content desired ones. static constexpr nsecs_t MARGIN_FOR_PERIOD_CALCULATION = @@ -69,40 +67,31 @@ public: DisplayModeId defaultMode; // Whether or not we switch mode groups to get the best frame rate. bool allowGroupSwitching = kAllowGroupSwitchingDefault; - // The primary refresh rate range represents display manager's general guidance on the - // display modes we'll consider when switching refresh rates. Unless we get an explicit - // signal from an app, we should stay within this range. - FpsRange primaryRange; - // The app request refresh rate range allows us to consider more display modes when - // switching refresh rates. Although we should generally stay within the primary range, - // specific considerations, such as layer frame rate settings specified via the - // setFrameRate() api, may cause us to go outside the primary range. We never go outside the - // app request range. The app request range will be greater than or equal to the primary - // refresh rate range, never smaller. - FpsRange appRequestRange; + // The primary refresh rate ranges. @see DisplayModeSpecs.aidl for details. + // TODO(b/257072060): use the render range when selecting SF render rate + // or the app override frame rate + FpsRanges primaryRanges; + // The app request refresh rate ranges. @see DisplayModeSpecs.aidl for details. + FpsRanges appRequestRanges; Policy() = default; - Policy(DisplayModeId defaultMode, FpsRange range) - : Policy(defaultMode, kAllowGroupSwitchingDefault, range, range) {} - - Policy(DisplayModeId defaultMode, bool allowGroupSwitching, FpsRange range) - : Policy(defaultMode, allowGroupSwitching, range, range) {} + Policy(DisplayModeId defaultMode, FpsRange range, + bool allowGroupSwitching = kAllowGroupSwitchingDefault) + : Policy(defaultMode, FpsRanges{range, range}, FpsRanges{range, range}, + allowGroupSwitching) {} - Policy(DisplayModeId defaultMode, FpsRange primaryRange, FpsRange appRequestRange) - : Policy(defaultMode, kAllowGroupSwitchingDefault, primaryRange, appRequestRange) {} - - Policy(DisplayModeId defaultMode, bool allowGroupSwitching, FpsRange primaryRange, - FpsRange appRequestRange) + Policy(DisplayModeId defaultMode, FpsRanges primaryRanges, FpsRanges appRequestRanges, + bool allowGroupSwitching = kAllowGroupSwitchingDefault) : defaultMode(defaultMode), allowGroupSwitching(allowGroupSwitching), - primaryRange(primaryRange), - appRequestRange(appRequestRange) {} + primaryRanges(primaryRanges), + appRequestRanges(appRequestRanges) {} bool operator==(const Policy& other) const { using namespace fps_approx_ops; - return defaultMode == other.defaultMode && primaryRange == other.primaryRange && - appRequestRange == other.appRequestRange && + return defaultMode == other.defaultMode && primaryRanges == other.primaryRanges && + appRequestRanges == other.appRequestRanges && allowGroupSwitching == other.allowGroupSwitching; } @@ -262,7 +251,20 @@ public: // Configuration flags. struct Config { - bool enableFrameRateOverride = false; + enum class FrameRateOverride { + // Do not override the frame rate for an app + Disabled, + + // Override the frame rate for an app to a value which is also + // a display refresh rate + EnabledForNativeRefreshRates, + + // Override the frame rate for an app to any value + Enabled, + + ftl_last = Enabled + }; + FrameRateOverride enableFrameRateOverride = FrameRateOverride::Disabled; // Specifies the upper refresh rate threshold (inclusive) for layer vote types of multiple // or heuristic, such that refresh rates higher than this value will not be voted for. 0 if @@ -277,14 +279,15 @@ public: std::optional<KernelIdleTimerController> kernelIdleTimerController; }; - RefreshRateConfigs(DisplayModes, DisplayModeId activeModeId, - Config config = {.enableFrameRateOverride = false, - .frameRateMultipleThreshold = 0, - .idleTimerTimeout = 0ms, - .kernelIdleTimerController = {}}); + RefreshRateSelector( + DisplayModes, DisplayModeId activeModeId, + Config config = {.enableFrameRateOverride = Config::FrameRateOverride::Disabled, + .frameRateMultipleThreshold = 0, + .idleTimerTimeout = 0ms, + .kernelIdleTimerController = {}}); - RefreshRateConfigs(const RefreshRateConfigs&) = delete; - RefreshRateConfigs& operator=(const RefreshRateConfigs&) = delete; + RefreshRateSelector(const RefreshRateSelector&) = delete; + RefreshRateSelector& operator=(const RefreshRateSelector&) = delete; // Returns whether switching modes (refresh rate or resolution) is possible. // TODO(b/158780872): Consider HAL support, and skip frame rate detection if the modes only @@ -296,15 +299,17 @@ public: // Class to enumerate options around toggling the kernel timer on and off. enum class KernelIdleTimerAction { - TurnOff, // Turn off the idle timer. - TurnOn // Turn on the idle timer. + TurnOff, // Turn off the idle timer. + TurnOn // Turn on the idle timer. }; // Checks whether kernel idle timer should be active depending the policy decisions around // refresh rates. KernelIdleTimerAction getIdleTimerAction() const; - bool supportsFrameRateOverrideByContent() const { return mSupportsFrameRateOverrideByContent; } + bool supportsFrameRateOverrideByContent() const { + return mFrameRateOverrideConfig != Config::FrameRateOverride::Disabled; + } // Return the display refresh rate divisor to match the layer // frame rate, or 0 if the display refresh rate is not a multiple of the @@ -373,7 +378,7 @@ public: std::chrono::milliseconds getIdleTimerTimeout(); private: - friend struct TestableRefreshRateConfigs; + friend struct TestableRefreshRateSelector; void constructAvailableRefreshRates() REQUIRES(mLock); @@ -457,7 +462,7 @@ private: const std::vector<Fps> mKnownFrameRates; const Config mConfig; - bool mSupportsFrameRateOverrideByContent; + Config::FrameRateOverride mFrameRateOverrideConfig; struct GetRankedRefreshRatesCache { std::pair<std::vector<LayerRequirement>, GlobalSignals> arguments; diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index be3ebb7947..0e1b775a8c 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -68,8 +68,8 @@ Scheduler::~Scheduler() { mDisplayPowerTimer.reset(); mTouchTimer.reset(); - // Stop idle timer and clear callbacks, as the RefreshRateConfigs may outlive the Scheduler. - setRefreshRateConfigs(nullptr); + // Stop idle timer and clear callbacks, as the RefreshRateSelector may outlive the Scheduler. + setRefreshRateSelector(nullptr); } void Scheduler::startTimers() { @@ -94,17 +94,17 @@ void Scheduler::startTimers() { } } -void Scheduler::setRefreshRateConfigs(std::shared_ptr<RefreshRateConfigs> configs) { - // The current RefreshRateConfigs instance may outlive this call, so unbind its idle timer. +void Scheduler::setRefreshRateSelector(RefreshRateSelectorPtr selectorPtr) { + // The current RefreshRateSelector instance may outlive this call, so unbind its idle timer. { - // mRefreshRateConfigsLock is not locked here to avoid the deadlock + // mRefreshRateSelectorLock is not locked here to avoid the deadlock // as the callback can attempt to acquire the lock before stopIdleTimer can finish // the execution. It's safe to FakeGuard as main thread is the only thread that - // writes to the mRefreshRateConfigs. - ftl::FakeGuard guard(mRefreshRateConfigsLock); - if (mRefreshRateConfigs) { - mRefreshRateConfigs->stopIdleTimer(); - mRefreshRateConfigs->clearIdleTimerCallbacks(); + // writes to the mRefreshRateSelector. + ftl::FakeGuard guard(mRefreshRateSelectorLock); + if (mRefreshRateSelector) { + mRefreshRateSelector->stopIdleTimer(); + mRefreshRateSelector->clearIdleTimerCallbacks(); } } { @@ -113,26 +113,25 @@ void Scheduler::setRefreshRateConfigs(std::shared_ptr<RefreshRateConfigs> config mPolicy = {}; } - std::scoped_lock lock(mRefreshRateConfigsLock); - mRefreshRateConfigs = std::move(configs); - if (!mRefreshRateConfigs) return; + std::scoped_lock lock(mRefreshRateSelectorLock); + mRefreshRateSelector = std::move(selectorPtr); + if (!mRefreshRateSelector) return; - mRefreshRateConfigs->setIdleTimerCallbacks( + mRefreshRateSelector->setIdleTimerCallbacks( {.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); }, .onExpired = [this] { idleTimerCallback(TimerState::Expired); }}, .kernel = {.onReset = [this] { kernelIdleTimerCallback(TimerState::Reset); }, .onExpired = [this] { kernelIdleTimerCallback(TimerState::Expired); }}}); - mRefreshRateConfigs->startIdleTimer(); + mRefreshRateSelector->startIdleTimer(); } -void Scheduler::registerDisplay(sp<const DisplayDevice> display) { - if (display->isPrimary()) { - mLeaderDisplayId = display->getPhysicalId(); +void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) { + if (!mLeaderDisplayId) { + mLeaderDisplayId = displayId; } - const bool ok = mDisplays.try_emplace(display->getPhysicalId(), std::move(display)).second; - ALOGE_IF(!ok, "%s: Duplicate display", __func__); + mRefreshRateSelectors.emplace_or_replace(displayId, std::move(selectorPtr)); } void Scheduler::unregisterDisplay(PhysicalDisplayId displayId) { @@ -140,7 +139,7 @@ void Scheduler::unregisterDisplay(PhysicalDisplayId displayId) { mLeaderDisplayId.reset(); } - mDisplays.erase(displayId); + mRefreshRateSelectors.erase(displayId); } void Scheduler::run() { @@ -174,9 +173,8 @@ std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource( } std::optional<Fps> Scheduler::getFrameRateOverride(uid_t uid) const { - const auto refreshRateConfigs = holdRefreshRateConfigs(); const bool supportsFrameRateOverrideByContent = - refreshRateConfigs->supportsFrameRateOverrideByContent(); + holdRefreshRateSelector()->supportsFrameRateOverrideByContent(); return mFrameRateOverrideMappings .getFrameRateOverrideForUid(uid, supportsFrameRateOverrideByContent); } @@ -191,7 +189,7 @@ bool Scheduler::isVsyncValid(TimePoint expectedVsyncTimestamp, uid_t uid) const } impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() const { - std::scoped_lock lock(mRefreshRateConfigsLock); + std::scoped_lock lock(mRefreshRateSelectorLock); return [this](nsecs_t expectedVsyncTimestamp, uid_t uid) { return !isVsyncValid(TimePoint::fromNs(expectedVsyncTimestamp), uid); @@ -200,7 +198,7 @@ impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() impl::EventThread::GetVsyncPeriodFunction Scheduler::makeGetVsyncPeriodFunction() const { return [this](uid_t uid) { - const Fps refreshRate = holdRefreshRateConfigs()->getActiveModePtr()->getFps(); + const Fps refreshRate = holdRefreshRateSelector()->getActiveModePtr()->getFps(); const nsecs_t currentPeriod = mVsyncSchedule->period().ns() ?: refreshRate.getPeriodNsecs(); const auto frameRate = getFrameRateOverride(uid); @@ -208,7 +206,7 @@ impl::EventThread::GetVsyncPeriodFunction Scheduler::makeGetVsyncPeriodFunction( return currentPeriod; } - const auto divisor = RefreshRateConfigs::getFrameRateDivisor(refreshRate, *frameRate); + const auto divisor = RefreshRateSelector::getFrameRateDivisor(refreshRate, *frameRate); if (divisor <= 1) { return currentPeriod; } @@ -293,9 +291,8 @@ void Scheduler::onScreenReleased(ConnectionHandle handle) { } void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId) { - const auto refreshRateConfigs = holdRefreshRateConfigs(); const bool supportsFrameRateOverrideByContent = - refreshRateConfigs->supportsFrameRateOverrideByContent(); + holdRefreshRateSelector()->supportsFrameRateOverrideByContent(); std::vector<FrameRateOverride> overrides = mFrameRateOverrideMappings.getAllFrameRateOverrides(supportsFrameRateOverrideByContent); @@ -336,8 +333,8 @@ void Scheduler::dispatchCachedReportedMode() { // If the mode is not the current mode, this means that a // mode change is in progress. In that case we shouldn't dispatch an event // as it will be dispatched when the current mode changes. - if (std::scoped_lock lock(mRefreshRateConfigsLock); - mRefreshRateConfigs->getActiveModePtr() != mPolicy.mode) { + if (std::scoped_lock lock(mRefreshRateSelectorLock); + mRefreshRateSelector->getActiveModePtr() != mPolicy.mode) { return; } @@ -431,8 +428,8 @@ void Scheduler::resync() { if (now - last > kIgnoreDelay) { const auto refreshRate = [&] { - std::scoped_lock lock(mRefreshRateConfigsLock); - return mRefreshRateConfigs->getActiveModePtr()->getFps(); + std::scoped_lock lock(mRefreshRateSelectorLock); + return mRefreshRateSelector->getActiveModePtr()->getFps(); }(); resyncToHardwareVsync(false, refreshRate); } @@ -493,8 +490,8 @@ void Scheduler::deregisterLayer(Layer* layer) { void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime, LayerHistory::LayerUpdateType updateType) { { - std::scoped_lock lock(mRefreshRateConfigsLock); - if (!mRefreshRateConfigs->canSwitch()) return; + std::scoped_lock lock(mRefreshRateSelectorLock); + if (!mRefreshRateSelector->canSwitch()) return; } mLayerHistory.record(layer, presentTime, systemTime(), updateType); @@ -510,26 +507,26 @@ void Scheduler::setDefaultFrameRateCompatibility(Layer* layer) { } void Scheduler::chooseRefreshRateForContent() { - const auto configs = holdRefreshRateConfigs(); - if (!configs->canSwitch()) return; + const auto selectorPtr = holdRefreshRateSelector(); + if (!selectorPtr->canSwitch()) return; ATRACE_CALL(); - LayerHistory::Summary summary = mLayerHistory.summarize(*configs, systemTime()); + LayerHistory::Summary summary = mLayerHistory.summarize(*selectorPtr, systemTime()); applyPolicy(&Policy::contentRequirements, std::move(summary)); } void Scheduler::resetIdleTimer() { - std::scoped_lock lock(mRefreshRateConfigsLock); - mRefreshRateConfigs->resetIdleTimer(/*kernelOnly*/ false); + std::scoped_lock lock(mRefreshRateSelectorLock); + mRefreshRateSelector->resetIdleTimer(/*kernelOnly*/ false); } void Scheduler::onTouchHint() { if (mTouchTimer) { mTouchTimer->reset(); - std::scoped_lock lock(mRefreshRateConfigsLock); - mRefreshRateConfigs->resetIdleTimer(/*kernelOnly*/ true); + std::scoped_lock lock(mRefreshRateSelectorLock); + mRefreshRateSelector->resetIdleTimer(/*kernelOnly*/ true); } } @@ -555,8 +552,8 @@ void Scheduler::kernelIdleTimerCallback(TimerState state) { // TODO(145561154): cleanup the kernel idle timer implementation and the refresh rate // magic number const Fps refreshRate = [&] { - std::scoped_lock lock(mRefreshRateConfigsLock); - return mRefreshRateConfigs->getActiveModePtr()->getFps(); + std::scoped_lock lock(mRefreshRateSelectorLock); + return mRefreshRateSelector->getActiveModePtr()->getFps(); }(); constexpr Fps FPS_THRESHOLD_FOR_KERNEL_TIMER = 65_Hz; @@ -623,15 +620,14 @@ void Scheduler::dumpVsync(std::string& out) const { } bool Scheduler::updateFrameRateOverrides(GlobalSignals consideredSignals, Fps displayRefreshRate) { - const auto refreshRateConfigs = holdRefreshRateConfigs(); - // we always update mFrameRateOverridesByContent here // supportsFrameRateOverridesByContent will be checked // when getting FrameRateOverrides from mFrameRateOverrideMappings if (!consideredSignals.idle) { const auto frameRateOverrides = - refreshRateConfigs->getFrameRateOverrides(mPolicy.contentRequirements, - displayRefreshRate, consideredSignals); + holdRefreshRateSelector()->getFrameRateOverrides(mPolicy.contentRequirements, + displayRefreshRate, + consideredSignals); return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides); } return false; @@ -645,7 +641,6 @@ auto Scheduler::applyPolicy(S Policy::*statePtr, T&& newState) -> GlobalSignals bool refreshRateChanged = false; bool frameRateOverridesChanged; - const auto refreshRateConfigs = holdRefreshRateConfigs(); { std::lock_guard<std::mutex> lock(mPolicyLock); @@ -698,7 +693,7 @@ auto Scheduler::applyPolicy(S Policy::*statePtr, T&& newState) -> GlobalSignals auto Scheduler::chooseDisplayModes() const -> DisplayModeChoiceMap { ATRACE_CALL(); - using RankedRefreshRates = RefreshRateConfigs::RankedRefreshRates; + using RankedRefreshRates = RefreshRateSelector::RankedRefreshRates; display::PhysicalDisplayVector<RankedRefreshRates> perDisplayRanking; // Tallies the score of a refresh rate across `displayCount` displays. @@ -715,10 +710,9 @@ auto Scheduler::chooseDisplayModes() const -> DisplayModeChoiceMap { const auto globalSignals = makeGlobalSignals(); - for (const auto& [id, display] : mDisplays) { + for (const auto& [id, selectorPtr] : mRefreshRateSelectors) { auto rankedRefreshRates = - display->holdRefreshRateConfigs() - ->getRankedRefreshRates(mPolicy.contentRequirements, globalSignals); + selectorPtr->getRankedRefreshRates(mPolicy.contentRequirements, globalSignals); for (const auto& [modePtr, score] : rankedRefreshRates.ranking) { const auto [it, inserted] = refreshRateTallies.try_emplace(modePtr->getFps(), score); @@ -737,7 +731,7 @@ auto Scheduler::chooseDisplayModes() const -> DisplayModeChoiceMap { // Find the first refresh rate common to all displays. while (maxScoreIt != refreshRateTallies.cend() && - maxScoreIt->second.displayCount != mDisplays.size()) { + maxScoreIt->second.displayCount != mRefreshRateSelectors.size()) { ++maxScoreIt; } @@ -746,7 +740,8 @@ auto Scheduler::chooseDisplayModes() const -> DisplayModeChoiceMap { for (auto it = maxScoreIt + 1; it != refreshRateTallies.cend(); ++it) { const auto [fps, tally] = *it; - if (tally.displayCount == mDisplays.size() && tally.score > maxScoreIt->second.score) { + if (tally.displayCount == mRefreshRateSelectors.size() && + tally.score > maxScoreIt->second.score) { maxScoreIt = it; } } @@ -793,9 +788,9 @@ DisplayModePtr Scheduler::getPreferredDisplayMode() { std::lock_guard<std::mutex> lock(mPolicyLock); // Make sure the stored mode is up to date. if (mPolicy.mode) { - const auto configs = holdRefreshRateConfigs(); const auto ranking = - configs->getRankedRefreshRates(mPolicy.contentRequirements, makeGlobalSignals()) + holdRefreshRateSelector() + ->getRankedRefreshRates(mPolicy.contentRequirements, makeGlobalSignals()) .ranking; mPolicy.mode = ranking.front().modePtr; diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index 33f612632b..901cf74558 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -39,13 +39,12 @@ #include "Display/DisplayMap.h" #include "Display/DisplayModeRequest.h" -#include "DisplayDevice.h" #include "EventThread.h" #include "FrameRateOverrideMappings.h" #include "LayerHistory.h" #include "MessageQueue.h" #include "OneShotTimer.h" -#include "RefreshRateConfigs.h" +#include "RefreshRateSelector.h" #include "VsyncSchedule.h" namespace android::scheduler { @@ -87,7 +86,7 @@ class TokenManager; namespace scheduler { -using GlobalSignals = RefreshRateConfigs::GlobalSignals; +using GlobalSignals = RefreshRateSelector::GlobalSignals; struct ISchedulerCallback { virtual void setVsyncEnabled(bool) = 0; @@ -107,10 +106,11 @@ public: virtual ~Scheduler(); void startTimers(); - void setRefreshRateConfigs(std::shared_ptr<RefreshRateConfigs>) - EXCLUDES(mRefreshRateConfigsLock); - void registerDisplay(sp<const DisplayDevice>); + using RefreshRateSelectorPtr = std::shared_ptr<RefreshRateSelector>; + void setRefreshRateSelector(RefreshRateSelectorPtr) EXCLUDES(mRefreshRateSelectorLock); + + void registerDisplay(PhysicalDisplayId, RefreshRateSelectorPtr); void unregisterDisplay(PhysicalDisplayId); void run(); @@ -163,7 +163,7 @@ public: // Otherwise, if hardware vsync is not already enabled then this method will // no-op. void resyncToHardwareVsync(bool makeAvailable, Fps refreshRate); - void resync() EXCLUDES(mRefreshRateConfigsLock); + void resync() EXCLUDES(mRefreshRateSelectorLock); void forceNextResync() { mLastResyncTime = 0; } // Passes a vsync sample to VsyncController. periodFlushed will be true if @@ -175,13 +175,13 @@ public: // Layers are registered on creation, and unregistered when the weak reference expires. void registerLayer(Layer*); void recordLayerHistory(Layer*, nsecs_t presentTime, LayerHistory::LayerUpdateType updateType) - EXCLUDES(mRefreshRateConfigsLock); + EXCLUDES(mRefreshRateSelectorLock); void setModeChangePending(bool pending); void setDefaultFrameRateCompatibility(Layer*); void deregisterLayer(Layer*); // Detects content using layer history, and selects a matching refresh rate. - void chooseRefreshRateForContent() EXCLUDES(mRefreshRateConfigsLock); + void chooseRefreshRateForContent() EXCLUDES(mRefreshRateSelectorLock); void resetIdleTimer(); @@ -226,11 +226,11 @@ public: void setGameModeRefreshRateForUid(FrameRateOverride); // Retrieves the overridden refresh rate for a given uid. - std::optional<Fps> getFrameRateOverride(uid_t uid) const EXCLUDES(mRefreshRateConfigsLock); + std::optional<Fps> getFrameRateOverride(uid_t uid) const EXCLUDES(mRefreshRateSelectorLock); - nsecs_t getVsyncPeriodFromRefreshRateConfigs() const EXCLUDES(mRefreshRateConfigsLock) { - std::scoped_lock lock(mRefreshRateConfigsLock); - return mRefreshRateConfigs->getActiveModePtr()->getFps().getPeriodNsecs(); + nsecs_t getVsyncPeriodFromRefreshRateSelector() const EXCLUDES(mRefreshRateSelectorLock) { + std::scoped_lock lock(mRefreshRateSelectorLock); + return mRefreshRateSelector->getActiveModePtr()->getFps().getPeriodNsecs(); } // Returns the framerate of the layer with the given sequence ID @@ -254,7 +254,7 @@ private: EventThread*, EventRegistrationFlags eventRegistration = {}); // Update feature state machine to given state when corresponding timer resets or expires. - void kernelIdleTimerCallback(TimerState) EXCLUDES(mRefreshRateConfigsLock); + void kernelIdleTimerCallback(TimerState) EXCLUDES(mRefreshRateSelectorLock); void idleTimerCallback(TimerState); void touchTimerCallback(TimerState); void displayPowerTimerCallback(TimerState); @@ -293,16 +293,15 @@ private: bool updateFrameRateOverrides(GlobalSignals, Fps displayRefreshRate) REQUIRES(mPolicyLock); - void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mRefreshRateConfigsLock); + void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mRefreshRateSelectorLock); android::impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const - EXCLUDES(mRefreshRateConfigsLock); + EXCLUDES(mRefreshRateSelectorLock); android::impl::EventThread::GetVsyncPeriodFunction makeGetVsyncPeriodFunction() const; - std::shared_ptr<RefreshRateConfigs> holdRefreshRateConfigs() const - EXCLUDES(mRefreshRateConfigsLock) { - std::scoped_lock lock(mRefreshRateConfigsLock); - return mRefreshRateConfigs; + RefreshRateSelectorPtr holdRefreshRateSelector() const EXCLUDES(mRefreshRateSelectorLock) { + std::scoped_lock lock(mRefreshRateSelectorLock); + return mRefreshRateSelector; } // Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection. @@ -336,7 +335,7 @@ private: mutable std::mutex mPolicyLock; - display::PhysicalDisplayMap<PhysicalDisplayId, sp<const DisplayDevice>> mDisplays; + display::PhysicalDisplayMap<PhysicalDisplayId, RefreshRateSelectorPtr> mRefreshRateSelectors; std::optional<PhysicalDisplayId> mLeaderDisplayId; struct Policy { @@ -359,8 +358,9 @@ private: std::optional<ModeChangedParams> cachedModeChangedParams; } mPolicy GUARDED_BY(mPolicyLock); - mutable std::mutex mRefreshRateConfigsLock; - std::shared_ptr<RefreshRateConfigs> mRefreshRateConfigs GUARDED_BY(mRefreshRateConfigsLock); + // TODO(b/255635821): Remove this by instead looking up the `mLeaderDisplayId` selector. + mutable std::mutex mRefreshRateSelectorLock; + RefreshRateSelectorPtr mRefreshRateSelector GUARDED_BY(mRefreshRateSelectorLock); std::mutex mVsyncTimelineLock; std::optional<hal::VsyncPeriodChangeTimeline> mLastVsyncPeriodChangeTimeline diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp index 898e86578a..0ad42364a2 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp @@ -34,7 +34,7 @@ #include <utils/Log.h> #include <utils/Trace.h> -#include "RefreshRateConfigs.h" +#include "RefreshRateSelector.h" #include "VSyncPredictor.h" namespace android::scheduler { @@ -274,7 +274,7 @@ bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const { std::lock_guard lock(mMutex); const auto divisor = - RefreshRateConfigs::getFrameRateDivisor(Fps::fromPeriodNsecs(mIdealPeriod), frameRate); + RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(mIdealPeriod), frameRate); if (divisor <= 1 || timePoint == 0) { return true; } diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h index bd4f40989d..31b1d6901c 100644 --- a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h +++ b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h @@ -68,6 +68,15 @@ struct FpsRange { bool includes(Fps) const; }; +struct FpsRanges { + // The range of refresh rates that refers to the display mode setting. + FpsRange physical; + + // the range of frame rates that refers to the render rate, which is + // the rate that frames are swapped. + FpsRange render; +}; + static_assert(std::is_trivially_copyable_v<Fps>); constexpr Fps operator""_Hz(unsigned long long frequency) { @@ -127,8 +136,24 @@ inline bool operator!=(FpsRange lhs, FpsRange rhs) { return !(lhs == rhs); } +inline bool operator==(const FpsRanges& lhs, const FpsRanges& rhs) { + return lhs.physical == rhs.physical && lhs.render == rhs.render; +} + +inline bool operator!=(const FpsRanges& lhs, const FpsRanges& rhs) { + return !(lhs == rhs); +} + +inline unsigned operator/(Fps lhs, Fps rhs) { + return static_cast<unsigned>(std::ceil(lhs.getValue() / rhs.getValue())); +} + } // namespace fps_approx_ops +constexpr Fps operator/(Fps fps, unsigned divisor) { + return Fps::fromPeriodNsecs(fps.getPeriodNsecs() * static_cast<nsecs_t>(divisor)); +} + inline bool FpsRange::includes(Fps fps) const { using fps_approx_ops::operator<=; return min <= fps && fps <= max; @@ -151,4 +176,10 @@ inline std::string to_string(FpsRange range) { return base::StringPrintf("[%s, %s]", to_string(min).c_str(), to_string(max).c_str()); } +inline std::string to_string(FpsRanges ranges) { + const auto& [physical, render] = ranges; + return base::StringPrintf("{physical=%s, render=%s}", to_string(physical).c_str(), + to_string(render).c_str()); +} + } // namespace android diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index cfebec70cb..6a25104a95 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -185,7 +185,7 @@ using ui::Dataspace; using ui::DisplayPrimaries; using ui::RenderIntent; -using KernelIdleTimerController = scheduler::RefreshRateConfigs::KernelIdleTimerController; +using KernelIdleTimerController = scheduler::RefreshRateSelector::KernelIdleTimerController; namespace hal = android::hardware::graphics::composer::hal; @@ -357,9 +357,6 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI // debugging stuff... char value[PROPERTY_VALUE_MAX]; - property_get("ro.bq.gpu_to_cpu_unsupported", value, "0"); - mGpuToCpuSupported = !atoi(value); - property_get("ro.build.type", value, "user"); mIsUserBuild = strcmp(value, "user") == 0; @@ -602,7 +599,7 @@ HWComposer& SurfaceFlinger::getHwComposer() const { } renderengine::RenderEngine& SurfaceFlinger::getRenderEngine() const { - return mCompositionEngine->getRenderEngine(); + return *mRenderEngine; } compositionengine::CompositionEngine& SurfaceFlinger::getCompositionEngine() const { @@ -763,7 +760,8 @@ void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) { if (auto type = chooseRenderEngineTypeViaSysProp()) { builder.setRenderEngineType(type.value()); } - mCompositionEngine->setRenderEngine(renderengine::RenderEngine::create(builder.build())); + mRenderEngine = renderengine::RenderEngine::create(builder.build()); + mCompositionEngine->setRenderEngine(mRenderEngine.get()); mMaxRenderTargetSize = std::min(getRenderEngine().getMaxTextureSize(), getRenderEngine().getMaxViewportDims()); @@ -1031,7 +1029,7 @@ status_t SurfaceFlinger::getDynamicDisplayInfo(const sp<IBinder>& displayToken, const PhysicalDisplayId displayId = snapshot.displayId(); - info->activeDisplayModeId = display->refreshRateConfigs().getActiveModePtr()->getId().value(); + info->activeDisplayModeId = display->refreshRateSelector().getActiveModePtr()->getId().value(); info->activeColorMode = display->getCompositionDisplay()->getState().colorMode; info->hdrCapabilities = display->getHdrCapabilities(); @@ -1128,11 +1126,11 @@ status_t SurfaceFlinger::setActiveModeFromBackdoor(const sp<display::DisplayToke // Keep the old switching type. const bool allowGroupSwitching = - display->refreshRateConfigs().getCurrentPolicy().allowGroupSwitching; + display->refreshRateSelector().getCurrentPolicy().allowGroupSwitching; - const scheduler::RefreshRateConfigs::DisplayManagerPolicy policy{modeId, - allowGroupSwitching, - {fps, fps}}; + const scheduler::RefreshRateSelector::DisplayManagerPolicy policy{modeId, + {fps, fps}, + allowGroupSwitching}; return setDesiredDisplayModeSpecsInternal(display, policy); }); @@ -1250,7 +1248,7 @@ void SurfaceFlinger::setActiveModeInHwcIfNeeded() { // Desired active mode was set, it is different than the mode currently in use, however // allowed modes might have changed by the time we process the refresh. // Make sure the desired mode is still allowed - const auto displayModeAllowed = display->refreshRateConfigs().isModeAllowed(desiredModeId); + const auto displayModeAllowed = display->refreshRateSelector().isModeAllowed(desiredModeId); if (!displayModeAllowed) { clearDesiredActiveModeState(display); continue; @@ -1272,7 +1270,7 @@ void SurfaceFlinger::setActiveModeInHwcIfNeeded() { continue; } - display->refreshRateConfigs().onModeChangeInitiated(); + display->refreshRateSelector().onModeChangeInitiated(); mScheduler->onNewVsyncPeriodChangeTimeline(outTimeline); if (outTimeline.refreshRequired) { @@ -1580,9 +1578,10 @@ status_t SurfaceFlinger::addRegionSamplingListener(const Rect& samplingArea, // LayerHandle::getLayer promotes the layer object in a binder thread but we will not destroy // the layer here since the caller has a strong ref to the layer's handle. - // TODO (b/238781169): replace layer with layer id - const wp<Layer> stopLayer = LayerHandle::getLayer(stopLayerHandle); - mRegionSamplingThread->addListener(samplingArea, stopLayer, listener); + const sp<Layer> stopLayer = LayerHandle::getLayer(stopLayerHandle); + mRegionSamplingThread->addListener(samplingArea, + stopLayer ? stopLayer->getSequence() : UNASSIGNED_LAYER_ID, + listener); return NO_ERROR; } @@ -2775,21 +2774,34 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( const auto [kernelIdleTimerController, idleTimerTimeoutMs] = getKernelIdleTimerProperties(compositionDisplay->getId()); - scheduler::RefreshRateConfigs::Config config = - {.enableFrameRateOverride = android::sysprop::enable_frame_rate_override(false), + const auto enableFrameRateOverride = [&] { + using Config = scheduler::RefreshRateSelector::Config; + if (!sysprop::enable_frame_rate_override(false)) { + return Config::FrameRateOverride::Disabled; + } + + if (sysprop::frame_rate_override_for_native_rates(true)) { + return Config::FrameRateOverride::EnabledForNativeRefreshRates; + } + + return Config::FrameRateOverride::Enabled; + }(); + + scheduler::RefreshRateSelector::Config config = + {.enableFrameRateOverride = enableFrameRateOverride, .frameRateMultipleThreshold = base::GetIntProperty("debug.sf.frame_rate_multiple_threshold", 0), .idleTimerTimeout = idleTimerTimeoutMs, .kernelIdleTimerController = kernelIdleTimerController}; - creationArgs.refreshRateConfigs = + creationArgs.refreshRateSelector = mPhysicalDisplays.get(physical->id) .transform(&PhysicalDisplay::snapshotRef) .transform([&](const display::DisplaySnapshot& snapshot) { return std::make_shared< - scheduler::RefreshRateConfigs>(snapshot.displayModes(), - creationArgs.activeModeId, - config); + scheduler::RefreshRateSelector>(snapshot.displayModes(), + creationArgs.activeModeId, + config); }) .value_or(nullptr); @@ -2935,11 +2947,15 @@ void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken, displaySurface, producer); if (mScheduler && !display->isVirtual()) { + auto selectorPtr = display->holdRefreshRateSelector(); + // Display modes are reloaded on hotplug reconnect. if (display->isPrimary()) { - mScheduler->setRefreshRateConfigs(display->holdRefreshRateConfigs()); + mScheduler->setRefreshRateSelector(selectorPtr); } - mScheduler->registerDisplay(display); + + const auto displayId = display->getPhysicalId(); + mScheduler->registerDisplay(displayId, std::move(selectorPtr)); dispatchDisplayHotplugEvent(display->getPhysicalId(), true); } @@ -2992,8 +3008,6 @@ void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken, display->disconnect(); if (display->isVirtual()) { releaseVirtualDisplay(display->getVirtualId()); - } else { - mScheduler->unregisterDisplay(display->getPhysicalId()); } } @@ -3353,7 +3367,7 @@ void SurfaceFlinger::requestDisplayModes(std::vector<display::DisplayModeRequest if (!display) continue; - if (display->refreshRateConfigs().isModeAllowed(modePtr->getId())) { + if (display->refreshRateSelector().isModeAllowed(modePtr->getId())) { setDesiredActiveMode(std::move(request)); } else { ALOGV("%s: Mode %d is disallowed for display %s", __func__, modePtr->getId().value(), @@ -3374,7 +3388,7 @@ void SurfaceFlinger::triggerOnFrameRateOverridesChanged() { void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { LOG_ALWAYS_FATAL_IF(mScheduler); - const auto activeModePtr = display->refreshRateConfigs().getActiveModePtr(); + const auto activeModePtr = display->refreshRateSelector().getActiveModePtr(); const Fps activeRefreshRate = activeModePtr->getFps(); mRefreshRateStats = std::make_unique<scheduler::RefreshRateStats>(*mTimeStats, activeRefreshRate, @@ -3401,14 +3415,14 @@ void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { static_cast<ISchedulerCallback&>(*this), features); { - auto configs = display->holdRefreshRateConfigs(); - if (configs->kernelIdleTimerController().has_value()) { + auto selectorPtr = display->holdRefreshRateSelector(); + if (selectorPtr->kernelIdleTimerController()) { features |= Feature::kKernelIdleTimer; } mScheduler->createVsyncSchedule(features); - mScheduler->setRefreshRateConfigs(std::move(configs)); - mScheduler->registerDisplay(display); + mScheduler->setRefreshRateSelector(selectorPtr); + mScheduler->registerDisplay(display->getPhysicalId(), std::move(selectorPtr)); } setVsyncEnabled(false); mScheduler->startTimers(); @@ -3640,11 +3654,6 @@ status_t SurfaceFlinger::addClientLayer(const LayerCreationArgs& args, const sp< mCreatedLayers.emplace_back(layer, parent, args.addToRoot); } - // attach this layer to the client - if (args.client != nullptr) { - args.client->attachLayer(handle, layer); - } - setTransactionFlags(eTransactionNeeded); return NO_ERROR; } @@ -4650,7 +4659,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: display->setPowerMode(mode); - const auto refreshRate = display->refreshRateConfigs().getActiveMode().getFps(); + const auto refreshRate = display->refreshRateSelector().getActiveMode().getFps(); if (*currentMode == hal::PowerMode::OFF) { // Turn on the display if (isInternalDisplay && (!activeDisplay || !activeDisplay->isPoweredOn())) { @@ -5019,8 +5028,22 @@ void SurfaceFlinger::dumpWideColorInfo(std::string& result) const { } LayersProto SurfaceFlinger::dumpDrawingStateProto(uint32_t traceFlags) const { + std::unordered_set<uint64_t> stackIdsToSkip; + + // Determine if virtual layers display should be skipped + if ((traceFlags & LayerTracing::TRACE_VIRTUAL_DISPLAYS) == 0) { + for (const auto& [_, display] : FTL_FAKE_GUARD(mStateLock, mDisplays)) { + if (display->isVirtual()) { + stackIdsToSkip.insert(display->getLayerStack().id); + } + } + } + LayersProto layersProto; for (const sp<Layer>& layer : mDrawingState.layersSortedByZ) { + if (stackIdsToSkip.find(layer->getLayerStack().id) != stackIdsToSkip.end()) { + continue; + } layer->writeToProto(layersProto, traceFlags); } @@ -5182,14 +5205,11 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, const std::string& comp StringAppendF(&result, " orientation=%s, isPoweredOn=%d\n", toCString(display->getOrientation()), display->isPoweredOn()); } - StringAppendF(&result, - " transaction-flags : %08x\n" - " gpu_to_cpu_unsupported : %d\n", - mTransactionFlags.load(), !mGpuToCpuSupported); + StringAppendF(&result, " transaction-flags : %08x\n", mTransactionFlags.load()); if (const auto display = getDefaultDisplayDeviceLocked()) { std::string fps, xDpi, yDpi; - if (const auto activeModePtr = display->refreshRateConfigs().getActiveModePtr()) { + if (const auto activeModePtr = display->refreshRateSelector().getActiveModePtr()) { fps = to_string(activeModePtr->getFps()); const auto dpi = activeModePtr->getDpi(); @@ -5735,8 +5755,8 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r // defaultMode. The defaultMode doesn't matter for the override // policy though, since we set allowGroupSwitching to true, so it's // not a problem. - scheduler::RefreshRateConfigs::OverridePolicy overridePolicy; - overridePolicy.defaultMode = display->refreshRateConfigs() + scheduler::RefreshRateSelector::OverridePolicy overridePolicy; + overridePolicy.defaultMode = display->refreshRateSelector() .getDisplayManagerPolicy() .defaultMode; overridePolicy.allowGroupSwitching = true; @@ -5749,7 +5769,8 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()); return setDesiredDisplayModeSpecsInternal( - display, scheduler::RefreshRateConfigs::NoOverridePolicy{}); + display, + scheduler::RefreshRateSelector::NoOverridePolicy{}); }) .get(); } @@ -5860,7 +5881,7 @@ void SurfaceFlinger::kernelTimerChanged(bool expired) { if (!updateOverlay) return; // Update the overlay on the main thread to avoid race conditions with - // mRefreshRateConfigs->getActiveMode() + // RefreshRateSelector::getActiveMode. static_cast<void>(mScheduler->schedule([=] { const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()); if (!display) { @@ -5924,7 +5945,7 @@ void SurfaceFlinger::updateKernelIdleTimer(std::chrono::milliseconds timeout, } void SurfaceFlinger::toggleKernelIdleTimer() { - using KernelIdleTimerAction = scheduler::RefreshRateConfigs::KernelIdleTimerAction; + using KernelIdleTimerAction = scheduler::RefreshRateSelector::KernelIdleTimerAction; const auto display = getDefaultDisplayDeviceLocked(); if (!display) { @@ -5935,12 +5956,12 @@ void SurfaceFlinger::toggleKernelIdleTimer() { // If the support for kernel idle timer is disabled for the active display, // don't do anything. const std::optional<KernelIdleTimerController> kernelIdleTimerController = - display->refreshRateConfigs().kernelIdleTimerController(); + display->refreshRateSelector().kernelIdleTimerController(); if (!kernelIdleTimerController.has_value()) { return; } - const KernelIdleTimerAction action = display->refreshRateConfigs().getIdleTimerAction(); + const KernelIdleTimerAction action = display->refreshRateSelector().getIdleTimerAction(); switch (action) { case KernelIdleTimerAction::TurnOff: @@ -5956,7 +5977,7 @@ void SurfaceFlinger::toggleKernelIdleTimer() { if (!mKernelIdleTimerEnabled) { ATRACE_INT("KernelIdleTimer", 1); const std::chrono::milliseconds timeout = - display->refreshRateConfigs().getIdleTimerTimeout(); + display->refreshRateSelector().getIdleTimerTimeout(); updateKernelIdleTimer(timeout, kernelIdleTimerController.value(), display->getPhysicalId()); mKernelIdleTimerEnabled = true; @@ -6525,7 +6546,6 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( // Use an empty fence for the buffer fence, since we just created the buffer so // there is no need for synchronization with the GPU. base::unique_fd bufferFence; - getRenderEngine().useProtectedContext(useProtected); constexpr bool kUseFramebufferCache = false; const auto future = getRenderEngine() @@ -6537,9 +6557,6 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( layer->onLayerDisplayed(future); } - // Always switch back to unprotected context. - getRenderEngine().useProtectedContext(false); - return future; } @@ -6598,7 +6615,7 @@ std::optional<ftl::NonNull<DisplayModePtr>> SurfaceFlinger::getPreferredDisplayM status_t SurfaceFlinger::setDesiredDisplayModeSpecsInternal( const sp<DisplayDevice>& display, - const scheduler::RefreshRateConfigs::PolicyVariant& policy) { + const scheduler::RefreshRateSelector::PolicyVariant& policy) { const auto displayId = display->getPhysicalId(); Mutex::Autolock lock(mStateLock); @@ -6608,10 +6625,10 @@ status_t SurfaceFlinger::setDesiredDisplayModeSpecsInternal( return NO_ERROR; } - auto& configs = display->refreshRateConfigs(); - using SetPolicyResult = scheduler::RefreshRateConfigs::SetPolicyResult; + auto& selector = display->refreshRateSelector(); + using SetPolicyResult = scheduler::RefreshRateSelector::SetPolicyResult; - switch (configs.setPolicy(policy)) { + switch (selector.setPolicy(policy)) { case SetPolicyResult::Invalid: return BAD_VALUE; case SetPolicyResult::Unchanged: @@ -6620,12 +6637,12 @@ status_t SurfaceFlinger::setDesiredDisplayModeSpecsInternal( break; } - const scheduler::RefreshRateConfigs::Policy currentPolicy = configs.getCurrentPolicy(); + const scheduler::RefreshRateSelector::Policy currentPolicy = selector.getCurrentPolicy(); ALOGV("Setting desired display mode specs: %s", currentPolicy.toString().c_str()); // TODO(b/140204874): Leave the event in until we do proper testing with all apps that might // be depending in this callback. - if (const auto activeModePtr = configs.getActiveModePtr(); displayId == mActiveDisplayId) { + if (const auto activeModePtr = selector.getActiveModePtr(); displayId == mActiveDisplayId) { mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, activeModePtr); toggleKernelIdleTimer(); } else { @@ -6644,7 +6661,7 @@ status_t SurfaceFlinger::setDesiredDisplayModeSpecsInternal( ALOGV("Switching to Scheduler preferred mode %d (%s)", preferredModeId.value(), to_string(preferredMode->getFps()).c_str()); - if (!configs.isModeAllowed(preferredModeId)) { + if (!selector.isModeAllowed(preferredModeId)) { ALOGE("%s: Preferred mode %d is disallowed", __func__, preferredModeId.value()); return INVALID_OPERATION; } @@ -6653,10 +6670,33 @@ status_t SurfaceFlinger::setDesiredDisplayModeSpecsInternal( return NO_ERROR; } -status_t SurfaceFlinger::setDesiredDisplayModeSpecs( - const sp<IBinder>& displayToken, ui::DisplayModeId defaultMode, bool allowGroupSwitching, - float primaryRefreshRateMin, float primaryRefreshRateMax, float appRequestRefreshRateMin, - float appRequestRefreshRateMax) { +namespace { +FpsRange translate(const gui::DisplayModeSpecs::RefreshRateRanges::RefreshRateRange& aidlRange) { + return FpsRange{Fps::fromValue(aidlRange.min), Fps::fromValue(aidlRange.max)}; +} + +FpsRanges translate(const gui::DisplayModeSpecs::RefreshRateRanges& aidlRanges) { + return FpsRanges{translate(aidlRanges.physical), translate(aidlRanges.render)}; +} + +gui::DisplayModeSpecs::RefreshRateRanges::RefreshRateRange translate(const FpsRange& range) { + gui::DisplayModeSpecs::RefreshRateRanges::RefreshRateRange aidlRange; + aidlRange.min = range.min.getValue(); + aidlRange.max = range.max.getValue(); + return aidlRange; +} + +gui::DisplayModeSpecs::RefreshRateRanges translate(const FpsRanges& ranges) { + gui::DisplayModeSpecs::RefreshRateRanges aidlRanges; + aidlRanges.physical = translate(ranges.physical); + aidlRanges.render = translate(ranges.render); + return aidlRanges; +} + +} // namespace + +status_t SurfaceFlinger::setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, + const gui::DisplayModeSpecs& specs) { ATRACE_CALL(); if (!displayToken) { @@ -6673,13 +6713,9 @@ status_t SurfaceFlinger::setDesiredDisplayModeSpecs( ALOGW("Attempt to set desired display modes for virtual display"); return INVALID_OPERATION; } else { - using Policy = scheduler::RefreshRateConfigs::DisplayManagerPolicy; - const Policy policy{DisplayModeId(defaultMode), - allowGroupSwitching, - {Fps::fromValue(primaryRefreshRateMin), - Fps::fromValue(primaryRefreshRateMax)}, - {Fps::fromValue(appRequestRefreshRateMin), - Fps::fromValue(appRequestRefreshRateMax)}}; + using Policy = scheduler::RefreshRateSelector::DisplayManagerPolicy; + const Policy policy{DisplayModeId(specs.defaultMode), translate(specs.primaryRanges), + translate(specs.appRequestRanges), specs.allowGroupSwitching}; return setDesiredDisplayModeSpecsInternal(display, policy); } @@ -6689,16 +6725,10 @@ status_t SurfaceFlinger::setDesiredDisplayModeSpecs( } status_t SurfaceFlinger::getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, - ui::DisplayModeId* outDefaultMode, - bool* outAllowGroupSwitching, - float* outPrimaryRefreshRateMin, - float* outPrimaryRefreshRateMax, - float* outAppRequestRefreshRateMin, - float* outAppRequestRefreshRateMax) { + gui::DisplayModeSpecs* outSpecs) { ATRACE_CALL(); - if (!displayToken || !outDefaultMode || !outPrimaryRefreshRateMin || - !outPrimaryRefreshRateMax || !outAppRequestRefreshRateMin || !outAppRequestRefreshRateMax) { + if (!displayToken || !outSpecs) { return BAD_VALUE; } @@ -6712,14 +6742,12 @@ status_t SurfaceFlinger::getDesiredDisplayModeSpecs(const sp<IBinder>& displayTo return INVALID_OPERATION; } - scheduler::RefreshRateConfigs::Policy policy = - display->refreshRateConfigs().getDisplayManagerPolicy(); - *outDefaultMode = policy.defaultMode.value(); - *outAllowGroupSwitching = policy.allowGroupSwitching; - *outPrimaryRefreshRateMin = policy.primaryRange.min.getValue(); - *outPrimaryRefreshRateMax = policy.primaryRange.max.getValue(); - *outAppRequestRefreshRateMin = policy.appRequestRange.min.getValue(); - *outAppRequestRefreshRateMax = policy.appRequestRange.max.getValue(); + scheduler::RefreshRateSelector::Policy policy = + display->refreshRateSelector().getDisplayManagerPolicy(); + outSpecs->defaultMode = policy.defaultMode.value(); + outSpecs->allowGroupSwitching = policy.allowGroupSwitching; + outSpecs->primaryRanges = translate(policy.primaryRanges); + outSpecs->appRequestRanges = translate(policy.appRequestRanges); return NO_ERROR; } @@ -6830,7 +6858,7 @@ status_t SurfaceFlinger::getMaxAcquiredBufferCount(int* buffers) const { if (!getHwComposer().isHeadless()) { if (const auto display = getDefaultDisplayDevice()) { - maxRefreshRate = display->refreshRateConfigs().getSupportedRefreshRateRange().max; + maxRefreshRate = display->refreshRateSelector().getSupportedRefreshRateRange().max; } } @@ -6845,7 +6873,7 @@ uint32_t SurfaceFlinger::getMaxAcquiredBufferCountForCurrentRefreshRate(uid_t ui refreshRate = *frameRateOverride; } else if (!getHwComposer().isHeadless()) { if (const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked())) { - refreshRate = display->refreshRateConfigs().getActiveModePtr()->getFps(); + refreshRate = display->refreshRateSelector().getActiveModePtr()->getFps(); } } @@ -6934,7 +6962,7 @@ void SurfaceFlinger::onActiveDisplayChangedLocked(const sp<DisplayDevice>& activ activeDisplay->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true); updateInternalDisplayVsyncLocked(activeDisplay); mScheduler->setModeChangePending(false); - mScheduler->setRefreshRateConfigs(activeDisplay->holdRefreshRateConfigs()); + mScheduler->setRefreshRateSelector(activeDisplay->holdRefreshRateSelector()); onActiveDisplaySizeChanged(activeDisplay); mActiveDisplayTransformHint = activeDisplay->getTransformHint(); @@ -7610,18 +7638,11 @@ binder::Status SurfaceComposerAIDL::removeTunnelModeEnabledListener( return binderStatusFromStatusT(status); } -binder::Status SurfaceComposerAIDL::setDesiredDisplayModeSpecs( - const sp<IBinder>& displayToken, int32_t defaultMode, bool allowGroupSwitching, - float primaryRefreshRateMin, float primaryRefreshRateMax, float appRequestRefreshRateMin, - float appRequestRefreshRateMax) { +binder::Status SurfaceComposerAIDL::setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, + const gui::DisplayModeSpecs& specs) { status_t status = checkAccessPermission(); if (status == OK) { - status = mFlinger->setDesiredDisplayModeSpecs(displayToken, - static_cast<ui::DisplayModeId>(defaultMode), - allowGroupSwitching, primaryRefreshRateMin, - primaryRefreshRateMax, - appRequestRefreshRateMin, - appRequestRefreshRateMax); + status = mFlinger->setDesiredDisplayModeSpecs(displayToken, specs); } return binderStatusFromStatusT(status); } @@ -7637,25 +7658,7 @@ binder::Status SurfaceComposerAIDL::getDesiredDisplayModeSpecs(const sp<IBinder> return binderStatusFromStatusT(status); } - ui::DisplayModeId displayModeId; - bool allowGroupSwitching; - float primaryRefreshRateMin; - float primaryRefreshRateMax; - float appRequestRefreshRateMin; - float appRequestRefreshRateMax; - status = mFlinger->getDesiredDisplayModeSpecs(displayToken, &displayModeId, - &allowGroupSwitching, &primaryRefreshRateMin, - &primaryRefreshRateMax, &appRequestRefreshRateMin, - &appRequestRefreshRateMax); - if (status == NO_ERROR) { - outSpecs->defaultMode = displayModeId; - outSpecs->allowGroupSwitching = allowGroupSwitching; - outSpecs->primaryRefreshRateMin = primaryRefreshRateMin; - outSpecs->primaryRefreshRateMax = primaryRefreshRateMax; - outSpecs->appRequestRefreshRateMin = appRequestRefreshRateMin; - outSpecs->appRequestRefreshRateMax = appRequestRefreshRateMax; - } - + status = mFlinger->getDesiredDisplayModeSpecs(displayToken, outSpecs); return binderStatusFromStatusT(status); } diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 85c194bbb5..3d93d90b30 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -69,7 +69,7 @@ #include "FrontEnd/LayerCreationArgs.h" #include "FrontEnd/TransactionHandler.h" #include "LayerVector.h" -#include "Scheduler/RefreshRateConfigs.h" +#include "Scheduler/RefreshRateSelector.h" #include "Scheduler/RefreshRateStats.h" #include "Scheduler/Scheduler.h" #include "Scheduler/VsyncModulator.h" @@ -464,7 +464,7 @@ private: typename Handler = VsyncModulator::VsyncConfigOpt (VsyncModulator::*)(Args...)> void modulateVsync(Handler handler, Args... args) { if (const auto config = (*mVsyncModulator.*handler)(args...)) { - const auto vsyncPeriod = mScheduler->getVsyncPeriodFromRefreshRateConfigs(); + const auto vsyncPeriod = mScheduler->getVsyncPeriodFromRefreshRateSelector(); setVsyncConfig(*config, vsyncPeriod); } } @@ -551,17 +551,8 @@ private: status_t addTunnelModeEnabledListener(const sp<gui::ITunnelModeEnabledListener>& listener); status_t removeTunnelModeEnabledListener(const sp<gui::ITunnelModeEnabledListener>& listener); status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, - ui::DisplayModeId displayModeId, bool allowGroupSwitching, - float primaryRefreshRateMin, float primaryRefreshRateMax, - float appRequestRefreshRateMin, - float appRequestRefreshRateMax); - status_t getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, - ui::DisplayModeId* outDefaultMode, - bool* outAllowGroupSwitching, - float* outPrimaryRefreshRateMin, - float* outPrimaryRefreshRateMax, - float* outAppRequestRefreshRateMin, - float* outAppRequestRefreshRateMax); + const gui::DisplayModeSpecs&); + status_t getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, gui::DisplayModeSpecs*); status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken, bool* outSupport) const; status_t setDisplayBrightness(const sp<IBinder>& displayToken, const gui::DisplayBrightness& brightness); @@ -633,7 +624,7 @@ private: // Toggles the kernel idle timer on or off depending the policy decisions around refresh rates. void toggleKernelIdleTimer() REQUIRES(mStateLock); - using KernelIdleTimerController = scheduler::RefreshRateConfigs::KernelIdleTimerController; + using KernelIdleTimerController = scheduler::RefreshRateSelector::KernelIdleTimerController; // Get the controller and timeout that will help decide how the kernel idle timer will be // configured and what value to use as the timeout. @@ -672,8 +663,8 @@ private: std::optional<ftl::NonNull<DisplayModePtr>> getPreferredDisplayMode( PhysicalDisplayId, DisplayModeId defaultModeId) const REQUIRES(mStateLock); - status_t setDesiredDisplayModeSpecsInternal(const sp<DisplayDevice>&, - const scheduler::RefreshRateConfigs::PolicyVariant&) + status_t setDesiredDisplayModeSpecsInternal( + const sp<DisplayDevice>&, const scheduler::RefreshRateSelector::PolicyVariant&) EXCLUDES(mStateLock) REQUIRES(kMainThreadContext); void commitTransactions() EXCLUDES(mStateLock) REQUIRES(kMainThreadContext); @@ -1109,7 +1100,6 @@ private: // constant members (no synchronization needed for access) const nsecs_t mBootTime = systemTime(); - bool mGpuToCpuSupported = false; bool mIsUserBuild = true; // Can only accessed from the main thread, these members @@ -1248,6 +1238,7 @@ private: ui::Dataspace mColorSpaceAgnosticDataspace; float mDimmingRatio = -1.f; + std::unique_ptr<renderengine::RenderEngine> mRenderEngine; std::unique_ptr<compositionengine::CompositionEngine> mCompositionEngine; // mMaxRenderTargetSize is only set once in init() so it doesn't need to be protected by // any mutex. @@ -1450,11 +1441,8 @@ public: const sp<gui::ITunnelModeEnabledListener>& listener) override; binder::Status removeTunnelModeEnabledListener( const sp<gui::ITunnelModeEnabledListener>& listener) override; - binder::Status setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, int32_t defaultMode, - bool allowGroupSwitching, float primaryRefreshRateMin, - float primaryRefreshRateMax, - float appRequestRefreshRateMin, - float appRequestRefreshRateMax) override; + binder::Status setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, + const gui::DisplayModeSpecs&) override; binder::Status getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, gui::DisplayModeSpecs* outSpecs) override; binder::Status getDisplayBrightnessSupport(const sp<IBinder>& displayToken, diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h index 41edd22102..f310c4ac53 100644 --- a/services/surfaceflinger/SurfaceFlingerFactory.h +++ b/services/surfaceflinger/SurfaceFlingerFactory.h @@ -52,7 +52,6 @@ class CompositionEngine; namespace scheduler { class VsyncConfiguration; class VsyncController; -class RefreshRateConfigs; } // namespace scheduler namespace frametimeline { diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp index 20fa091730..c8c71dfad1 100644 --- a/services/surfaceflinger/SurfaceFlingerProperties.cpp +++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp @@ -367,6 +367,10 @@ bool enable_frame_rate_override(bool defaultValue) { return SurfaceFlingerProperties::enable_frame_rate_override().value_or(defaultValue); } +bool frame_rate_override_for_native_rates(bool defaultValue) { + return SurfaceFlingerProperties::frame_rate_override_for_native_rates().value_or(defaultValue); +} + bool enable_layer_caching(bool defaultValue) { return SurfaceFlingerProperties::enable_layer_caching().value_or(defaultValue); } diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h index 080feee686..5e316cfa0e 100644 --- a/services/surfaceflinger/SurfaceFlingerProperties.h +++ b/services/surfaceflinger/SurfaceFlingerProperties.h @@ -96,6 +96,8 @@ bool update_device_product_info_on_hotplug_reconnect(bool defaultValue); bool enable_frame_rate_override(bool defaultValue); +bool frame_rate_override_for_native_rates(bool defaultValue); + bool enable_layer_caching(bool defaultValue); bool enable_sdr_dimming(bool defaultValue); diff --git a/services/surfaceflinger/Tracing/LayerTracing.h b/services/surfaceflinger/Tracing/LayerTracing.h index e73dac62e4..b32001cc63 100644 --- a/services/surfaceflinger/Tracing/LayerTracing.h +++ b/services/surfaceflinger/Tracing/LayerTracing.h @@ -55,6 +55,7 @@ public: TRACE_EXTRA = 1 << 3, TRACE_HWC = 1 << 4, TRACE_BUFFERS = 1 << 5, + TRACE_VIRTUAL_DISPLAYS = 1 << 6, TRACE_ALL = TRACE_INPUT | TRACE_COMPOSITION | TRACE_EXTRA, }; void setTraceFlags(uint32_t flags); diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp index 14384a7028..ce4d18fbe1 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp +++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp @@ -245,6 +245,7 @@ void SurfaceFlingerFuzzer::process(const uint8_t *data, size_t size) { setDisplayStateLocked(); setTransactionState(); + mTestableFlinger.flushTransactionQueues(); onTransact(data, size); } diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h index e55586774f..9ba9b90b82 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h +++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h @@ -39,7 +39,7 @@ #include "NativeWindowSurface.h" #include "Scheduler/EventThread.h" #include "Scheduler/MessageQueue.h" -#include "Scheduler/RefreshRateConfigs.h" +#include "Scheduler/RefreshRateSelector.h" #include "Scheduler/VSyncTracker.h" #include "Scheduler/VsyncConfiguration.h" #include "Scheduler/VsyncController.h" @@ -218,18 +218,19 @@ namespace scheduler { class TestableScheduler : public Scheduler, private ICompositor { public: - TestableScheduler(const std::shared_ptr<scheduler::RefreshRateConfigs> &refreshRateConfigs, - ISchedulerCallback &callback) + TestableScheduler(const std::shared_ptr<scheduler::RefreshRateSelector>& selectorPtr, + ISchedulerCallback& callback) : TestableScheduler(std::make_unique<android::mock::VsyncController>(), - std::make_unique<android::mock::VSyncTracker>(), refreshRateConfigs, + std::make_unique<android::mock::VSyncTracker>(), selectorPtr, callback) {} TestableScheduler(std::unique_ptr<VsyncController> controller, std::unique_ptr<VSyncTracker> tracker, - std::shared_ptr<RefreshRateConfigs> configs, ISchedulerCallback &callback) + std::shared_ptr<RefreshRateSelector> selectorPtr, + ISchedulerCallback& callback) : Scheduler(*this, callback, Feature::kContentDetection) { mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker), nullptr, std::move(controller))); - setRefreshRateConfigs(std::move(configs)); + setRefreshRateSelector(std::move(selectorPtr)); } ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) { @@ -241,7 +242,7 @@ public: auto &mutableLayerHistory() { return mLayerHistory; } - auto refreshRateConfigs() { return holdRefreshRateConfigs(); } + auto refreshRateSelector() { return holdRefreshRateSelector(); } void replaceTouchTimer(int64_t millis) { if (mTouchTimer) { @@ -310,8 +311,8 @@ public: } std::unique_ptr<scheduler::Scheduler> createScheduler( - const std::shared_ptr<scheduler::RefreshRateConfigs> &, - scheduler::ISchedulerCallback &) { + const std::shared_ptr<scheduler::RefreshRateSelector>&, + scheduler::ISchedulerCallback&) { return nullptr; } @@ -504,16 +505,8 @@ public: } void getDesiredDisplayModeSpecs(sp<IBinder> &display) { - ui::DisplayModeId outDefaultMode; - bool outAllowGroupSwitching; - float outPrimaryRefreshRateMin; - float outPrimaryRefreshRateMax; - float outAppRequestRefreshRateMin; - float outAppRequestRefreshRateMax; - mFlinger->getDesiredDisplayModeSpecs(display, &outDefaultMode, &outAllowGroupSwitching, - &outPrimaryRefreshRateMin, &outPrimaryRefreshRateMax, - &outAppRequestRefreshRateMin, - &outAppRequestRefreshRateMax); + gui::DisplayModeSpecs _; + mFlinger->getDesiredDisplayModeSpecs(display, &_); } void setVsyncConfig(FuzzedDataProvider *fdp) { @@ -633,7 +626,8 @@ public: } void setupRenderEngine(std::unique_ptr<renderengine::RenderEngine> renderEngine) { - mFlinger->mCompositionEngine->setRenderEngine(std::move(renderEngine)); + mFlinger->mRenderEngine = std::move(renderEngine); + mFlinger->mCompositionEngine->setRenderEngine(mFlinger->mRenderEngine.get()); } void setupComposer(std::unique_ptr<Hwc2::Composer> composer) { @@ -660,9 +654,9 @@ public: modes.try_emplace(kModeId90, mock::createDisplayMode(kModeId90, 90_Hz)); } - mRefreshRateConfigs = std::make_shared<scheduler::RefreshRateConfigs>(modes, kModeId60); + mRefreshRateSelector = std::make_shared<scheduler::RefreshRateSelector>(modes, kModeId60); const auto fps = - FTL_FAKE_GUARD(kMainThreadContext, mRefreshRateConfigs->getActiveMode().getFps()); + FTL_FAKE_GUARD(kMainThreadContext, mRefreshRateSelector->getActiveMode().getFps()); mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps); mFlinger->mVsyncModulator = sp<scheduler::VsyncModulator>::make( mFlinger->mVsyncConfiguration->getCurrentConfigs()); @@ -671,7 +665,7 @@ public: hal::PowerMode::OFF); mScheduler = new scheduler::TestableScheduler(std::move(vsyncController), - std::move(vsyncTracker), mRefreshRateConfigs, + std::move(vsyncTracker), mRefreshRateSelector, *(callback ?: this)); mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread)); @@ -748,6 +742,11 @@ public: listenerCallbacks, transactionId); } + auto flushTransactionQueues() { + ftl::FakeGuard guard(kMainThreadContext); + return mFlinger->flushTransactionQueues(VsyncId{0}); + } + auto onTransact(uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) { return mFlinger->onTransact(code, data, reply, flags); } @@ -775,8 +774,8 @@ public: mutableDrawingState().displays.clear(); mFlinger->mScheduler.reset(); mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>()); - mFlinger->mCompositionEngine->setRenderEngine( - std::unique_ptr<renderengine::RenderEngine>()); + mFlinger->mRenderEngine = std::unique_ptr<renderengine::RenderEngine>(); + mFlinger->mCompositionEngine->setRenderEngine(mFlinger->mRenderEngine.get()); } private: @@ -789,7 +788,7 @@ private: sp<SurfaceFlinger> mFlinger = sp<SurfaceFlinger>::make(mFactory, SurfaceFlinger::SkipInitialization); scheduler::TestableScheduler *mScheduler = nullptr; - std::shared_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs; + std::shared_ptr<scheduler::RefreshRateSelector> mRefreshRateSelector; }; } // namespace android diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp index 2614288016..f2d008d59f 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp +++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp @@ -23,6 +23,7 @@ #include "Scheduler/DispSyncSource.h" #include "Scheduler/OneShotTimer.h" +#include "Scheduler/RefreshRateSelector.h" #include "Scheduler/VSyncDispatchTimerQueue.h" #include "Scheduler/VSyncPredictor.h" #include "Scheduler/VSyncReactor.h" @@ -38,7 +39,7 @@ constexpr nsecs_t kVsyncPeriods[] = {(30_Hz).getPeriodNsecs(), (60_Hz).getPeriod (72_Hz).getPeriodNsecs(), (90_Hz).getPeriodNsecs(), (120_Hz).getPeriodNsecs()}; -constexpr auto kLayerVoteTypes = ftl::enum_range<scheduler::RefreshRateConfigs::LayerVoteType>(); +constexpr auto kLayerVoteTypes = ftl::enum_range<scheduler::RefreshRateSelector::LayerVoteType>(); constexpr PowerMode kPowerModes[] = {PowerMode::ON, PowerMode::DOZE, PowerMode::OFF, PowerMode::DOZE_SUSPEND, PowerMode::ON_SUSPEND}; @@ -59,7 +60,7 @@ public: private: void fuzzRefreshRateSelection(); - void fuzzRefreshRateConfigs(); + void fuzzRefreshRateSelector(); void fuzzPresentLatencyTracker(); void fuzzVSyncModulator(); void fuzzVSyncPredictor(); @@ -238,8 +239,8 @@ void SchedulerFuzzer::fuzzLayerHistory() { time1 += mFdp.PickValueInArray(kVsyncPeriods); time2 += mFdp.PickValueInArray(kVsyncPeriods); } - historyV1.summarize(*scheduler->refreshRateConfigs(), time1); - historyV1.summarize(*scheduler->refreshRateConfigs(), time2); + historyV1.summarize(*scheduler->refreshRateSelector(), time1); + historyV1.summarize(*scheduler->refreshRateSelector(), time2); scheduler->createConnection(std::make_unique<android::mock::EventThread>()); @@ -327,9 +328,9 @@ void SchedulerFuzzer::fuzzRefreshRateSelection() { layer->setFrameRateSelectionPriority(mFdp.ConsumeIntegral<int16_t>()); } -void SchedulerFuzzer::fuzzRefreshRateConfigs() { - using RefreshRateConfigs = scheduler::RefreshRateConfigs; - using LayerRequirement = RefreshRateConfigs::LayerRequirement; +void SchedulerFuzzer::fuzzRefreshRateSelector() { + using RefreshRateSelector = scheduler::RefreshRateSelector; + using LayerRequirement = RefreshRateSelector::LayerRequirement; using RefreshRateStats = scheduler::RefreshRateStats; const uint16_t minRefreshRate = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX >> 1); @@ -345,48 +346,48 @@ void SchedulerFuzzer::fuzzRefreshRateConfigs() { Fps::fromValue(static_cast<float>(fps)))); } - RefreshRateConfigs refreshRateConfigs(displayModes, modeId); + RefreshRateSelector refreshRateSelector(displayModes, modeId); - const RefreshRateConfigs::GlobalSignals globalSignals = {.touch = false, .idle = false}; + const RefreshRateSelector::GlobalSignals globalSignals = {.touch = false, .idle = false}; std::vector<LayerRequirement> layers = {{.weight = mFdp.ConsumeFloatingPoint<float>()}}; - refreshRateConfigs.getRankedRefreshRates(layers, globalSignals); + refreshRateSelector.getRankedRefreshRates(layers, globalSignals); layers[0].name = mFdp.ConsumeRandomLengthString(kRandomStringLength); layers[0].ownerUid = mFdp.ConsumeIntegral<uint16_t>(); layers[0].desiredRefreshRate = Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()); layers[0].vote = mFdp.PickValueInArray(kLayerVoteTypes.values); auto frameRateOverrides = - refreshRateConfigs.getFrameRateOverrides(layers, - Fps::fromValue( - mFdp.ConsumeFloatingPoint<float>()), - globalSignals); + refreshRateSelector.getFrameRateOverrides(layers, + Fps::fromValue( + mFdp.ConsumeFloatingPoint<float>()), + globalSignals); { ftl::FakeGuard guard(kMainThreadContext); - refreshRateConfigs.setPolicy( - RefreshRateConfigs:: + refreshRateSelector.setPolicy( + RefreshRateSelector:: DisplayManagerPolicy{modeId, {Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()), Fps::fromValue(mFdp.ConsumeFloatingPoint<float>())}}); - refreshRateConfigs.setPolicy( - RefreshRateConfigs::OverridePolicy{modeId, - {Fps::fromValue( - mFdp.ConsumeFloatingPoint<float>()), - Fps::fromValue( - mFdp.ConsumeFloatingPoint<float>())}}); - refreshRateConfigs.setPolicy(RefreshRateConfigs::NoOverridePolicy{}); + refreshRateSelector.setPolicy( + RefreshRateSelector::OverridePolicy{modeId, + {Fps::fromValue( + mFdp.ConsumeFloatingPoint<float>()), + Fps::fromValue( + mFdp.ConsumeFloatingPoint<float>())}}); + refreshRateSelector.setPolicy(RefreshRateSelector::NoOverridePolicy{}); - refreshRateConfigs.setActiveModeId(modeId); + refreshRateSelector.setActiveModeId(modeId); } - RefreshRateConfigs::isFractionalPairOrMultiple(Fps::fromValue( - mFdp.ConsumeFloatingPoint<float>()), - Fps::fromValue( - mFdp.ConsumeFloatingPoint<float>())); - RefreshRateConfigs::getFrameRateDivisor(Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()), - Fps::fromValue(mFdp.ConsumeFloatingPoint<float>())); + RefreshRateSelector::isFractionalPairOrMultiple(Fps::fromValue( + mFdp.ConsumeFloatingPoint<float>()), + Fps::fromValue( + mFdp.ConsumeFloatingPoint<float>())); + RefreshRateSelector::getFrameRateDivisor(Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()), + Fps::fromValue(mFdp.ConsumeFloatingPoint<float>())); android::mock::TimeStats timeStats; RefreshRateStats refreshRateStats(timeStats, Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()), @@ -407,7 +408,7 @@ void SchedulerFuzzer::fuzzPresentLatencyTracker() { void SchedulerFuzzer::process() { fuzzRefreshRateSelection(); - fuzzRefreshRateConfigs(); + fuzzRefreshRateSelector(); fuzzPresentLatencyTracker(); fuzzVSyncModulator(); fuzzVSyncPredictor(); diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h index 1a49ead275..88e32e1bd0 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h +++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h @@ -27,7 +27,6 @@ #include "Clock.h" #include "Layer.h" #include "Scheduler/EventThread.h" -#include "Scheduler/RefreshRateConfigs.h" #include "Scheduler/Scheduler.h" #include "Scheduler/VSyncTracker.h" #include "Scheduler/VsyncModulator.h" diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop index bcbe21a483..28da81ff50 100644 --- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop +++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop @@ -445,6 +445,18 @@ prop { prop_name: "ro.surface_flinger.enable_frame_rate_override" } +# Limits the frame rate override feature (enable_frame_rate_override) to override the refresh rate +# to native display refresh rates only. Before introducing this flag, native display refresh rates +# was the default behvaiour. With this flag we can control which behaviour we want explicitly. +# This flag is intoruduced as a fail-safe machanism and planned to be defaulted to false. +prop { + api_name: "frame_rate_override_for_native_rates" + type: Boolean + scope: Public + access: Readonly + prop_name: "ro.surface_flinger.frame_rate_override_for_native_rates" +} + # Enables Layer Caching prop { api_name: "enable_layer_caching" diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt index 348a462038..0dfb80e5df 100644 --- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt +++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt @@ -61,6 +61,10 @@ props { prop_name: "ro.surface_flinger.force_hwc_copy_for_virtual_displays" } prop { + api_name: "frame_rate_override_for_native_rates" + prop_name: "ro.surface_flinger.frame_rate_override_for_native_rates" + } + prop { api_name: "has_HDR_display" prop_name: "ro.surface_flinger.has_HDR_display" } diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp index 4f04934d34..16768441f0 100644 --- a/services/surfaceflinger/tests/Credentials_test.cpp +++ b/services/surfaceflinger/tests/Credentials_test.cpp @@ -207,23 +207,12 @@ TEST_F(CredentialsTest, GetDisplayNativePrimariesTest) { TEST_F(CredentialsTest, SetDesiredDisplayConfigsTest) { const auto display = getFirstDisplayToken(); - ui::DisplayModeId defaultMode; - bool allowGroupSwitching; - float primaryFpsMin; - float primaryFpsMax; - float appRequestFpsMin; - float appRequestFpsMax; - status_t res = - SurfaceComposerClient::getDesiredDisplayModeSpecs(display, &defaultMode, - &allowGroupSwitching, &primaryFpsMin, - &primaryFpsMax, &appRequestFpsMin, - &appRequestFpsMax); + gui::DisplayModeSpecs specs; + status_t res = SurfaceComposerClient::getDesiredDisplayModeSpecs(display, &specs); ASSERT_EQ(res, NO_ERROR); + gui::DisplayModeSpecs setSpecs; std::function<status_t()> condition = [=]() { - return SurfaceComposerClient::setDesiredDisplayModeSpecs(display, defaultMode, - allowGroupSwitching, primaryFpsMin, - primaryFpsMax, appRequestFpsMin, - appRequestFpsMax); + return SurfaceComposerClient::setDesiredDisplayModeSpecs(display, specs); }; ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED)); } diff --git a/services/surfaceflinger/tests/DisplayConfigs_test.cpp b/services/surfaceflinger/tests/DisplayConfigs_test.cpp index 02c934e576..10dae4636e 100644 --- a/services/surfaceflinger/tests/DisplayConfigs_test.cpp +++ b/services/surfaceflinger/tests/DisplayConfigs_test.cpp @@ -39,37 +39,19 @@ namespace android { */ class RefreshRateRangeTest : public ::testing::Test { private: - ui::DisplayModeId initialDefaultMode; - bool initialAllowGroupSwitching; - float initialPrimaryMin; - float initialPrimaryMax; - float initialAppRequestMin; - float initialAppRequestMax; + gui::DisplayModeSpecs mSpecs; protected: void SetUp() override { const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); ASSERT_FALSE(ids.empty()); mDisplayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front()); - status_t res = - SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, - &initialDefaultMode, - &initialAllowGroupSwitching, - &initialPrimaryMin, - &initialPrimaryMax, - &initialAppRequestMin, - &initialAppRequestMax); + status_t res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &mSpecs); ASSERT_EQ(res, NO_ERROR); } void TearDown() override { - status_t res = - SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, initialDefaultMode, - initialAllowGroupSwitching, - initialPrimaryMin, - initialPrimaryMax, - initialAppRequestMin, - initialAppRequestMax); + status_t res = SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, mSpecs); ASSERT_EQ(res, NO_ERROR); } @@ -85,61 +67,39 @@ TEST_F(RefreshRateRangeTest, setAllConfigs) { ASSERT_EQ(res, NO_ERROR); ASSERT_GT(modes.size(), 0); + gui::DisplayModeSpecs setSpecs; + setSpecs.allowGroupSwitching = false; for (size_t i = 0; i < modes.size(); i++) { - res = SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, modes[i].id, false, - modes[i].refreshRate, - modes[i].refreshRate, - modes[i].refreshRate, - modes[i].refreshRate); + setSpecs.defaultMode = modes[i].id; + setSpecs.primaryRanges.physical.min = modes[i].refreshRate; + setSpecs.primaryRanges.physical.max = modes[i].refreshRate; + setSpecs.primaryRanges.render = setSpecs.primaryRanges.physical; + setSpecs.appRequestRanges = setSpecs.primaryRanges; + res = SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, setSpecs); ASSERT_EQ(res, NO_ERROR); - ui::DisplayModeId defaultConfig; - bool allowGroupSwitching; - float primaryRefreshRateMin; - float primaryRefreshRateMax; - float appRequestRefreshRateMin; - float appRequestRefreshRateMax; - res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &defaultConfig, - &allowGroupSwitching, - &primaryRefreshRateMin, - &primaryRefreshRateMax, - &appRequestRefreshRateMin, - &appRequestRefreshRateMax); + gui::DisplayModeSpecs getSpecs; + res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &getSpecs); ASSERT_EQ(res, NO_ERROR); - ASSERT_EQ(defaultConfig, i); - ASSERT_EQ(allowGroupSwitching, false); - ASSERT_EQ(primaryRefreshRateMin, modes[i].refreshRate); - ASSERT_EQ(primaryRefreshRateMax, modes[i].refreshRate); - ASSERT_EQ(appRequestRefreshRateMin, modes[i].refreshRate); - ASSERT_EQ(appRequestRefreshRateMax, modes[i].refreshRate); + ASSERT_EQ(setSpecs, getSpecs); } } void RefreshRateRangeTest::testSetAllowGroupSwitching(bool allowGroupSwitching) { - status_t res = - SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, 0, allowGroupSwitching, - 0.f, 90.f, 0.f, 90.f); + gui::DisplayModeSpecs setSpecs; + setSpecs.defaultMode = 0; + setSpecs.allowGroupSwitching = allowGroupSwitching; + setSpecs.primaryRanges.physical.min = 0; + setSpecs.primaryRanges.physical.max = 90; + setSpecs.primaryRanges.render = setSpecs.primaryRanges.physical; + setSpecs.appRequestRanges = setSpecs.primaryRanges; + + status_t res = SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, setSpecs); ASSERT_EQ(res, NO_ERROR); - ui::DisplayModeId defaultConfig; - bool newAllowGroupSwitching; - float primaryRefreshRateMin; - float primaryRefreshRateMax; - float appRequestRefreshRateMin; - float appRequestRefreshRateMax; - - res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &defaultConfig, - &newAllowGroupSwitching, - &primaryRefreshRateMin, - &primaryRefreshRateMax, - &appRequestRefreshRateMin, - &appRequestRefreshRateMax); + gui::DisplayModeSpecs getSpecs; + res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &getSpecs); ASSERT_EQ(res, NO_ERROR); - ASSERT_EQ(defaultConfig, 0); - ASSERT_EQ(newAllowGroupSwitching, allowGroupSwitching); - ASSERT_EQ(primaryRefreshRateMin, 0.f); - ASSERT_EQ(primaryRefreshRateMax, 90.f); - ASSERT_EQ(appRequestRefreshRateMin, 0.f); - ASSERT_EQ(appRequestRefreshRateMax, 90.f); + ASSERT_EQ(setSpecs, getSpecs); } TEST_F(RefreshRateRangeTest, setAllowGroupSwitching) { diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index d2b58137f0..d88da4da4a 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -111,7 +111,7 @@ cc_test { "SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp", "SchedulerTest.cpp", "SetFrameRateTest.cpp", - "RefreshRateConfigsTest.cpp", + "RefreshRateSelectorTest.cpp", "RefreshRateSelectionTest.cpp", "RefreshRateStatsTest.cpp", "RegionSamplingTest.cpp", @@ -150,7 +150,7 @@ cc_defaults { "android.hardware.power@1.1", "android.hardware.power@1.2", "android.hardware.power@1.3", - "android.hardware.power-V2-cpp", + "android.hardware.power-V4-cpp", "libaidlcommonsupport", "libcompositionengine_mocks", "libcompositionengine", diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp index 972198cbdb..979924af58 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp @@ -69,11 +69,11 @@ protected: // LayerHistory::summarize makes no guarantee of the order of the elements in the summary // however, for testing only, a stable order is required, therefore we sort the list here. // Any tests requiring ordered results must create layers with names. - auto summary = history().summarize(*mScheduler->refreshRateConfigs(), now); + auto summary = history().summarize(*mScheduler->refreshRateSelector(), now); std::sort(summary.begin(), summary.end(), - [](const RefreshRateConfigs::LayerRequirement& a, - const RefreshRateConfigs::LayerRequirement& b) -> bool { - return a.name < b.name; + [](const RefreshRateSelector::LayerRequirement& lhs, + const RefreshRateSelector::LayerRequirement& rhs) -> bool { + return lhs.name < rhs.name; }); return summary; } @@ -125,16 +125,16 @@ protected: ASSERT_EQ(desiredRefreshRate, summary[0].desiredRefreshRate); } - std::shared_ptr<RefreshRateConfigs> mConfigs = - std::make_shared<RefreshRateConfigs>(makeModes(createDisplayMode(DisplayModeId(0), - LO_FPS), - createDisplayMode(DisplayModeId(1), - HI_FPS)), - DisplayModeId(0)); + std::shared_ptr<RefreshRateSelector> mSelector = + std::make_shared<RefreshRateSelector>(makeModes(createDisplayMode(DisplayModeId(0), + LO_FPS), + createDisplayMode(DisplayModeId(1), + HI_FPS)), + DisplayModeId(0)); mock::SchedulerCallback mSchedulerCallback; - TestableScheduler* mScheduler = new TestableScheduler(mConfigs, mSchedulerCallback); + TestableScheduler* mScheduler = new TestableScheduler(mSelector, mSchedulerCallback); TestableSurfaceFlinger mFlinger; }; diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp index 924c5befde..cedb7eb6ee 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp @@ -28,7 +28,7 @@ #include "DisplayHardware/HWC2.h" #include "FpsOps.h" -#include "Scheduler/RefreshRateConfigs.h" +#include "Scheduler/RefreshRateSelector.h" #include "mock/DisplayHardware/MockDisplayMode.h" using namespace std::chrono_literals; @@ -37,26 +37,27 @@ namespace android::scheduler { namespace hal = android::hardware::graphics::composer::hal; -using LayerRequirement = RefreshRateConfigs::LayerRequirement; -using LayerVoteType = RefreshRateConfigs::LayerVoteType; -using SetPolicyResult = RefreshRateConfigs::SetPolicyResult; +using Config = RefreshRateSelector::Config; +using LayerRequirement = RefreshRateSelector::LayerRequirement; +using LayerVoteType = RefreshRateSelector::LayerVoteType; +using SetPolicyResult = RefreshRateSelector::SetPolicyResult; using mock::createDisplayMode; -struct TestableRefreshRateConfigs : RefreshRateConfigs { - using RefreshRateConfigs::RefreshRateOrder; - using RefreshRateConfigs::RefreshRateRanking; +struct TestableRefreshRateSelector : RefreshRateSelector { + using RefreshRateSelector::RefreshRateOrder; + using RefreshRateSelector::RefreshRateRanking; - using RefreshRateConfigs::RefreshRateConfigs; + using RefreshRateSelector::RefreshRateSelector; void setActiveModeId(DisplayModeId modeId) { ftl::FakeGuard guard(kMainThreadContext); - return RefreshRateConfigs::setActiveModeId(modeId); + return RefreshRateSelector::setActiveModeId(modeId); } const DisplayMode& getActiveMode() const { ftl::FakeGuard guard(kMainThreadContext); - return RefreshRateConfigs::getActiveMode(); + return RefreshRateSelector::getActiveMode(); } DisplayModePtr getMinSupportedRefreshRate() const { @@ -82,17 +83,17 @@ struct TestableRefreshRateConfigs : RefreshRateConfigs { RefreshRateRanking rankRefreshRates(std::optional<int> anchorGroupOpt, RefreshRateOrder refreshRateOrder) const { std::lock_guard lock(mLock); - return RefreshRateConfigs::rankRefreshRates(anchorGroupOpt, refreshRateOrder); + return RefreshRateSelector::rankRefreshRates(anchorGroupOpt, refreshRateOrder); } const std::vector<Fps>& knownFrameRates() const { return mKnownFrameRates; } - using RefreshRateConfigs::GetRankedRefreshRatesCache; + using RefreshRateSelector::GetRankedRefreshRatesCache; auto& mutableGetRankedRefreshRatesCache() { return mGetRankedRefreshRatesCache; } auto getRankedRefreshRates(const std::vector<LayerRequirement>& layers, GlobalSignals signals) const { - const auto result = RefreshRateConfigs::getRankedRefreshRates(layers, signals); + const auto result = RefreshRateSelector::getRankedRefreshRates(layers, signals); EXPECT_TRUE(std::is_sorted(result.ranking.begin(), result.ranking.end(), ScoredRefreshRate::DescendingScore{})); @@ -113,7 +114,7 @@ struct TestableRefreshRateConfigs : RefreshRateConfigs { SetPolicyResult setPolicy(const PolicyVariant& policy) { ftl::FakeGuard guard(kMainThreadContext); - return RefreshRateConfigs::setPolicy(policy); + return RefreshRateSelector::setPolicy(policy); } SetPolicyResult setDisplayManagerPolicy(const DisplayManagerPolicy& policy) { @@ -121,12 +122,12 @@ struct TestableRefreshRateConfigs : RefreshRateConfigs { } }; -class RefreshRateConfigsTest : public testing::Test { +class RefreshRateSelectorTest : public testing::Test { protected: - using RefreshRateOrder = TestableRefreshRateConfigs::RefreshRateOrder; + using RefreshRateOrder = TestableRefreshRateSelector::RefreshRateOrder; - RefreshRateConfigsTest(); - ~RefreshRateConfigsTest(); + RefreshRateSelectorTest(); + ~RefreshRateSelectorTest(); static constexpr DisplayModeId kModeId60{0}; static constexpr DisplayModeId kModeId90{1}; @@ -186,13 +187,13 @@ protected: kMode60Frac); }; -RefreshRateConfigsTest::RefreshRateConfigsTest() { +RefreshRateSelectorTest::RefreshRateSelectorTest() { const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info(); ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); } -RefreshRateConfigsTest::~RefreshRateConfigsTest() { +RefreshRateSelectorTest::~RefreshRateSelectorTest() { const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info(); ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); @@ -200,358 +201,359 @@ RefreshRateConfigsTest::~RefreshRateConfigsTest() { namespace { -TEST_F(RefreshRateConfigsTest, oneMode_canSwitch) { - RefreshRateConfigs configs(kModes_60, kModeId60); - EXPECT_FALSE(configs.canSwitch()); +TEST_F(RefreshRateSelectorTest, oneMode_canSwitch) { + RefreshRateSelector selector(kModes_60, kModeId60); + EXPECT_FALSE(selector.canSwitch()); } -TEST_F(RefreshRateConfigsTest, invalidPolicy) { - TestableRefreshRateConfigs configs(kModes_60, kModeId60); +TEST_F(RefreshRateSelectorTest, invalidPolicy) { + TestableRefreshRateSelector selector(kModes_60, kModeId60); EXPECT_EQ(SetPolicyResult::Invalid, - configs.setDisplayManagerPolicy({DisplayModeId(10), {60_Hz, 60_Hz}})); + selector.setDisplayManagerPolicy({DisplayModeId(10), {60_Hz, 60_Hz}})); EXPECT_EQ(SetPolicyResult::Invalid, - configs.setDisplayManagerPolicy({kModeId60, {20_Hz, 40_Hz}})); + selector.setDisplayManagerPolicy({kModeId60, {20_Hz, 40_Hz}})); } -TEST_F(RefreshRateConfigsTest, unchangedPolicy) { - TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); +TEST_F(RefreshRateSelectorTest, unchangedPolicy) { + TestableRefreshRateSelector selector(kModes_60_90, kModeId60); EXPECT_EQ(SetPolicyResult::Changed, - configs.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}})); + selector.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}})); EXPECT_EQ(SetPolicyResult::Unchanged, - configs.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}})); + selector.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}})); // Override to the same policy. EXPECT_EQ(SetPolicyResult::Unchanged, - configs.setPolicy(RefreshRateConfigs::OverridePolicy{kModeId90, {60_Hz, 90_Hz}})); + selector.setPolicy(RefreshRateSelector::OverridePolicy{kModeId90, {60_Hz, 90_Hz}})); // Clear override to restore DisplayManagerPolicy. EXPECT_EQ(SetPolicyResult::Unchanged, - configs.setPolicy(RefreshRateConfigs::NoOverridePolicy{})); + selector.setPolicy(RefreshRateSelector::NoOverridePolicy{})); EXPECT_EQ(SetPolicyResult::Changed, - configs.setDisplayManagerPolicy({kModeId90, {30_Hz, 90_Hz}})); + selector.setDisplayManagerPolicy({kModeId90, {30_Hz, 90_Hz}})); } -TEST_F(RefreshRateConfigsTest, twoModes_storesFullRefreshRateMap) { - TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); +TEST_F(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap) { + TestableRefreshRateSelector selector(kModes_60_90, kModeId60); - const auto minRate = configs.getMinSupportedRefreshRate(); - const auto performanceRate = configs.getMaxSupportedRefreshRate(); + const auto minRate = selector.getMinSupportedRefreshRate(); + const auto performanceRate = selector.getMaxSupportedRefreshRate(); EXPECT_EQ(kMode60, minRate); EXPECT_EQ(kMode90, performanceRate); - const auto minRateByPolicy = configs.getMinRefreshRateByPolicy(); - const auto performanceRateByPolicy = configs.getMaxRefreshRateByPolicy(); + const auto minRateByPolicy = selector.getMinRefreshRateByPolicy(); + const auto performanceRateByPolicy = selector.getMaxRefreshRateByPolicy(); EXPECT_EQ(minRateByPolicy, minRate); EXPECT_EQ(performanceRateByPolicy, performanceRate); } -TEST_F(RefreshRateConfigsTest, twoModes_storesFullRefreshRateMap_differentGroups) { - TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60); +TEST_F(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap_differentGroups) { + TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60); - const auto minRate = configs.getMinRefreshRateByPolicy(); - const auto performanceRate = configs.getMaxSupportedRefreshRate(); - const auto minRate60 = configs.getMinRefreshRateByPolicy(); - const auto performanceRate60 = configs.getMaxRefreshRateByPolicy(); + const auto minRate = selector.getMinRefreshRateByPolicy(); + const auto performanceRate = selector.getMaxSupportedRefreshRate(); + const auto minRate60 = selector.getMinRefreshRateByPolicy(); + const auto performanceRate60 = selector.getMaxRefreshRateByPolicy(); EXPECT_EQ(kMode60, minRate); EXPECT_EQ(kMode60, minRate60); EXPECT_EQ(kMode60, performanceRate60); EXPECT_EQ(SetPolicyResult::Changed, - configs.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}})); - configs.setActiveModeId(kModeId90); + selector.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}})); + selector.setActiveModeId(kModeId90); - const auto minRate90 = configs.getMinRefreshRateByPolicy(); - const auto performanceRate90 = configs.getMaxRefreshRateByPolicy(); + const auto minRate90 = selector.getMinRefreshRateByPolicy(); + const auto performanceRate90 = selector.getMaxRefreshRateByPolicy(); EXPECT_EQ(kMode90_G1, performanceRate); EXPECT_EQ(kMode90_G1, minRate90); EXPECT_EQ(kMode90_G1, performanceRate90); } -TEST_F(RefreshRateConfigsTest, twoModes_storesFullRefreshRateMap_differentResolutions) { - TestableRefreshRateConfigs configs(kModes_60_90_4K, kModeId60); +TEST_F(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap_differentResolutions) { + TestableRefreshRateSelector selector(kModes_60_90_4K, kModeId60); - const auto minRate = configs.getMinRefreshRateByPolicy(); - const auto performanceRate = configs.getMaxSupportedRefreshRate(); - const auto minRate60 = configs.getMinRefreshRateByPolicy(); - const auto performanceRate60 = configs.getMaxRefreshRateByPolicy(); + const auto minRate = selector.getMinRefreshRateByPolicy(); + const auto performanceRate = selector.getMaxSupportedRefreshRate(); + const auto minRate60 = selector.getMinRefreshRateByPolicy(); + const auto performanceRate60 = selector.getMaxRefreshRateByPolicy(); EXPECT_EQ(kMode60, minRate); EXPECT_EQ(kMode60, minRate60); EXPECT_EQ(kMode60, performanceRate60); EXPECT_EQ(SetPolicyResult::Changed, - configs.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}})); - configs.setActiveModeId(kModeId90); + selector.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}})); + selector.setActiveModeId(kModeId90); - const auto minRate90 = configs.getMinRefreshRateByPolicy(); - const auto performanceRate90 = configs.getMaxRefreshRateByPolicy(); + const auto minRate90 = selector.getMinRefreshRateByPolicy(); + const auto performanceRate90 = selector.getMaxRefreshRateByPolicy(); EXPECT_EQ(kMode90_4K, performanceRate); EXPECT_EQ(kMode90_4K, minRate90); EXPECT_EQ(kMode90_4K, performanceRate90); } -TEST_F(RefreshRateConfigsTest, twoModes_policyChange) { - TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); +TEST_F(RefreshRateSelectorTest, twoModes_policyChange) { + TestableRefreshRateSelector selector(kModes_60_90, kModeId60); - const auto minRate = configs.getMinRefreshRateByPolicy(); - const auto performanceRate = configs.getMaxRefreshRateByPolicy(); + const auto minRate = selector.getMinRefreshRateByPolicy(); + const auto performanceRate = selector.getMaxRefreshRateByPolicy(); EXPECT_EQ(kMode60, minRate); EXPECT_EQ(kMode90, performanceRate); EXPECT_EQ(SetPolicyResult::Changed, - configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}})); + selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}})); - const auto minRate60 = configs.getMinRefreshRateByPolicy(); - const auto performanceRate60 = configs.getMaxRefreshRateByPolicy(); + const auto minRate60 = selector.getMinRefreshRateByPolicy(); + const auto performanceRate60 = selector.getMaxRefreshRateByPolicy(); EXPECT_EQ(kMode60, minRate60); EXPECT_EQ(kMode60, performanceRate60); } -TEST_F(RefreshRateConfigsTest, twoModes_getActiveMode) { - TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); +TEST_F(RefreshRateSelectorTest, twoModes_getActiveMode) { + TestableRefreshRateSelector selector(kModes_60_90, kModeId60); { - const auto& mode = configs.getActiveMode(); + const auto& mode = selector.getActiveMode(); EXPECT_EQ(mode.getId(), kModeId60); } - configs.setActiveModeId(kModeId90); + selector.setActiveModeId(kModeId90); { - const auto& mode = configs.getActiveMode(); + const auto& mode = selector.getActiveMode(); EXPECT_EQ(mode.getId(), kModeId90); } EXPECT_EQ(SetPolicyResult::Changed, - configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}})); + selector.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}})); { - const auto& mode = configs.getActiveMode(); + const auto& mode = selector.getActiveMode(); EXPECT_EQ(mode.getId(), kModeId90); } } -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_noLayers) { +TEST_F(RefreshRateSelectorTest, getBestRefreshRate_noLayers) { { - TestableRefreshRateConfigs configs(kModes_60_72_90, kModeId72); + TestableRefreshRateSelector selector(kModes_60_72_90, kModeId72); // If there are no layers we select the default frame rate, which is the max of the primary // range. - EXPECT_EQ(kMode90, configs.getBestRefreshRate()); + EXPECT_EQ(kMode90, selector.getBestRefreshRate()); EXPECT_EQ(SetPolicyResult::Changed, - configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}})); - EXPECT_EQ(kMode60, configs.getBestRefreshRate()); + selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}})); + EXPECT_EQ(kMode60, selector.getBestRefreshRate()); } { // We select max even when this will cause a non-seamless switch. - TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60); + TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60); constexpr bool kAllowGroupSwitching = true; EXPECT_EQ(SetPolicyResult::Changed, - configs.setDisplayManagerPolicy( - {kModeId90, kAllowGroupSwitching, {0_Hz, 90_Hz}})); - EXPECT_EQ(kMode90_G1, configs.getBestRefreshRate()); + selector.setDisplayManagerPolicy( + {kModeId90, {0_Hz, 90_Hz}, kAllowGroupSwitching})); + EXPECT_EQ(kMode90_G1, selector.getBestRefreshRate()); } } -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_exactDontChangeRefreshRateWhenNotInPolicy) { - TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId72); +TEST_F(RefreshRateSelectorTest, getBestRefreshRate_exactDontChangeRefreshRateWhenNotInPolicy) { + TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId72); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; layers[0].vote = LayerVoteType::ExplicitExact; layers[0].desiredRefreshRate = 120_Hz; EXPECT_EQ(SetPolicyResult::Changed, - configs.setDisplayManagerPolicy({kModeId72, {0_Hz, 90_Hz}})); - EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers)); + selector.setDisplayManagerPolicy({kModeId72, {0_Hz, 90_Hz}})); + EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers)); } -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_90) { - TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); +TEST_F(RefreshRateSelectorTest, getBestRefreshRate_60_90) { + TestableRefreshRateSelector selector(kModes_60_90, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& lr = layers[0]; lr.vote = LayerVoteType::Min; lr.name = "Min"; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); lr.vote = LayerVoteType::Max; lr.name = "Max"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 90_Hz; lr.vote = LayerVoteType::Heuristic; lr.name = "90Hz Heuristic"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 60_Hz; lr.name = "60Hz Heuristic"; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 45_Hz; lr.name = "45Hz Heuristic"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 30_Hz; lr.name = "30Hz Heuristic"; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 24_Hz; lr.name = "24Hz Heuristic"; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); lr.name = ""; EXPECT_EQ(SetPolicyResult::Changed, - configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}})); + selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}})); lr.vote = LayerVoteType::Min; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); lr.vote = LayerVoteType::Max; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 90_Hz; lr.vote = LayerVoteType::Heuristic; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 60_Hz; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 45_Hz; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 30_Hz; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 24_Hz; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); EXPECT_EQ(SetPolicyResult::Changed, - configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}})); + selector.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}})); lr.vote = LayerVoteType::Min; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr.vote = LayerVoteType::Max; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 90_Hz; lr.vote = LayerVoteType::Heuristic; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 60_Hz; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 45_Hz; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 30_Hz; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 24_Hz; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); EXPECT_EQ(SetPolicyResult::Changed, - configs.setDisplayManagerPolicy({kModeId60, {0_Hz, 120_Hz}})); + selector.setDisplayManagerPolicy({kModeId60, {0_Hz, 120_Hz}})); lr.vote = LayerVoteType::Min; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); lr.vote = LayerVoteType::Max; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 90_Hz; lr.vote = LayerVoteType::Heuristic; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 60_Hz; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 45_Hz; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 30_Hz; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 24_Hz; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); } -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_multipleThreshold_60_90) { - TestableRefreshRateConfigs configs(kModes_60_90, kModeId60, {.frameRateMultipleThreshold = 90}); +TEST_F(RefreshRateSelectorTest, getBestRefreshRate_multipleThreshold_60_90) { + TestableRefreshRateSelector selector(kModes_60_90, kModeId60, + {.frameRateMultipleThreshold = 90}); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& lr = layers[0]; lr.vote = LayerVoteType::Min; lr.name = "Min"; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); lr.vote = LayerVoteType::Max; lr.name = "Max"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 90_Hz; lr.vote = LayerVoteType::Heuristic; lr.name = "90Hz Heuristic"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 60_Hz; lr.name = "60Hz Heuristic"; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 45_Hz; lr.name = "45Hz Heuristic"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 30_Hz; lr.name = "30Hz Heuristic"; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 24_Hz; lr.name = "24Hz Heuristic"; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); } -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_72_90) { - TestableRefreshRateConfigs configs(kModes_60_72_90, kModeId60); +TEST_F(RefreshRateSelectorTest, getBestRefreshRate_60_72_90) { + TestableRefreshRateSelector selector(kModes_60_72_90, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& lr = layers[0]; lr.vote = LayerVoteType::Min; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); lr.vote = LayerVoteType::Max; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 90_Hz; lr.vote = LayerVoteType::Heuristic; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 60_Hz; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 45_Hz; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 30_Hz; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 24_Hz; - EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers)); } -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_72_90_120) { - TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60); +TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_72_90_120) { + TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; auto& lr1 = layers[0]; @@ -561,23 +563,23 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_72_90_120) { lr1.vote = LayerVoteType::Heuristic; lr2.desiredRefreshRate = 60_Hz; lr2.vote = LayerVoteType::Heuristic; - EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::Heuristic; lr2.desiredRefreshRate = 48_Hz; lr2.vote = LayerVoteType::Heuristic; - EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::Heuristic; lr2.desiredRefreshRate = 48_Hz; lr2.vote = LayerVoteType::Heuristic; - EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers)); } -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes) { - TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60); +TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_90_120_DifferentTypes) { + TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; auto& lr1 = layers[0]; @@ -589,7 +591,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes) { lr2.desiredRefreshRate = 60_Hz; lr2.vote = LayerVoteType::Heuristic; lr2.name = "60Hz Heuristic"; - EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -597,7 +599,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes) { lr2.desiredRefreshRate = 60_Hz; lr2.vote = LayerVoteType::Heuristic; lr2.name = "60Hz Heuristic"; - EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -605,7 +607,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes) { lr2.desiredRefreshRate = 60_Hz; lr2.vote = LayerVoteType::ExplicitDefault; lr2.name = "60Hz ExplicitDefault"; - EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -613,7 +615,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes) { lr2.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::Heuristic; lr2.name = "90Hz Heuristic"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -621,7 +623,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes) { lr2.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::ExplicitDefault; lr2.name = "90Hz Heuristic"; - EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitDefault; @@ -629,7 +631,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes) { lr2.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::Heuristic; lr2.name = "90Hz Heuristic"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::Heuristic; @@ -637,7 +639,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes) { lr2.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::ExplicitDefault; lr2.name = "90Hz ExplicitDefault"; - EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -645,7 +647,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes) { lr2.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::ExplicitDefault; lr2.name = "90Hz ExplicitDefault"; - EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitDefault; @@ -653,12 +655,12 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes) { lr2.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::ExplicitExactOrMultiple; lr2.name = "90Hz ExplicitExactOrMultiple"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); } -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_multipleThreshold) { - TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60, - {.frameRateMultipleThreshold = 120}); +TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_90_120_DifferentTypes_multipleThreshold) { + TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60, + {.frameRateMultipleThreshold = 120}); std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}, {.weight = 1.f}}; auto& lr1 = layers[0]; @@ -671,7 +673,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_mu lr2.desiredRefreshRate = 60_Hz; lr2.vote = LayerVoteType::Heuristic; lr2.name = "60Hz Heuristic"; - EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -679,7 +681,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_mu lr2.desiredRefreshRate = 60_Hz; lr2.vote = LayerVoteType::Heuristic; lr2.name = "60Hz Heuristic"; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -687,7 +689,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_mu lr2.desiredRefreshRate = 60_Hz; lr2.vote = LayerVoteType::ExplicitDefault; lr2.name = "60Hz ExplicitDefault"; - EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -695,7 +697,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_mu lr2.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::Heuristic; lr2.name = "90Hz Heuristic"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -703,7 +705,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_mu lr2.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::ExplicitDefault; lr2.name = "90Hz Heuristic"; - EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitDefault; @@ -711,7 +713,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_mu lr2.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::Heuristic; lr2.name = "90Hz Heuristic"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::Heuristic; @@ -719,7 +721,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_mu lr2.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::ExplicitDefault; lr2.name = "90Hz ExplicitDefault"; - EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -727,7 +729,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_mu lr2.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::ExplicitDefault; lr2.name = "90Hz ExplicitDefault"; - EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitDefault; @@ -735,14 +737,14 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_mu lr2.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::ExplicitExactOrMultiple; lr2.name = "90Hz ExplicitExactOrMultiple"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; lr1.name = "24Hz ExplicitExactOrMultiple"; lr2.vote = LayerVoteType::Max; lr2.name = "Max"; - EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -750,7 +752,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_mu lr2.desiredRefreshRate = 120_Hz; lr2.vote = LayerVoteType::ExplicitDefault; lr2.name = "120Hz ExplicitDefault"; - EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -758,7 +760,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_mu lr2.desiredRefreshRate = 120_Hz; lr2.vote = LayerVoteType::ExplicitExact; lr2.name = "120Hz ExplicitExact"; - EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 10_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -766,7 +768,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_mu lr2.desiredRefreshRate = 120_Hz; lr2.vote = LayerVoteType::Heuristic; lr2.name = "120Hz ExplicitExact"; - EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers)); lr1.desiredRefreshRate = 30_Hz; lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -777,86 +779,86 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_mu lr3.vote = LayerVoteType::Heuristic; lr3.desiredRefreshRate = 120_Hz; lr3.name = "120Hz Heuristic"; - EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers)); } -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60) { - TestableRefreshRateConfigs configs(kModes_30_60, kModeId60); +TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60) { + TestableRefreshRateSelector selector(kModes_30_60, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& lr = layers[0]; lr.vote = LayerVoteType::Min; - EXPECT_EQ(kMode30, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode30, selector.getBestRefreshRate(layers)); lr.vote = LayerVoteType::Max; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 90_Hz; lr.vote = LayerVoteType::Heuristic; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 60_Hz; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 45_Hz; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 30_Hz; - EXPECT_EQ(kMode30, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode30, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 24_Hz; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); } -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_72_90) { - TestableRefreshRateConfigs configs(kModes_30_60_72_90, kModeId60); +TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_72_90) { + TestableRefreshRateSelector selector(kModes_30_60_72_90, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& lr = layers[0]; lr.vote = LayerVoteType::Min; lr.name = "Min"; - EXPECT_EQ(kMode30, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode30, selector.getBestRefreshRate(layers)); lr.vote = LayerVoteType::Max; lr.name = "Max"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 90_Hz; lr.vote = LayerVoteType::Heuristic; lr.name = "90Hz Heuristic"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr.desiredRefreshRate = 60_Hz; lr.name = "60Hz Heuristic"; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true})); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true})); lr.desiredRefreshRate = 45_Hz; lr.name = "45Hz Heuristic"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true})); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true})); lr.desiredRefreshRate = 30_Hz; lr.name = "30Hz Heuristic"; - EXPECT_EQ(kMode30, configs.getBestRefreshRate(layers)); - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true})); + EXPECT_EQ(kMode30, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true})); lr.desiredRefreshRate = 24_Hz; lr.name = "24Hz Heuristic"; - EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers)); - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true})); + EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true})); lr.desiredRefreshRate = 24_Hz; lr.vote = LayerVoteType::ExplicitExactOrMultiple; lr.name = "24Hz ExplicitExactOrMultiple"; - EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers)); - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true})); + EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true})); } -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_PriorityTest) { - TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId60); +TEST_F(RefreshRateSelectorTest, getBestRefreshRate_PriorityTest) { + TestableRefreshRateSelector selector(kModes_30_60_90, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; auto& lr1 = layers[0]; @@ -864,43 +866,43 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_PriorityTest) { lr1.vote = LayerVoteType::Min; lr2.vote = LayerVoteType::Max; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr1.vote = LayerVoteType::Min; lr2.vote = LayerVoteType::Heuristic; lr2.desiredRefreshRate = 24_Hz; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); lr1.vote = LayerVoteType::Min; lr2.vote = LayerVoteType::ExplicitExactOrMultiple; lr2.desiredRefreshRate = 24_Hz; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); lr1.vote = LayerVoteType::Max; lr2.vote = LayerVoteType::Heuristic; lr2.desiredRefreshRate = 60_Hz; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr1.vote = LayerVoteType::Max; lr2.vote = LayerVoteType::ExplicitExactOrMultiple; lr2.desiredRefreshRate = 60_Hz; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr1.vote = LayerVoteType::Heuristic; lr1.desiredRefreshRate = 15_Hz; lr2.vote = LayerVoteType::Heuristic; lr2.desiredRefreshRate = 45_Hz; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr1.vote = LayerVoteType::Heuristic; lr1.desiredRefreshRate = 30_Hz; lr2.vote = LayerVoteType::ExplicitExactOrMultiple; lr2.desiredRefreshRate = 45_Hz; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); } -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_24FpsVideo) { - TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); +TEST_F(RefreshRateSelectorTest, getBestRefreshRate_24FpsVideo) { + TestableRefreshRateSelector selector(kModes_60_90, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& lr = layers[0]; @@ -908,15 +910,15 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_24FpsVideo) { lr.vote = LayerVoteType::ExplicitExactOrMultiple; for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) { lr.desiredRefreshRate = Fps::fromValue(fps); - const auto mode = configs.getBestRefreshRate(layers); + const auto mode = selector.getBestRefreshRate(layers); EXPECT_EQ(kMode60, mode) << lr.desiredRefreshRate << " chooses " << to_string(mode->getFps()); } } -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_24FpsVideo_multipleThreshold_60_120) { - TestableRefreshRateConfigs configs(kModes_60_120, kModeId60, - {.frameRateMultipleThreshold = 120}); +TEST_F(RefreshRateSelectorTest, getBestRefreshRate_24FpsVideo_multipleThreshold_60_120) { + TestableRefreshRateSelector selector(kModes_60_120, kModeId60, + {.frameRateMultipleThreshold = 120}); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& lr = layers[0]; @@ -924,14 +926,14 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_24FpsVideo_multipleThreshold_6 lr.vote = LayerVoteType::ExplicitExactOrMultiple; for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) { lr.desiredRefreshRate = Fps::fromValue(fps); - const auto mode = configs.getBestRefreshRate(layers); + const auto mode = selector.getBestRefreshRate(layers); EXPECT_EQ(kMode60, mode) << lr.desiredRefreshRate << " chooses " << to_string(mode->getFps()); } } -TEST_F(RefreshRateConfigsTest, twoModes_getBestRefreshRate_Explicit) { - TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); +TEST_F(RefreshRateSelectorTest, twoModes_getBestRefreshRate_Explicit) { + TestableRefreshRateSelector selector(kModes_60_90, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; auto& lr1 = layers[0]; @@ -941,23 +943,23 @@ TEST_F(RefreshRateConfigsTest, twoModes_getBestRefreshRate_Explicit) { lr1.desiredRefreshRate = 60_Hz; lr2.vote = LayerVoteType::ExplicitExactOrMultiple; lr2.desiredRefreshRate = 90_Hz; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr1.vote = LayerVoteType::ExplicitDefault; lr1.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::ExplicitExactOrMultiple; lr2.desiredRefreshRate = 60_Hz; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); lr1.vote = LayerVoteType::Heuristic; lr1.desiredRefreshRate = 90_Hz; lr2.vote = LayerVoteType::ExplicitExactOrMultiple; lr2.desiredRefreshRate = 60_Hz; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); } -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_75HzContent) { - TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); +TEST_F(RefreshRateSelectorTest, getBestRefreshRate_75HzContent) { + TestableRefreshRateSelector selector(kModes_60_90, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& lr = layers[0]; @@ -965,14 +967,14 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_75HzContent) { lr.vote = LayerVoteType::ExplicitExactOrMultiple; for (float fps = 75.0f; fps < 100.0f; fps += 0.1f) { lr.desiredRefreshRate = Fps::fromValue(fps); - const auto mode = configs.getBestRefreshRate(layers, {}); + const auto mode = selector.getBestRefreshRate(layers, {}); EXPECT_EQ(kMode90, mode) << lr.desiredRefreshRate << " chooses " << to_string(mode->getFps()); } } -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_Multiples) { - TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); +TEST_F(RefreshRateSelectorTest, getBestRefreshRate_Multiples) { + TestableRefreshRateSelector selector(kModes_60_90, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; auto& lr1 = layers[0]; @@ -984,7 +986,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_Multiples) { lr2.vote = LayerVoteType::Heuristic; lr2.desiredRefreshRate = 90_Hz; lr2.name = "90Hz Heuristic"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr1.vote = LayerVoteType::ExplicitExactOrMultiple; lr1.desiredRefreshRate = 60_Hz; @@ -992,14 +994,14 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_Multiples) { lr2.vote = LayerVoteType::ExplicitDefault; lr2.desiredRefreshRate = 90_Hz; lr2.name = "90Hz ExplicitDefault"; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); lr1.vote = LayerVoteType::ExplicitExactOrMultiple; lr1.desiredRefreshRate = 60_Hz; lr1.name = "60Hz ExplicitExactOrMultiple"; lr2.vote = LayerVoteType::Max; lr2.name = "Max"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr1.vote = LayerVoteType::ExplicitExactOrMultiple; lr1.desiredRefreshRate = 30_Hz; @@ -1007,18 +1009,18 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_Multiples) { lr2.vote = LayerVoteType::Heuristic; lr2.desiredRefreshRate = 90_Hz; lr2.name = "90Hz Heuristic"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr1.vote = LayerVoteType::ExplicitExactOrMultiple; lr1.desiredRefreshRate = 30_Hz; lr1.name = "30Hz ExplicitExactOrMultiple"; lr2.vote = LayerVoteType::Max; lr2.name = "Max"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); } -TEST_F(RefreshRateConfigsTest, scrollWhileWatching60fps_60_90) { - TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); +TEST_F(RefreshRateSelectorTest, scrollWhileWatching60fps_60_90) { + TestableRefreshRateSelector selector(kModes_60_90, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; auto& lr1 = layers[0]; @@ -1029,28 +1031,28 @@ TEST_F(RefreshRateConfigsTest, scrollWhileWatching60fps_60_90) { lr1.name = "60Hz ExplicitExactOrMultiple"; lr2.vote = LayerVoteType::NoVote; lr2.name = "NoVote"; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); lr1.vote = LayerVoteType::ExplicitExactOrMultiple; lr1.desiredRefreshRate = 60_Hz; lr1.name = "60Hz ExplicitExactOrMultiple"; lr2.vote = LayerVoteType::NoVote; lr2.name = "NoVote"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true})); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true})); lr1.vote = LayerVoteType::ExplicitExactOrMultiple; lr1.desiredRefreshRate = 60_Hz; lr1.name = "60Hz ExplicitExactOrMultiple"; lr2.vote = LayerVoteType::Max; lr2.name = "Max"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true})); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true})); lr1.vote = LayerVoteType::ExplicitExactOrMultiple; lr1.desiredRefreshRate = 60_Hz; lr1.name = "60Hz ExplicitExactOrMultiple"; lr2.vote = LayerVoteType::Max; lr2.name = "Max"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); // The other layer starts to provide buffers lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -1059,16 +1061,16 @@ TEST_F(RefreshRateConfigsTest, scrollWhileWatching60fps_60_90) { lr2.vote = LayerVoteType::Heuristic; lr2.desiredRefreshRate = 90_Hz; lr2.name = "90Hz Heuristic"; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); } -TEST_F(RefreshRateConfigsTest, getMaxRefreshRatesByPolicy) { +TEST_F(RefreshRateSelectorTest, getMaxRefreshRatesByPolicy) { // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the // different group. - TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId60); + TestableRefreshRateSelector selector(kModes_30_60_90, kModeId60); - const auto refreshRates = configs.rankRefreshRates(configs.getActiveMode().getGroup(), - RefreshRateOrder::Descending); + const auto refreshRates = selector.rankRefreshRates(selector.getActiveMode().getGroup(), + RefreshRateOrder::Descending); const std::array expectedRefreshRates = {kMode90, kMode60, kMode30}; ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size()); @@ -1080,13 +1082,13 @@ TEST_F(RefreshRateConfigsTest, getMaxRefreshRatesByPolicy) { } } -TEST_F(RefreshRateConfigsTest, getMinRefreshRatesByPolicy) { +TEST_F(RefreshRateSelectorTest, getMinRefreshRatesByPolicy) { // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the // different group. - TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId60); + TestableRefreshRateSelector selector(kModes_30_60_90, kModeId60); - const auto refreshRates = configs.rankRefreshRates(configs.getActiveMode().getGroup(), - RefreshRateOrder::Ascending); + const auto refreshRates = selector.rankRefreshRates(selector.getActiveMode().getGroup(), + RefreshRateOrder::Ascending); const std::array expectedRefreshRates = {kMode30, kMode60, kMode90}; ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size()); @@ -1098,16 +1100,16 @@ TEST_F(RefreshRateConfigsTest, getMinRefreshRatesByPolicy) { } } -TEST_F(RefreshRateConfigsTest, getMinRefreshRatesByPolicyOutsideTheGroup) { +TEST_F(RefreshRateSelectorTest, getMinRefreshRatesByPolicyOutsideTheGroup) { // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the // different group. - TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId72); + TestableRefreshRateSelector selector(kModes_30_60_90, kModeId72); EXPECT_EQ(SetPolicyResult::Changed, - configs.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}, {30_Hz, 90_Hz}})); + selector.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}})); const auto refreshRates = - configs.rankRefreshRates(/*anchorGroupOpt*/ std::nullopt, RefreshRateOrder::Ascending); + selector.rankRefreshRates(/*anchorGroupOpt*/ std::nullopt, RefreshRateOrder::Ascending); const std::array expectedRefreshRates = {kMode30, kMode60, kMode90}; ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size()); @@ -1119,16 +1121,16 @@ TEST_F(RefreshRateConfigsTest, getMinRefreshRatesByPolicyOutsideTheGroup) { } } -TEST_F(RefreshRateConfigsTest, getMaxRefreshRatesByPolicyOutsideTheGroup) { +TEST_F(RefreshRateSelectorTest, getMaxRefreshRatesByPolicyOutsideTheGroup) { // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the // different group. - TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId72); + TestableRefreshRateSelector selector(kModes_30_60_90, kModeId72); EXPECT_EQ(SetPolicyResult::Changed, - configs.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}, {30_Hz, 90_Hz}})); + selector.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}})); - const auto refreshRates = - configs.rankRefreshRates(/*anchorGroupOpt*/ std::nullopt, RefreshRateOrder::Descending); + const auto refreshRates = selector.rankRefreshRates(/*anchorGroupOpt*/ std::nullopt, + RefreshRateOrder::Descending); const std::array expectedRefreshRates = {kMode90, kMode60, kMode30}; ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size()); @@ -1140,10 +1142,10 @@ TEST_F(RefreshRateConfigsTest, getMaxRefreshRatesByPolicyOutsideTheGroup) { } } -TEST_F(RefreshRateConfigsTest, powerOnImminentConsidered) { - TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); +TEST_F(RefreshRateSelectorTest, powerOnImminentConsidered) { + TestableRefreshRateSelector selector(kModes_60_90, kModeId60); - auto [refreshRates, signals] = configs.getRankedRefreshRates({}, {}); + auto [refreshRates, signals] = selector.getRankedRefreshRates({}, {}); EXPECT_FALSE(signals.powerOnImminent); std::array expectedRefreshRates = {kMode90, kMode60}; @@ -1156,7 +1158,7 @@ TEST_F(RefreshRateConfigsTest, powerOnImminentConsidered) { } std::tie(refreshRates, signals) = - configs.getRankedRefreshRatesAsPair({}, {.powerOnImminent = true}); + selector.getRankedRefreshRatesAsPair({}, {.powerOnImminent = true}); EXPECT_TRUE(signals.powerOnImminent); ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size()); @@ -1174,7 +1176,7 @@ TEST_F(RefreshRateConfigsTest, powerOnImminentConsidered) { lr1.name = "60Hz ExplicitExactOrMultiple"; std::tie(refreshRates, signals) = - configs.getRankedRefreshRatesAsPair(layers, {.powerOnImminent = true}); + selector.getRankedRefreshRatesAsPair(layers, {.powerOnImminent = true}); EXPECT_TRUE(signals.powerOnImminent); ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size()); @@ -1186,7 +1188,7 @@ TEST_F(RefreshRateConfigsTest, powerOnImminentConsidered) { } std::tie(refreshRates, signals) = - configs.getRankedRefreshRatesAsPair(layers, {.powerOnImminent = false}); + selector.getRankedRefreshRatesAsPair(layers, {.powerOnImminent = false}); EXPECT_FALSE(signals.powerOnImminent); expectedRefreshRates = {kMode60, kMode90}; @@ -1199,13 +1201,13 @@ TEST_F(RefreshRateConfigsTest, powerOnImminentConsidered) { } } -TEST_F(RefreshRateConfigsTest, touchConsidered) { - TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); +TEST_F(RefreshRateSelectorTest, touchConsidered) { + TestableRefreshRateSelector selector(kModes_60_90, kModeId60); - auto [_, signals] = configs.getRankedRefreshRates({}, {}); + auto [_, signals] = selector.getRankedRefreshRates({}, {}); EXPECT_FALSE(signals.touch); - std::tie(std::ignore, signals) = configs.getRankedRefreshRatesAsPair({}, {.touch = true}); + std::tie(std::ignore, signals) = selector.getRankedRefreshRatesAsPair({}, {.touch = true}); EXPECT_TRUE(signals.touch); std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; @@ -1218,7 +1220,7 @@ TEST_F(RefreshRateConfigsTest, touchConsidered) { lr2.vote = LayerVoteType::Heuristic; lr2.desiredRefreshRate = 60_Hz; lr2.name = "60Hz Heuristic"; - std::tie(std::ignore, signals) = configs.getRankedRefreshRatesAsPair(layers, {.touch = true}); + std::tie(std::ignore, signals) = selector.getRankedRefreshRatesAsPair(layers, {.touch = true}); EXPECT_TRUE(signals.touch); lr1.vote = LayerVoteType::ExplicitDefault; @@ -1227,7 +1229,7 @@ TEST_F(RefreshRateConfigsTest, touchConsidered) { lr2.vote = LayerVoteType::Heuristic; lr2.desiredRefreshRate = 60_Hz; lr2.name = "60Hz Heuristic"; - std::tie(std::ignore, signals) = configs.getRankedRefreshRatesAsPair(layers, {.touch = true}); + std::tie(std::ignore, signals) = selector.getRankedRefreshRatesAsPair(layers, {.touch = true}); EXPECT_FALSE(signals.touch); lr1.vote = LayerVoteType::ExplicitExactOrMultiple; @@ -1236,7 +1238,7 @@ TEST_F(RefreshRateConfigsTest, touchConsidered) { lr2.vote = LayerVoteType::Heuristic; lr2.desiredRefreshRate = 60_Hz; lr2.name = "60Hz Heuristic"; - std::tie(std::ignore, signals) = configs.getRankedRefreshRatesAsPair(layers, {.touch = true}); + std::tie(std::ignore, signals) = selector.getRankedRefreshRatesAsPair(layers, {.touch = true}); EXPECT_TRUE(signals.touch); lr1.vote = LayerVoteType::ExplicitDefault; @@ -1245,12 +1247,12 @@ TEST_F(RefreshRateConfigsTest, touchConsidered) { lr2.vote = LayerVoteType::Heuristic; lr2.desiredRefreshRate = 60_Hz; lr2.name = "60Hz Heuristic"; - std::tie(std::ignore, signals) = configs.getRankedRefreshRatesAsPair(layers, {.touch = true}); + std::tie(std::ignore, signals) = selector.getRankedRefreshRatesAsPair(layers, {.touch = true}); EXPECT_FALSE(signals.touch); } -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitDefault) { - TestableRefreshRateConfigs configs(kModes_60_90_72_120, kModeId60); +TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ExplicitDefault) { + TestableRefreshRateSelector selector(kModes_60_90_72_120, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& lr = layers[0]; @@ -1282,57 +1284,57 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitDefault) { ss << "ExplicitDefault " << desired; lr.name = ss.str(); - EXPECT_EQ(expected, configs.getBestRefreshRate(layers)->getFps()); + EXPECT_EQ(expected, selector.getBestRefreshRate(layers)->getFps()); } } -TEST_F(RefreshRateConfigsTest, +TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ExplicitExactOrMultiple_WithFractionalRefreshRates) { std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& lr = layers[0]; // Test that 23.976 will choose 24 if 23.976 is not supported { - TestableRefreshRateConfigs configs(makeModes(kMode24, kMode25, kMode30, kMode30Frac, - kMode60, kMode60Frac), - kModeId60); + TestableRefreshRateSelector selector(makeModes(kMode24, kMode25, kMode30, kMode30Frac, + kMode60, kMode60Frac), + kModeId60); lr.vote = LayerVoteType::ExplicitExactOrMultiple; lr.desiredRefreshRate = 23.976_Hz; lr.name = "ExplicitExactOrMultiple 23.976 Hz"; - EXPECT_EQ(kModeId24, configs.getBestRefreshRate(layers)->getId()); + EXPECT_EQ(kModeId24, selector.getBestRefreshRate(layers)->getId()); } // Test that 24 will choose 23.976 if 24 is not supported { - TestableRefreshRateConfigs configs(makeModes(kMode24Frac, kMode25, kMode30, kMode30Frac, - kMode60, kMode60Frac), - kModeId60); + TestableRefreshRateSelector selector(makeModes(kMode24Frac, kMode25, kMode30, kMode30Frac, + kMode60, kMode60Frac), + kModeId60); lr.desiredRefreshRate = 24_Hz; lr.name = "ExplicitExactOrMultiple 24 Hz"; - EXPECT_EQ(kModeId24Frac, configs.getBestRefreshRate(layers)->getId()); + EXPECT_EQ(kModeId24Frac, selector.getBestRefreshRate(layers)->getId()); } // Test that 29.97 will prefer 59.94 over 60 and 30 { - TestableRefreshRateConfigs configs(makeModes(kMode24, kMode24Frac, kMode25, kMode30, - kMode60, kMode60Frac), - kModeId60); + TestableRefreshRateSelector selector(makeModes(kMode24, kMode24Frac, kMode25, kMode30, + kMode60, kMode60Frac), + kModeId60); lr.desiredRefreshRate = 29.97_Hz; lr.name = "ExplicitExactOrMultiple 29.97 Hz"; - EXPECT_EQ(kModeId60Frac, configs.getBestRefreshRate(layers)->getId()); + EXPECT_EQ(kModeId60Frac, selector.getBestRefreshRate(layers)->getId()); } } -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExact_WithFractionalRefreshRates) { +TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ExplicitExact_WithFractionalRefreshRates) { std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& lr = layers[0]; // Test that voting for supported refresh rate will select this refresh rate { - TestableRefreshRateConfigs configs(kModes_24_25_30_50_60_Frac, kModeId60); + TestableRefreshRateSelector selector(kModes_24_25_30_50_60_Frac, kModeId60); for (auto desired : {23.976_Hz, 24_Hz, 25_Hz, 29.97_Hz, 30_Hz, 50_Hz, 59.94_Hz, 60_Hz}) { lr.vote = LayerVoteType::ExplicitExact; @@ -1341,17 +1343,19 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExact_WithFractionalRe ss << "ExplicitExact " << desired; lr.name = ss.str(); - EXPECT_EQ(lr.desiredRefreshRate, configs.getBestRefreshRate(layers)->getFps()); + EXPECT_EQ(lr.desiredRefreshRate, selector.getBestRefreshRate(layers)->getFps()); } } } -TEST_F(RefreshRateConfigsTest, +TEST_F(RefreshRateSelectorTest, getBestRefreshRate_withDisplayManagerRequestingSingleRate_ignoresTouchFlag) { - TestableRefreshRateConfigs configs(kModes_60_90, kModeId90); + TestableRefreshRateSelector selector(kModes_60_90, kModeId90); + constexpr FpsRange k90 = {90_Hz, 90_Hz}; + constexpr FpsRange k60_90 = {60_Hz, 90_Hz}; EXPECT_EQ(SetPolicyResult::Changed, - configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}})); + selector.setDisplayManagerPolicy({kModeId90, {k90, k90}, {k60_90, k60_90}})); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& lr = layers[0]; @@ -1362,18 +1366,21 @@ TEST_F(RefreshRateConfigsTest, lr.focused = true; const auto [mode, signals] = - configs.getRankedRefreshRates(layers, {.touch = true, .idle = true}); + selector.getRankedRefreshRates(layers, {.touch = true, .idle = true}); EXPECT_EQ(mode.begin()->modePtr, kMode60); EXPECT_FALSE(signals.touch); } -TEST_F(RefreshRateConfigsTest, +TEST_F(RefreshRateSelectorTest, getBestRefreshRate_withDisplayManagerRequestingSingleRate_ignoresIdleFlag) { - TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); + TestableRefreshRateSelector selector(kModes_60_90, kModeId60); + + constexpr FpsRange k60 = {60_Hz, 60_Hz}; + constexpr FpsRange k60_90 = {60_Hz, 90_Hz}; EXPECT_EQ(SetPolicyResult::Changed, - configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}, {60_Hz, 90_Hz}})); + selector.setDisplayManagerPolicy({kModeId60, {k60, k60}, {k60_90, k60_90}})); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& lr = layers[0]; @@ -1382,11 +1389,11 @@ TEST_F(RefreshRateConfigsTest, lr.desiredRefreshRate = 90_Hz; lr.name = "90Hz ExplicitDefault"; lr.focused = true; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.idle = true})); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.idle = true})); } -TEST_F(RefreshRateConfigsTest, testDisplayModeOrdering) { - TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60); +TEST_F(RefreshRateSelectorTest, testDisplayModeOrdering) { + TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}, @@ -1420,7 +1427,7 @@ TEST_F(RefreshRateConfigsTest, testDisplayModeOrdering) { lr5.focused = true; std::array expectedRanking = {kMode120, kMode90, kMode72, kMode60, kMode30}; - auto actualRanking = configs.getRankedRefreshRates(layers, {}).ranking; + auto actualRanking = selector.getRankedRefreshRates(layers, {}).ranking; ASSERT_EQ(expectedRanking.size(), actualRanking.size()); @@ -1446,7 +1453,7 @@ TEST_F(RefreshRateConfigsTest, testDisplayModeOrdering) { lr5.name = "120Hz"; expectedRanking = {kMode120, kMode90, kMode72, kMode60, kMode30}; - actualRanking = configs.getRankedRefreshRates(layers, {}).ranking; + actualRanking = selector.getRankedRefreshRates(layers, {}).ranking; ASSERT_EQ(expectedRanking.size(), actualRanking.size()); @@ -1470,7 +1477,7 @@ TEST_F(RefreshRateConfigsTest, testDisplayModeOrdering) { lr5.name = "72Hz"; expectedRanking = {kMode30, kMode60, kMode90, kMode120, kMode72}; - actualRanking = configs.getRankedRefreshRates(layers, {}).ranking; + actualRanking = selector.getRankedRefreshRates(layers, {}).ranking; ASSERT_EQ(expectedRanking.size(), actualRanking.size()); @@ -1497,7 +1504,7 @@ TEST_F(RefreshRateConfigsTest, testDisplayModeOrdering) { lr5.name = "120Hz-2"; expectedRanking = {kMode90, kMode60, kMode120, kMode72, kMode30}; - actualRanking = configs.getRankedRefreshRates(layers, {}).ranking; + actualRanking = selector.getRankedRefreshRates(layers, {}).ranking; ASSERT_EQ(expectedRanking.size(), actualRanking.size()); @@ -1508,14 +1515,17 @@ TEST_F(RefreshRateConfigsTest, testDisplayModeOrdering) { } } -TEST_F(RefreshRateConfigsTest, +TEST_F(RefreshRateSelectorTest, getBestRefreshRate_withDisplayManagerRequestingSingleRate_onlySwitchesRatesForExplicitFocusedLayers) { - TestableRefreshRateConfigs configs(kModes_60_90, kModeId90); + TestableRefreshRateSelector selector(kModes_60_90, kModeId90); + + constexpr FpsRange k90 = {90_Hz, 90_Hz}; + constexpr FpsRange k60_90 = {60_Hz, 90_Hz}; EXPECT_EQ(SetPolicyResult::Changed, - configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}})); + selector.setDisplayManagerPolicy({kModeId90, {k90, k90}, {k60_90, k60_90}})); - const auto [ranking, signals] = configs.getRankedRefreshRates({}, {}); + const auto [ranking, signals] = selector.getRankedRefreshRates({}, {}); EXPECT_EQ(ranking.front().modePtr, kMode90); EXPECT_FALSE(signals.touch); @@ -1526,50 +1536,50 @@ TEST_F(RefreshRateConfigsTest, lr.desiredRefreshRate = 60_Hz; lr.name = "60Hz ExplicitExactOrMultiple"; lr.focused = false; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr.focused = true; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr.vote = LayerVoteType::ExplicitDefault; lr.desiredRefreshRate = 60_Hz; lr.name = "60Hz ExplicitDefault"; lr.focused = false; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr.focused = true; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); lr.vote = LayerVoteType::Heuristic; lr.desiredRefreshRate = 60_Hz; lr.name = "60Hz Heuristic"; lr.focused = false; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr.focused = true; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr.vote = LayerVoteType::Max; lr.desiredRefreshRate = 60_Hz; lr.name = "60Hz Max"; lr.focused = false; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr.focused = true; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr.vote = LayerVoteType::Min; lr.desiredRefreshRate = 60_Hz; lr.name = "60Hz Min"; lr.focused = false; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); lr.focused = true; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); } -TEST_F(RefreshRateConfigsTest, groupSwitchingNotAllowed) { - TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60); +TEST_F(RefreshRateSelectorTest, groupSwitchingNotAllowed) { + TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60); // The default policy doesn't allow group switching. Verify that no // group switches are performed. @@ -1581,16 +1591,16 @@ TEST_F(RefreshRateConfigsTest, groupSwitchingNotAllowed) { layer.name = "90Hz ExplicitDefault"; layer.focused = true; - EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers)->getId()); + EXPECT_EQ(kModeId60, selector.getBestRefreshRate(layers)->getId()); } -TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayer) { - TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60); +TEST_F(RefreshRateSelectorTest, groupSwitchingWithOneLayer) { + TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60); - RefreshRateConfigs::DisplayManagerPolicy policy; - policy.defaultMode = configs.getCurrentPolicy().defaultMode; + RefreshRateSelector::DisplayManagerPolicy policy; + policy.defaultMode = selector.getCurrentPolicy().defaultMode; policy.allowGroupSwitching = true; - EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy)); + EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy)); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& layer = layers[0]; @@ -1599,16 +1609,16 @@ TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayer) { layer.seamlessness = Seamlessness::SeamedAndSeamless; layer.name = "90Hz ExplicitDefault"; layer.focused = true; - EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers)->getId()); + EXPECT_EQ(kModeId90, selector.getBestRefreshRate(layers)->getId()); } -TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamless) { - TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60); +TEST_F(RefreshRateSelectorTest, groupSwitchingWithOneLayerOnlySeamless) { + TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60); - RefreshRateConfigs::DisplayManagerPolicy policy; - policy.defaultMode = configs.getCurrentPolicy().defaultMode; + RefreshRateSelector::DisplayManagerPolicy policy; + policy.defaultMode = selector.getCurrentPolicy().defaultMode; policy.allowGroupSwitching = true; - EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy)); + EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy)); // Verify that we won't change the group if seamless switch is required. std::vector<LayerRequirement> layers = {{.weight = 1.f}}; @@ -1618,18 +1628,18 @@ TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamless) { layer.seamlessness = Seamlessness::OnlySeamless; layer.name = "90Hz ExplicitDefault"; layer.focused = true; - EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers)->getId()); + EXPECT_EQ(kModeId60, selector.getBestRefreshRate(layers)->getId()); } -TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamlessDefaultFps) { - TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60); +TEST_F(RefreshRateSelectorTest, groupSwitchingWithOneLayerOnlySeamlessDefaultFps) { + TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60); - RefreshRateConfigs::DisplayManagerPolicy policy; - policy.defaultMode = configs.getCurrentPolicy().defaultMode; + RefreshRateSelector::DisplayManagerPolicy policy; + policy.defaultMode = selector.getCurrentPolicy().defaultMode; policy.allowGroupSwitching = true; - EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy)); + EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy)); - configs.setActiveModeId(kModeId90); + selector.setActiveModeId(kModeId90); // Verify that we won't do a seamless switch if we request the same mode as the default std::vector<LayerRequirement> layers = {{.weight = 1.f}}; @@ -1639,21 +1649,21 @@ TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamlessDefaultFps) layer.seamlessness = Seamlessness::OnlySeamless; layer.name = "60Hz ExplicitDefault"; layer.focused = true; - EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers)->getId()); + EXPECT_EQ(kModeId90, selector.getBestRefreshRate(layers)->getId()); } -TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerDefaultSeamlessness) { - TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60); +TEST_F(RefreshRateSelectorTest, groupSwitchingWithOneLayerDefaultSeamlessness) { + TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60); - RefreshRateConfigs::DisplayManagerPolicy policy; - policy.defaultMode = configs.getCurrentPolicy().defaultMode; + RefreshRateSelector::DisplayManagerPolicy policy; + policy.defaultMode = selector.getCurrentPolicy().defaultMode; policy.allowGroupSwitching = true; - EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy)); + EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy)); - configs.setActiveModeId(kModeId90); + selector.setActiveModeId(kModeId90); - // Verify that if the current config is in another group and there are no layers with - // seamlessness=SeamedAndSeamless we'll go back to the default group. + // Verify that if the active mode is in another group and there are no layers with + // Seamlessness::SeamedAndSeamless, we should switch back to the default group. std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& layer = layers[0]; @@ -1663,21 +1673,21 @@ TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerDefaultSeamlessness) { layer.name = "60Hz ExplicitDefault"; layer.focused = true; - EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers)->getId()); + EXPECT_EQ(kModeId60, selector.getBestRefreshRate(layers)->getId()); } -TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersOnlySeamlessAndSeamed) { - TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60); +TEST_F(RefreshRateSelectorTest, groupSwitchingWithTwoLayersOnlySeamlessAndSeamed) { + TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60); - RefreshRateConfigs::DisplayManagerPolicy policy; - policy.defaultMode = configs.getCurrentPolicy().defaultMode; + RefreshRateSelector::DisplayManagerPolicy policy; + policy.defaultMode = selector.getCurrentPolicy().defaultMode; policy.allowGroupSwitching = true; - EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy)); + EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy)); - configs.setActiveModeId(kModeId90); + selector.setActiveModeId(kModeId90); - // If there's a layer with seamlessness=SeamedAndSeamless, another layer with - // seamlessness=OnlySeamless can't change the mode group. + // If there's a layer with Seamlessness::SeamedAndSeamless, another layer with + // Seamlessness::OnlySeamless can't change the mode group. std::vector<LayerRequirement> layers = {{.weight = 1.f}}; layers[0].vote = LayerVoteType::ExplicitDefault; layers[0].desiredRefreshRate = 60_Hz; @@ -1692,21 +1702,21 @@ TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersOnlySeamlessAndSeamed) layers[1].name = "90Hz ExplicitDefault"; layers[1].focused = false; - EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers)->getId()); + EXPECT_EQ(kModeId90, selector.getBestRefreshRate(layers)->getId()); } -TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultFocusedAndSeamed) { - TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60); +TEST_F(RefreshRateSelectorTest, groupSwitchingWithTwoLayersDefaultFocusedAndSeamed) { + TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60); - RefreshRateConfigs::DisplayManagerPolicy policy; - policy.defaultMode = configs.getCurrentPolicy().defaultMode; + RefreshRateSelector::DisplayManagerPolicy policy; + policy.defaultMode = selector.getCurrentPolicy().defaultMode; policy.allowGroupSwitching = true; - EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy)); + EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy)); - configs.setActiveModeId(kModeId90); + selector.setActiveModeId(kModeId90); - // If there's a focused layer with seamlessness=SeamedAndSeamless, another layer with - // seamlessness=Default can't change the mode group back to the group of the default + // If there's a focused layer with Seamlessness::SeamedAndSeamless, another layer with + // Seamlessness::Default can't change the mode group back to the group of the default // mode. // For example, this may happen when a video playback requests and gets a seamed switch, // but another layer (with default seamlessness) starts animating. The animating layer @@ -1725,21 +1735,21 @@ TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultFocusedAndSeame layers[1].vote = LayerVoteType::ExplicitDefault; layers[1].name = "90Hz ExplicitDefault"; - EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers)->getId()); + EXPECT_EQ(kModeId90, selector.getBestRefreshRate(layers)->getId()); } -TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultNotFocusedAndSeamed) { - TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60); +TEST_F(RefreshRateSelectorTest, groupSwitchingWithTwoLayersDefaultNotFocusedAndSeamed) { + TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60); - RefreshRateConfigs::DisplayManagerPolicy policy; - policy.defaultMode = configs.getCurrentPolicy().defaultMode; + RefreshRateSelector::DisplayManagerPolicy policy; + policy.defaultMode = selector.getCurrentPolicy().defaultMode; policy.allowGroupSwitching = true; - EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy)); + EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy)); - configs.setActiveModeId(kModeId90); + selector.setActiveModeId(kModeId90); - // Layer with seamlessness=Default can change the mode group if there's a not - // focused layer with seamlessness=SeamedAndSeamless. This happens for example, + // Layer with Seamlessness::Default can change the mode group if there's an + // unfocused layer with Seamlessness::SeamedAndSeamless. For example, this happens // when in split screen mode the user switches between the two visible applications. std::vector<LayerRequirement> layers = {{.weight = 1.f}}; layers[0].seamlessness = Seamlessness::Default; @@ -1755,17 +1765,17 @@ TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultNotFocusedAndSe layers[1].vote = LayerVoteType::ExplicitDefault; layers[1].name = "90Hz ExplicitDefault"; - EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers)->getId()); + EXPECT_EQ(kModeId60, selector.getBestRefreshRate(layers)->getId()); } -TEST_F(RefreshRateConfigsTest, nonSeamlessVotePrefersSeamlessSwitches) { - TestableRefreshRateConfigs configs(kModes_30_60, kModeId60); +TEST_F(RefreshRateSelectorTest, nonSeamlessVotePrefersSeamlessSwitches) { + TestableRefreshRateSelector selector(kModes_30_60, kModeId60); // Allow group switching. - RefreshRateConfigs::DisplayManagerPolicy policy; - policy.defaultMode = configs.getCurrentPolicy().defaultMode; + RefreshRateSelector::DisplayManagerPolicy policy; + policy.defaultMode = selector.getCurrentPolicy().defaultMode; policy.allowGroupSwitching = true; - EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy)); + EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy)); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; auto& layer = layers[0]; @@ -1775,20 +1785,20 @@ TEST_F(RefreshRateConfigsTest, nonSeamlessVotePrefersSeamlessSwitches) { layer.name = "60Hz ExplicitExactOrMultiple"; layer.focused = true; - EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers)->getId()); + EXPECT_EQ(kModeId60, selector.getBestRefreshRate(layers)->getId()); - configs.setActiveModeId(kModeId120); - EXPECT_EQ(kModeId120, configs.getBestRefreshRate(layers)->getId()); + selector.setActiveModeId(kModeId120); + EXPECT_EQ(kModeId120, selector.getBestRefreshRate(layers)->getId()); } -TEST_F(RefreshRateConfigsTest, nonSeamlessExactAndSeamlessMultipleLayers) { - TestableRefreshRateConfigs configs(kModes_25_30_50_60, kModeId60); +TEST_F(RefreshRateSelectorTest, nonSeamlessExactAndSeamlessMultipleLayers) { + TestableRefreshRateSelector selector(kModes_25_30_50_60, kModeId60); // Allow group switching. - RefreshRateConfigs::DisplayManagerPolicy policy; - policy.defaultMode = configs.getCurrentPolicy().defaultMode; + RefreshRateSelector::DisplayManagerPolicy policy; + policy.defaultMode = selector.getCurrentPolicy().defaultMode; policy.allowGroupSwitching = true; - EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy)); + EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy)); std::vector<LayerRequirement> layers = {{.name = "60Hz ExplicitDefault", .vote = LayerVoteType::ExplicitDefault, @@ -1803,33 +1813,33 @@ TEST_F(RefreshRateConfigsTest, nonSeamlessExactAndSeamlessMultipleLayers) { .weight = 1.f, .focused = true}}; - EXPECT_EQ(kModeId50, configs.getBestRefreshRate(layers)->getId()); + EXPECT_EQ(kModeId50, selector.getBestRefreshRate(layers)->getId()); auto& seamedLayer = layers[0]; seamedLayer.desiredRefreshRate = 30_Hz; seamedLayer.name = "30Hz ExplicitDefault"; - configs.setActiveModeId(kModeId30); + selector.setActiveModeId(kModeId30); - EXPECT_EQ(kModeId25, configs.getBestRefreshRate(layers)->getId()); + EXPECT_EQ(kModeId25, selector.getBestRefreshRate(layers)->getId()); } -TEST_F(RefreshRateConfigsTest, minLayersDontTrigerSeamedSwitch) { - TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId90); +TEST_F(RefreshRateSelectorTest, minLayersDontTrigerSeamedSwitch) { + TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId90); // Allow group switching. - RefreshRateConfigs::DisplayManagerPolicy policy; - policy.defaultMode = configs.getCurrentPolicy().defaultMode; + RefreshRateSelector::DisplayManagerPolicy policy; + policy.defaultMode = selector.getCurrentPolicy().defaultMode; policy.allowGroupSwitching = true; - EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy)); + EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy)); std::vector<LayerRequirement> layers = { {.name = "Min", .vote = LayerVoteType::Min, .weight = 1.f, .focused = true}}; - EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers)->getId()); + EXPECT_EQ(kModeId90, selector.getBestRefreshRate(layers)->getId()); } -TEST_F(RefreshRateConfigsTest, primaryVsAppRequestPolicy) { - TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId60); +TEST_F(RefreshRateSelectorTest, primaryVsAppRequestPolicy) { + TestableRefreshRateSelector selector(kModes_30_60_90, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; layers[0].name = "Test layer"; @@ -1839,19 +1849,22 @@ TEST_F(RefreshRateConfigsTest, primaryVsAppRequestPolicy) { bool focused = true; }; - // Return the config ID from calling getBestRefreshRate() for a single layer with the - // given voteType and fps. - auto getFrameRate = [&](LayerVoteType voteType, Fps fps, Args args = {}) -> DisplayModeId { + // Returns the mode selected by getBestRefreshRate for a single layer with the given arguments. + const auto getFrameRate = [&](LayerVoteType voteType, Fps fps, + Args args = {}) -> DisplayModeId { layers[0].vote = voteType; layers[0].desiredRefreshRate = fps; layers[0].focused = args.focused; - return configs.getBestRefreshRate(layers, {.touch = args.touch})->getId(); + return selector.getBestRefreshRate(layers, {.touch = args.touch})->getId(); }; + constexpr FpsRange k30_60 = {30_Hz, 60_Hz}; + constexpr FpsRange k30_90 = {30_Hz, 90_Hz}; + EXPECT_EQ(SetPolicyResult::Changed, - configs.setDisplayManagerPolicy({kModeId60, {30_Hz, 60_Hz}, {30_Hz, 90_Hz}})); + selector.setDisplayManagerPolicy({kModeId60, {k30_60, k30_60}, {k30_90, k30_90}})); - EXPECT_EQ(kModeId60, configs.getBestRefreshRate()->getId()); + EXPECT_EQ(kModeId60, selector.getBestRefreshRate()->getId()); EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::NoVote, 90_Hz)); EXPECT_EQ(kModeId30, getFrameRate(LayerVoteType::Min, 90_Hz)); EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Max, 90_Hz)); @@ -1859,7 +1872,7 @@ TEST_F(RefreshRateConfigsTest, primaryVsAppRequestPolicy) { EXPECT_EQ(kModeId90, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz)); EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz)); - // Unfocused layers are not allowed to override primary config. + // Unfocused layers are not allowed to override primary range. EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz, {.focused = false})); EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz, {.focused = false})); @@ -1874,7 +1887,7 @@ TEST_F(RefreshRateConfigsTest, primaryVsAppRequestPolicy) { getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz, {.touch = true})); EXPECT_EQ(SetPolicyResult::Changed, - configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}, {60_Hz, 60_Hz}})); + selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}})); EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::NoVote, 90_Hz)); EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Min, 90_Hz)); @@ -1884,8 +1897,8 @@ TEST_F(RefreshRateConfigsTest, primaryVsAppRequestPolicy) { EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz)); } -TEST_F(RefreshRateConfigsTest, idle) { - TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); +TEST_F(RefreshRateSelectorTest, idle) { + TestableRefreshRateSelector selector(kModes_60_90, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; layers[0].name = "Test layer"; @@ -1895,7 +1908,7 @@ TEST_F(RefreshRateConfigsTest, idle) { layers[0].desiredRefreshRate = 90_Hz; const auto [ranking, signals] = - configs.getRankedRefreshRates(layers, {.touch = touchActive, .idle = true}); + selector.getRankedRefreshRates(layers, {.touch = touchActive, .idle = true}); // Refresh rate will be chosen by either touch state or idle state. EXPECT_EQ(!touchActive, signals.idle); @@ -1903,7 +1916,7 @@ TEST_F(RefreshRateConfigsTest, idle) { }; EXPECT_EQ(SetPolicyResult::Changed, - configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 90_Hz}, {60_Hz, 90_Hz}})); + selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 90_Hz}})); // Idle should be lower priority than touch boost. { @@ -1918,10 +1931,10 @@ TEST_F(RefreshRateConfigsTest, idle) { } // With no layers, idle should still be lower priority than touch boost. - EXPECT_EQ(kModeId90, configs.getBestRefreshRate({}, {.touch = true, .idle = true})->getId()); + EXPECT_EQ(kModeId90, selector.getBestRefreshRate({}, {.touch = true, .idle = true})->getId()); // Idle should be higher precedence than other layer frame rate considerations. - configs.setActiveModeId(kModeId90); + selector.setActiveModeId(kModeId90); { constexpr bool kTouchActive = false; @@ -1934,15 +1947,15 @@ TEST_F(RefreshRateConfigsTest, idle) { getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, kTouchActive)); } - // Idle should be applied rather than the current config when there are no layers. - EXPECT_EQ(kModeId60, configs.getBestRefreshRate({}, {.idle = true})->getId()); + // Idle should be applied rather than the active mode when there are no layers. + EXPECT_EQ(kModeId60, selector.getBestRefreshRate({}, {.idle = true})->getId()); } -TEST_F(RefreshRateConfigsTest, findClosestKnownFrameRate) { - TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); +TEST_F(RefreshRateSelectorTest, findClosestKnownFrameRate) { + TestableRefreshRateSelector selector(kModes_60_90, kModeId60); for (float fps = 1.0f; fps <= 120.0f; fps += 0.1f) { - const auto knownFrameRate = configs.findClosestKnownFrameRate(Fps::fromValue(fps)); + const auto knownFrameRate = selector.findClosestKnownFrameRate(Fps::fromValue(fps)); const Fps expectedFrameRate = [fps] { if (fps < 26.91f) return 24_Hz; if (fps < 37.51f) return 30_Hz; @@ -1956,8 +1969,8 @@ TEST_F(RefreshRateConfigsTest, findClosestKnownFrameRate) { } } -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_KnownFrameRate) { - TestableRefreshRateConfigs configs(kModes_60_90, kModeId60); +TEST_F(RefreshRateSelectorTest, getBestRefreshRate_KnownFrameRate) { + TestableRefreshRateSelector selector(kModes_60_90, kModeId60); struct Expectation { Fps fps; @@ -1970,7 +1983,7 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_KnownFrameRate) { }; // Make sure the test tests all the known frame rate - const auto& knownFrameRates = configs.knownFrameRates(); + const auto& knownFrameRates = selector.knownFrameRates(); const bool equal = std::equal(knownFrameRates.begin(), knownFrameRates.end(), knownFrameRatesExpectations.begin(), [](Fps fps, const Expectation& expected) { @@ -1984,12 +1997,12 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_KnownFrameRate) { for (const auto& [fps, mode] : knownFrameRatesExpectations) { layer.desiredRefreshRate = fps; - EXPECT_EQ(mode, configs.getBestRefreshRate(layers)); + EXPECT_EQ(mode, selector.getBestRefreshRate(layers)); } } -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExact) { - TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60); +TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ExplicitExact) { + TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60); std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}}; auto& explicitExactLayer = layers[0]; @@ -2003,26 +2016,27 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExact) { explicitExactLayer.name = "ExplicitExact"; explicitExactLayer.desiredRefreshRate = 30_Hz; - EXPECT_EQ(kMode30, configs.getBestRefreshRate(layers)); - EXPECT_EQ(kMode30, configs.getBestRefreshRate(layers, {.touch = true})); + EXPECT_EQ(kMode30, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode30, selector.getBestRefreshRate(layers, {.touch = true})); explicitExactOrMultipleLayer.desiredRefreshRate = 120_Hz; explicitExactLayer.desiredRefreshRate = 60_Hz; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); explicitExactLayer.desiredRefreshRate = 72_Hz; - EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers)); explicitExactLayer.desiredRefreshRate = 90_Hz; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); explicitExactLayer.desiredRefreshRate = 120_Hz; - EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers)); } -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExactEnableFrameRateOverride) { - TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60, - {.enableFrameRateOverride = true}); +TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ExplicitExactEnableFrameRateOverride) { + TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60, + {.enableFrameRateOverride = + Config::FrameRateOverride::Enabled}); std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}}; auto& explicitExactLayer = layers[0]; @@ -2036,58 +2050,60 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExactEnableFrameRateOv explicitExactLayer.name = "ExplicitExact"; explicitExactLayer.desiredRefreshRate = 30_Hz; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers, {.touch = true})); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers, {.touch = true})); explicitExactOrMultipleLayer.desiredRefreshRate = 120_Hz; explicitExactLayer.desiredRefreshRate = 60_Hz; - EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers)); explicitExactLayer.desiredRefreshRate = 72_Hz; - EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers)); explicitExactLayer.desiredRefreshRate = 90_Hz; - EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers)); explicitExactLayer.desiredRefreshRate = 120_Hz; - EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers)); } -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ReadsCache) { - TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60); +TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ReadsCache) { + TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60); - using GlobalSignals = RefreshRateConfigs::GlobalSignals; + using GlobalSignals = RefreshRateSelector::GlobalSignals; const auto args = std::make_pair(std::vector<LayerRequirement>{}, GlobalSignals{.touch = true, .idle = true}); - const RefreshRateConfigs::RankedRefreshRates result = {{RefreshRateConfigs::ScoredRefreshRate{ - kMode90}}, - {.touch = true}}; + const RefreshRateSelector::RankedRefreshRates result = {{RefreshRateSelector::ScoredRefreshRate{ + kMode90}}, + {.touch = true}}; - configs.mutableGetRankedRefreshRatesCache() = {args, result}; + selector.mutableGetRankedRefreshRatesCache() = {args, result}; - EXPECT_EQ(result, configs.getRankedRefreshRates(args.first, args.second)); + EXPECT_EQ(result, selector.getRankedRefreshRates(args.first, args.second)); } -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_WritesCache) { - TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60); +TEST_F(RefreshRateSelectorTest, getBestRefreshRate_WritesCache) { + TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60); - EXPECT_FALSE(configs.mutableGetRankedRefreshRatesCache()); + EXPECT_FALSE(selector.mutableGetRankedRefreshRatesCache()); std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}}; - RefreshRateConfigs::GlobalSignals globalSignals{.touch = true, .idle = true}; + RefreshRateSelector::GlobalSignals globalSignals{.touch = true, .idle = true}; - const auto result = configs.getRankedRefreshRates(layers, globalSignals); + const auto result = selector.getRankedRefreshRates(layers, globalSignals); - const auto& cache = configs.mutableGetRankedRefreshRatesCache(); + const auto& cache = selector.mutableGetRankedRefreshRatesCache(); ASSERT_TRUE(cache); EXPECT_EQ(cache->arguments, std::make_pair(layers, globalSignals)); EXPECT_EQ(cache->result, result); } -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExactTouchBoost) { - TestableRefreshRateConfigs configs(kModes_60_120, kModeId60, {.enableFrameRateOverride = true}); +TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ExplicitExactTouchBoost) { + TestableRefreshRateSelector selector(kModes_60_120, kModeId60, + {.enableFrameRateOverride = + Config::FrameRateOverride::Enabled}); std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}}; auto& explicitExactLayer = layers[0]; @@ -2101,18 +2117,19 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExactTouchBoost) { explicitExactLayer.name = "ExplicitExact"; explicitExactLayer.desiredRefreshRate = 30_Hz; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers, {.touch = true})); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers, {.touch = true})); explicitExactOrMultipleLayer.vote = LayerVoteType::NoVote; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers, {.touch = true})); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers, {.touch = true})); } -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_FractionalRefreshRates_ExactAndDefault) { - TestableRefreshRateConfigs configs(kModes_24_25_30_50_60_Frac, kModeId60, - {.enableFrameRateOverride = true}); +TEST_F(RefreshRateSelectorTest, getBestRefreshRate_FractionalRefreshRates_ExactAndDefault) { + TestableRefreshRateSelector selector(kModes_24_25_30_50_60_Frac, kModeId60, + {.enableFrameRateOverride = + Config::FrameRateOverride::Enabled}); std::vector<LayerRequirement> layers = {{.weight = 0.5f}, {.weight = 0.5f}}; auto& explicitDefaultLayer = layers[0]; @@ -2126,11 +2143,11 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_FractionalRefreshRates_ExactAn explicitDefaultLayer.name = "ExplicitDefault"; explicitDefaultLayer.desiredRefreshRate = 59.94_Hz; - EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers)); + EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers)); } // b/190578904 -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_withCloseRefreshRates) { +TEST_F(RefreshRateSelectorTest, getBestRefreshRate_withCloseRefreshRates) { constexpr int kMinRefreshRate = 10; constexpr int kMaxRefreshRate = 240; @@ -2142,14 +2159,14 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_withCloseRefreshRates) { Fps::fromValue(static_cast<float>(fps)))); } - const TestableRefreshRateConfigs configs(std::move(displayModes), - DisplayModeId(kMinRefreshRate)); + const TestableRefreshRateSelector selector(std::move(displayModes), + DisplayModeId(kMinRefreshRate)); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; const auto testRefreshRate = [&](Fps fps, LayerVoteType vote) { layers[0].desiredRefreshRate = fps; layers[0].vote = vote; - EXPECT_EQ(fps.getIntValue(), configs.getBestRefreshRate(layers)->getFps().getIntValue()) + EXPECT_EQ(fps.getIntValue(), selector.getBestRefreshRate(layers)->getFps().getIntValue()) << "Failed for " << ftl::enum_string(vote); }; @@ -2163,15 +2180,15 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_withCloseRefreshRates) { } // b/190578904 -TEST_F(RefreshRateConfigsTest, getBestRefreshRate_conflictingVotes) { +TEST_F(RefreshRateSelectorTest, getBestRefreshRate_conflictingVotes) { constexpr DisplayModeId kActiveModeId{0}; DisplayModes displayModes = makeModes(createDisplayMode(kActiveModeId, 43_Hz), createDisplayMode(DisplayModeId(1), 53_Hz), createDisplayMode(DisplayModeId(2), 55_Hz), createDisplayMode(DisplayModeId(3), 60_Hz)); - const RefreshRateConfigs::GlobalSignals globalSignals = {.touch = false, .idle = false}; - const TestableRefreshRateConfigs configs(std::move(displayModes), kActiveModeId); + const RefreshRateSelector::GlobalSignals globalSignals = {.touch = false, .idle = false}; + const TestableRefreshRateSelector selector(std::move(displayModes), kActiveModeId); const std::vector<LayerRequirement> layers = { { @@ -2188,133 +2205,125 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_conflictingVotes) { }, }; - EXPECT_EQ(53_Hz, configs.getBestRefreshRate(layers, globalSignals)->getFps()); + EXPECT_EQ(53_Hz, selector.getBestRefreshRate(layers, globalSignals)->getFps()); } -TEST_F(RefreshRateConfigsTest, modeComparison) { +TEST_F(RefreshRateSelectorTest, modeComparison) { EXPECT_LT(kMode60->getFps(), kMode90->getFps()); EXPECT_GE(kMode60->getFps(), kMode60->getFps()); EXPECT_GE(kMode90->getFps(), kMode90->getFps()); } -TEST_F(RefreshRateConfigsTest, testKernelIdleTimerAction) { - using KernelIdleTimerAction = RefreshRateConfigs::KernelIdleTimerAction; +TEST_F(RefreshRateSelectorTest, testKernelIdleTimerAction) { + using KernelIdleTimerAction = RefreshRateSelector::KernelIdleTimerAction; - TestableRefreshRateConfigs configs(kModes_60_90, kModeId90); + TestableRefreshRateSelector selector(kModes_60_90, kModeId90); - // setPolicy(60, 90), current 90Hz => TurnOn. - EXPECT_EQ(KernelIdleTimerAction::TurnOn, configs.getIdleTimerAction()); + EXPECT_EQ(KernelIdleTimerAction::TurnOn, selector.getIdleTimerAction()); - // setPolicy(60, 90), current 60Hz => TurnOn. EXPECT_EQ(SetPolicyResult::Changed, - configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 90_Hz}})); - EXPECT_EQ(KernelIdleTimerAction::TurnOn, configs.getIdleTimerAction()); + selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 90_Hz}})); + EXPECT_EQ(KernelIdleTimerAction::TurnOn, selector.getIdleTimerAction()); - // setPolicy(60, 60), current 60Hz => TurnOff EXPECT_EQ(SetPolicyResult::Changed, - configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}})); - EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction()); + selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}})); + EXPECT_EQ(KernelIdleTimerAction::TurnOff, selector.getIdleTimerAction()); - // setPolicy(90, 90), current 90Hz => TurnOff. EXPECT_EQ(SetPolicyResult::Changed, - configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}})); - EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction()); + selector.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}})); + EXPECT_EQ(KernelIdleTimerAction::TurnOff, selector.getIdleTimerAction()); } -TEST_F(RefreshRateConfigsTest, testKernelIdleTimerActionFor120Hz) { - using KernelIdleTimerAction = RefreshRateConfigs::KernelIdleTimerAction; +TEST_F(RefreshRateSelectorTest, testKernelIdleTimerActionFor120Hz) { + using KernelIdleTimerAction = RefreshRateSelector::KernelIdleTimerAction; - TestableRefreshRateConfigs configs(kModes_60_120, kModeId120); + TestableRefreshRateSelector selector(kModes_60_120, kModeId120); - // setPolicy(0, 60), current 60Hz => TurnOn. EXPECT_EQ(SetPolicyResult::Changed, - configs.setDisplayManagerPolicy({kModeId60, {0_Hz, 60_Hz}})); - EXPECT_EQ(KernelIdleTimerAction::TurnOn, configs.getIdleTimerAction()); + selector.setDisplayManagerPolicy({kModeId60, {0_Hz, 60_Hz}})); + EXPECT_EQ(KernelIdleTimerAction::TurnOn, selector.getIdleTimerAction()); - // setPolicy(60, 60), current 60Hz => TurnOff. EXPECT_EQ(SetPolicyResult::Changed, - configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}})); - EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction()); + selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}})); + EXPECT_EQ(KernelIdleTimerAction::TurnOff, selector.getIdleTimerAction()); - // setPolicy(60, 120), current 60Hz => TurnOn. EXPECT_EQ(SetPolicyResult::Changed, - configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 120_Hz}})); - EXPECT_EQ(KernelIdleTimerAction::TurnOn, configs.getIdleTimerAction()); + selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 120_Hz}})); + EXPECT_EQ(KernelIdleTimerAction::TurnOn, selector.getIdleTimerAction()); - // setPolicy(120, 120), current 120Hz => TurnOff. EXPECT_EQ(SetPolicyResult::Changed, - configs.setDisplayManagerPolicy({kModeId120, {120_Hz, 120_Hz}})); - EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction()); + selector.setDisplayManagerPolicy({kModeId120, {120_Hz, 120_Hz}})); + EXPECT_EQ(KernelIdleTimerAction::TurnOff, selector.getIdleTimerAction()); } -TEST_F(RefreshRateConfigsTest, getFrameRateDivisor) { - TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId30); +TEST_F(RefreshRateSelectorTest, getFrameRateDivisor) { + TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId30); const auto frameRate = 30_Hz; - Fps displayRefreshRate = configs.getActiveMode().getFps(); - EXPECT_EQ(1, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate)); + Fps displayRefreshRate = selector.getActiveMode().getFps(); + EXPECT_EQ(1, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate)); - configs.setActiveModeId(kModeId60); - displayRefreshRate = configs.getActiveMode().getFps(); - EXPECT_EQ(2, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate)); + selector.setActiveModeId(kModeId60); + displayRefreshRate = selector.getActiveMode().getFps(); + EXPECT_EQ(2, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate)); - configs.setActiveModeId(kModeId72); - displayRefreshRate = configs.getActiveMode().getFps(); - EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate)); + selector.setActiveModeId(kModeId72); + displayRefreshRate = selector.getActiveMode().getFps(); + EXPECT_EQ(0, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate)); - configs.setActiveModeId(kModeId90); - displayRefreshRate = configs.getActiveMode().getFps(); - EXPECT_EQ(3, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate)); + selector.setActiveModeId(kModeId90); + displayRefreshRate = selector.getActiveMode().getFps(); + EXPECT_EQ(3, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate)); - configs.setActiveModeId(kModeId120); - displayRefreshRate = configs.getActiveMode().getFps(); - EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate)); + selector.setActiveModeId(kModeId120); + displayRefreshRate = selector.getActiveMode().getFps(); + EXPECT_EQ(4, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate)); - configs.setActiveModeId(kModeId90); - displayRefreshRate = configs.getActiveMode().getFps(); - EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, 22.5_Hz)); + selector.setActiveModeId(kModeId90); + displayRefreshRate = selector.getActiveMode().getFps(); + EXPECT_EQ(4, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, 22.5_Hz)); - EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivisor(24_Hz, 25_Hz)); - EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivisor(24_Hz, 23.976_Hz)); - EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivisor(30_Hz, 29.97_Hz)); - EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivisor(60_Hz, 59.94_Hz)); + EXPECT_EQ(0, RefreshRateSelector::getFrameRateDivisor(24_Hz, 25_Hz)); + EXPECT_EQ(0, RefreshRateSelector::getFrameRateDivisor(24_Hz, 23.976_Hz)); + EXPECT_EQ(0, RefreshRateSelector::getFrameRateDivisor(30_Hz, 29.97_Hz)); + EXPECT_EQ(0, RefreshRateSelector::getFrameRateDivisor(60_Hz, 59.94_Hz)); } -TEST_F(RefreshRateConfigsTest, isFractionalPairOrMultiple) { - EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(23.976_Hz, 24_Hz)); - EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(24_Hz, 23.976_Hz)); +TEST_F(RefreshRateSelectorTest, isFractionalPairOrMultiple) { + EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(23.976_Hz, 24_Hz)); + EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(24_Hz, 23.976_Hz)); - EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(29.97_Hz, 30_Hz)); - EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(30_Hz, 29.97_Hz)); + EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(29.97_Hz, 30_Hz)); + EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(30_Hz, 29.97_Hz)); - EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(59.94_Hz, 60_Hz)); - EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(60_Hz, 59.94_Hz)); + EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(59.94_Hz, 60_Hz)); + EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(60_Hz, 59.94_Hz)); - EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(29.97_Hz, 60_Hz)); - EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(60_Hz, 29.97_Hz)); + EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(29.97_Hz, 60_Hz)); + EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(60_Hz, 29.97_Hz)); - EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(59.94_Hz, 30_Hz)); - EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(30_Hz, 59.94_Hz)); + EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(59.94_Hz, 30_Hz)); + EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(30_Hz, 59.94_Hz)); const auto refreshRates = {23.976_Hz, 24_Hz, 25_Hz, 29.97_Hz, 30_Hz, 50_Hz, 59.94_Hz, 60_Hz}; for (auto refreshRate : refreshRates) { - EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(refreshRate, refreshRate)); + EXPECT_FALSE(RefreshRateSelector::isFractionalPairOrMultiple(refreshRate, refreshRate)); } - EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(24_Hz, 25_Hz)); - EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(23.978_Hz, 25_Hz)); - EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(29.97_Hz, 59.94_Hz)); + EXPECT_FALSE(RefreshRateSelector::isFractionalPairOrMultiple(24_Hz, 25_Hz)); + EXPECT_FALSE(RefreshRateSelector::isFractionalPairOrMultiple(23.978_Hz, 25_Hz)); + EXPECT_FALSE(RefreshRateSelector::isFractionalPairOrMultiple(29.97_Hz, 59.94_Hz)); } -TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_noLayers) { - RefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId120); +TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_noLayers) { + RefreshRateSelector selector(kModes_30_60_72_90_120, kModeId120); - EXPECT_TRUE(configs.getFrameRateOverrides({}, 120_Hz, {}).empty()); + EXPECT_TRUE(selector.getFrameRateOverrides({}, 120_Hz, {}).empty()); } -TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_60on120) { - RefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId120, - {.enableFrameRateOverride = true}); +TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_60on120) { + RefreshRateSelector selector(kModes_30_60_72_90_120, kModeId120, + {.enableFrameRateOverride = Config::FrameRateOverride::Enabled}); std::vector<LayerRequirement> layers = {{.weight = 1.f}}; layers[0].name = "Test layer"; @@ -2322,37 +2331,37 @@ TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_60on120) { layers[0].desiredRefreshRate = 60_Hz; layers[0].vote = LayerVoteType::ExplicitDefault; - auto frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {}); + auto frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); EXPECT_EQ(1u, frameRateOverrides.size()); ASSERT_EQ(1u, frameRateOverrides.count(1234)); EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); layers[0].vote = LayerVoteType::ExplicitExactOrMultiple; - frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {}); + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); EXPECT_EQ(1u, frameRateOverrides.size()); ASSERT_EQ(1u, frameRateOverrides.count(1234)); EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); layers[0].vote = LayerVoteType::NoVote; - frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {}); + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); EXPECT_TRUE(frameRateOverrides.empty()); layers[0].vote = LayerVoteType::Min; - frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {}); + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); EXPECT_TRUE(frameRateOverrides.empty()); layers[0].vote = LayerVoteType::Max; - frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {}); + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); EXPECT_TRUE(frameRateOverrides.empty()); layers[0].vote = LayerVoteType::Heuristic; - frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {}); + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); EXPECT_TRUE(frameRateOverrides.empty()); } -TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_twoUids) { - RefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId120, - {.enableFrameRateOverride = true}); +TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_twoUids) { + RefreshRateSelector selector(kModes_30_60_72_90_120, kModeId120, + {.enableFrameRateOverride = Config::FrameRateOverride::Enabled}); std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f}, {.ownerUid = 5678, .weight = 1.f}}; @@ -2364,7 +2373,7 @@ TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_twoUids) { layers[1].name = "Test layer 5678"; layers[1].desiredRefreshRate = 30_Hz; layers[1].vote = LayerVoteType::ExplicitDefault; - auto frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {}); + auto frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); EXPECT_EQ(2u, frameRateOverrides.size()); ASSERT_EQ(1u, frameRateOverrides.count(1234)); @@ -2373,54 +2382,188 @@ TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_twoUids) { EXPECT_EQ(30_Hz, frameRateOverrides.at(5678)); layers[1].vote = LayerVoteType::Heuristic; - frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {}); + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); EXPECT_EQ(1u, frameRateOverrides.size()); ASSERT_EQ(1u, frameRateOverrides.count(1234)); EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); layers[1].ownerUid = 1234; - frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {}); + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); EXPECT_TRUE(frameRateOverrides.empty()); } -TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_touch) { - RefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId120, - {.enableFrameRateOverride = true}); +TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_touch) { + RefreshRateSelector selector(kModes_30_60_72_90_120, kModeId120, + {.enableFrameRateOverride = Config::FrameRateOverride::Enabled}); std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f}}; layers[0].name = "Test layer"; layers[0].desiredRefreshRate = 60_Hz; layers[0].vote = LayerVoteType::ExplicitDefault; - auto frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {}); + auto frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); EXPECT_EQ(1u, frameRateOverrides.size()); ASSERT_EQ(1u, frameRateOverrides.count(1234)); EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); - frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {.touch = true}); + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true}); EXPECT_EQ(1u, frameRateOverrides.size()); ASSERT_EQ(1u, frameRateOverrides.count(1234)); EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); layers[0].vote = LayerVoteType::ExplicitExact; - frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {}); + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); EXPECT_EQ(1u, frameRateOverrides.size()); ASSERT_EQ(1u, frameRateOverrides.count(1234)); EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); - frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {.touch = true}); + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true}); EXPECT_EQ(1u, frameRateOverrides.size()); ASSERT_EQ(1u, frameRateOverrides.count(1234)); EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); layers[0].vote = LayerVoteType::ExplicitExactOrMultiple; - frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {}); + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); EXPECT_EQ(1u, frameRateOverrides.size()); ASSERT_EQ(1u, frameRateOverrides.count(1234)); EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); - frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {.touch = true}); + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true}); + EXPECT_TRUE(frameRateOverrides.empty()); +} + +TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_DivisorIsNotDisplayRefreshRate_Enabled) { + RefreshRateSelector selector(kModes_60_120, kModeId120, + {.enableFrameRateOverride = Config::FrameRateOverride::Enabled}); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + layers[0].name = "Test layer"; + layers[0].ownerUid = 1234; + layers[0].desiredRefreshRate = 30_Hz; + layers[0].vote = LayerVoteType::ExplicitDefault; + + auto frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(30_Hz, frameRateOverrides.at(1234)); + + layers[0].vote = LayerVoteType::ExplicitExactOrMultiple; + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(30_Hz, frameRateOverrides.at(1234)); + + layers[0].vote = LayerVoteType::NoVote; + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_TRUE(frameRateOverrides.empty()); + + layers[0].vote = LayerVoteType::Min; + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_TRUE(frameRateOverrides.empty()); + + layers[0].vote = LayerVoteType::Max; + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); EXPECT_TRUE(frameRateOverrides.empty()); + + layers[0].vote = LayerVoteType::Heuristic; + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_TRUE(frameRateOverrides.empty()); +} + +TEST_F(RefreshRateSelectorTest, + getFrameRateOverrides_DivisorIsNotDisplayRefreshRate_EnabledForNativeRefreshRates) { + RefreshRateSelector selector(kModes_60_120, kModeId120, + {.enableFrameRateOverride = + Config::FrameRateOverride::EnabledForNativeRefreshRates}); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + layers[0].name = "Test layer"; + layers[0].ownerUid = 1234; + layers[0].desiredRefreshRate = 30_Hz; + layers[0].vote = LayerVoteType::ExplicitDefault; + + auto frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); + + layers[0].vote = LayerVoteType::ExplicitExactOrMultiple; + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); + + layers[0].vote = LayerVoteType::NoVote; + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_TRUE(frameRateOverrides.empty()); + + layers[0].vote = LayerVoteType::Min; + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_TRUE(frameRateOverrides.empty()); + + layers[0].vote = LayerVoteType::Max; + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_TRUE(frameRateOverrides.empty()); + + layers[0].vote = LayerVoteType::Heuristic; + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_TRUE(frameRateOverrides.empty()); +} + +TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_InPolicy) { + TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId120, + {.enableFrameRateOverride = + Config::FrameRateOverride::Enabled}); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + { + const FpsRange physical = {120_Hz, 120_Hz}; + const FpsRange render = {60_Hz, 90_Hz}; + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy( + {kModeId120, {physical, render}, {physical, render}})); + } + + layers[0].name = "30Hz"; + layers[0].ownerUid = 1234; + layers[0].desiredRefreshRate = 30_Hz; + layers[0].vote = LayerVoteType::ExplicitDefault; + + auto frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_EQ(1u, frameRateOverrides.size()); + EXPECT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); + + { + const FpsRange physical = {120_Hz, 120_Hz}; + const FpsRange render = {30_Hz, 90_Hz}; + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy( + {kModeId120, {physical, render}, {physical, render}})); + } + + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_EQ(1u, frameRateOverrides.size()); + EXPECT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(30_Hz, frameRateOverrides.at(1234)); + + { + const FpsRange physical = {120_Hz, 120_Hz}; + const FpsRange render = {30_Hz, 30_Hz}; + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy( + {kModeId120, {physical, render}, {physical, render}})); + } + + layers[0].name = "60Hz"; + layers[0].ownerUid = 1234; + layers[0].desiredRefreshRate = 60_Hz; + layers[0].vote = LayerVoteType::ExplicitDefault; + + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_EQ(1u, frameRateOverrides.size()); + EXPECT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(30_Hz, frameRateOverrides.at(1234)); } } // namespace diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp index 147433b422..ea4666ed4b 100644 --- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp @@ -20,9 +20,8 @@ #include <mutex> -#include "FakeDisplayInjector.h" #include "Scheduler/EventThread.h" -#include "Scheduler/RefreshRateConfigs.h" +#include "Scheduler/RefreshRateSelector.h" #include "TestableScheduler.h" #include "TestableSurfaceFlinger.h" #include "mock/DisplayHardware/MockDisplayMode.h" @@ -41,7 +40,6 @@ namespace { using MockEventThread = android::mock::EventThread; using MockLayer = android::mock::MockLayer; -using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector; class SchedulerTest : public testing::Test { protected: @@ -78,22 +76,18 @@ protected: createDisplayMode(kDisplayId3, DisplayModeId(0), 60_Hz); static inline const DisplayModes kDisplay3Modes = makeModes(kDisplay3Mode60); - std::shared_ptr<RefreshRateConfigs> mConfigs = - std::make_shared<RefreshRateConfigs>(makeModes(kDisplay1Mode60), - kDisplay1Mode60->getId()); + std::shared_ptr<RefreshRateSelector> mSelector = + std::make_shared<RefreshRateSelector>(makeModes(kDisplay1Mode60), + kDisplay1Mode60->getId()); mock::SchedulerCallback mSchedulerCallback; - TestableScheduler* mScheduler = new TestableScheduler{mConfigs, mSchedulerCallback}; + TestableScheduler* mScheduler = new TestableScheduler{mSelector, mSchedulerCallback}; ConnectionHandle mConnectionHandle; MockEventThread* mEventThread; sp<MockEventThreadConnection> mEventThreadConnection; TestableSurfaceFlinger mFlinger; - Hwc2::mock::PowerAdvisor mPowerAdvisor; - sp<android::mock::NativeWindow> mNativeWindow = sp<android::mock::NativeWindow>::make(); - - FakeDisplayInjector mFakeDisplayInjector{mFlinger, mPowerAdvisor, mNativeWindow}; }; SchedulerTest::SchedulerTest() { @@ -196,8 +190,8 @@ TEST_F(SchedulerTest, updateDisplayModes) { sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger()); ASSERT_EQ(1u, mScheduler->layerHistorySize()); - mScheduler->setRefreshRateConfigs( - std::make_shared<RefreshRateConfigs>(kDisplay1Modes, kDisplay1Mode60->getId())); + mScheduler->setRefreshRateSelector( + std::make_shared<RefreshRateSelector>(kDisplay1Modes, kDisplay1Mode60->getId())); ASSERT_EQ(0u, mScheduler->getNumActiveLayers()); mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer); @@ -240,14 +234,11 @@ MATCHER(Is120Hz, "") { } TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) { - const auto display = mFakeDisplayInjector.injectInternalDisplay( - [&](FakeDisplayDeviceInjector& injector) { - injector.setDisplayModes(kDisplay1Modes, kDisplay1Mode60->getId()); - }, - {.displayId = kDisplayId1}); + const auto selectorPtr = + std::make_shared<RefreshRateSelector>(kDisplay1Modes, kDisplay1Mode60->getId()); - mScheduler->registerDisplay(display); - mScheduler->setRefreshRateConfigs(display->holdRefreshRateConfigs()); + mScheduler->registerDisplay(kDisplayId1, selectorPtr); + mScheduler->setRefreshRateSelector(selectorPtr); const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger()); EXPECT_CALL(*layer, isVisible()).WillOnce(Return(true)); @@ -269,16 +260,12 @@ TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) { } TEST_F(SchedulerTest, chooseDisplayModesSingleDisplay) { - const auto display = mFakeDisplayInjector.injectInternalDisplay( - [&](FakeDisplayDeviceInjector& injector) { - injector.setDisplayModes(kDisplay1Modes, kDisplay1Mode60->getId()); - }, - {.displayId = kDisplayId1}); - - mScheduler->registerDisplay(display); + mScheduler->registerDisplay(kDisplayId1, + std::make_shared<RefreshRateSelector>(kDisplay1Modes, + kDisplay1Mode60->getId())); - std::vector<RefreshRateConfigs::LayerRequirement> layers = - std::vector<RefreshRateConfigs::LayerRequirement>({{.weight = 1.f}, {.weight = 1.f}}); + std::vector<RefreshRateSelector::LayerRequirement> layers = + std::vector<RefreshRateSelector::LayerRequirement>({{.weight = 1.f}, {.weight = 1.f}}); mScheduler->setContentRequirements(layers); GlobalSignals globalSignals = {.idle = true}; mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals); @@ -314,23 +301,16 @@ TEST_F(SchedulerTest, chooseDisplayModesSingleDisplay) { EXPECT_EQ(choice->get(), DisplayModeChoice(kDisplay1Mode120, globalSignals)); mScheduler->unregisterDisplay(kDisplayId1); - EXPECT_TRUE(mScheduler->mutableDisplays().empty()); + EXPECT_FALSE(mScheduler->hasRefreshRateSelectors()); } TEST_F(SchedulerTest, chooseDisplayModesMultipleDisplays) { - const auto display1 = mFakeDisplayInjector.injectInternalDisplay( - [&](FakeDisplayDeviceInjector& injector) { - injector.setDisplayModes(kDisplay1Modes, kDisplay1Mode60->getId()); - }, - {.displayId = kDisplayId1, .hwcDisplayId = 42, .isPrimary = true}); - const auto display2 = mFakeDisplayInjector.injectInternalDisplay( - [&](FakeDisplayDeviceInjector& injector) { - injector.setDisplayModes(kDisplay2Modes, kDisplay2Mode60->getId()); - }, - {.displayId = kDisplayId2, .hwcDisplayId = 41, .isPrimary = false}); - - mScheduler->registerDisplay(display1); - mScheduler->registerDisplay(display2); + mScheduler->registerDisplay(kDisplayId1, + std::make_shared<RefreshRateSelector>(kDisplay1Modes, + kDisplay1Mode60->getId())); + mScheduler->registerDisplay(kDisplayId2, + std::make_shared<RefreshRateSelector>(kDisplay2Modes, + kDisplay2Mode60->getId())); using DisplayModeChoice = TestableScheduler::DisplayModeChoice; TestableScheduler::DisplayModeChoiceMap expectedChoices; @@ -343,8 +323,8 @@ TEST_F(SchedulerTest, chooseDisplayModesMultipleDisplays) { globalSignals)(kDisplayId2, kDisplay2Mode60, globalSignals); - std::vector<RefreshRateConfigs::LayerRequirement> layers = {{.weight = 1.f}, - {.weight = 1.f}}; + std::vector<RefreshRateSelector::LayerRequirement> layers = {{.weight = 1.f}, + {.weight = 1.f}}; mScheduler->setContentRequirements(layers); mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals); @@ -380,13 +360,10 @@ TEST_F(SchedulerTest, chooseDisplayModesMultipleDisplays) { } { // This display does not support 120 Hz, so we should choose 60 Hz despite the touch signal. - const auto display3 = mFakeDisplayInjector.injectInternalDisplay( - [&](FakeDisplayDeviceInjector& injector) { - injector.setDisplayModes(kDisplay3Modes, kDisplay3Mode60->getId()); - }, - {.displayId = kDisplayId3, .hwcDisplayId = 40, .isPrimary = false}); - - mScheduler->registerDisplay(display3); + mScheduler + ->registerDisplay(kDisplayId3, + std::make_shared<RefreshRateSelector>(kDisplay3Modes, + kDisplay3Mode60->getId())); const GlobalSignals globalSignals = {.touch = true}; mScheduler->replaceTouchTimer(10); diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp index dfcfd912f9..6adcd5259d 100644 --- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp +++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp @@ -383,8 +383,8 @@ TEST_P(SetFrameRateTest, SetOnParentActivatesTree) { history.record(parent.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer); history.record(child.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer); - const auto configs = mFlinger.mutableScheduler().refreshRateConfigs(); - const auto summary = history.summarize(*configs, 0); + const auto selectorPtr = mFlinger.mutableScheduler().refreshRateSelector(); + const auto summary = history.summarize(*selectorPtr, 0); ASSERT_EQ(2u, summary.size()); EXPECT_EQ(FRAME_RATE_VOTE1.rate, summary[0].desiredRefreshRate); diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp index 6b7e3533a5..05d0ebf773 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "mock/MockDisplayModeSpecs.h" #include "mock/MockEventThread.h" #undef LOG_TAG #define LOG_TAG "LibSurfaceFlingerUnittests" @@ -42,15 +43,15 @@ public: PrimaryDisplayVariant::setupHwcGetActiveConfigCallExpectations(this); DisplayModes modes = makeModes(kMode60, kMode90, kMode120, kMode90_4K); - auto configs = std::make_shared<scheduler::RefreshRateConfigs>(modes, kModeId60); + auto selectorPtr = std::make_shared<scheduler::RefreshRateSelector>(modes, kModeId60); - setupScheduler(configs); + setupScheduler(selectorPtr); mFlinger.onComposerHalHotplug(PrimaryDisplayVariant::HWC_DISPLAY_ID, Connection::CONNECTED); mFlinger.configureAndCommit(); mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this) - .setDisplayModes(std::move(modes), kModeId60, std::move(configs)) + .setDisplayModes(std::move(modes), kModeId60, std::move(selectorPtr)) .inject(); // isVsyncPeriodSwitchSupported should return true, otherwise the SF's HWC proxy @@ -60,7 +61,7 @@ public: } protected: - void setupScheduler(std::shared_ptr<scheduler::RefreshRateConfigs>); + void setupScheduler(std::shared_ptr<scheduler::RefreshRateSelector>); sp<DisplayDevice> mDisplay; mock::EventThread* mAppEventThread; @@ -80,7 +81,7 @@ protected: }; void DisplayModeSwitchingTest::setupScheduler( - std::shared_ptr<scheduler::RefreshRateConfigs> configs) { + std::shared_ptr<scheduler::RefreshRateSelector> selectorPtr) { auto eventThread = std::make_unique<mock::EventThread>(); mAppEventThread = eventThread.get(); auto sfEventThread = std::make_unique<mock::EventThread>(); @@ -108,7 +109,7 @@ void DisplayModeSwitchingTest::setupScheduler( mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker), std::move(eventThread), std::move(sfEventThread), TestableSurfaceFlinger::SchedulerCallbackImpl::kNoOp, - std::move(configs)); + std::move(selectorPtr)); } TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithRefreshRequired) { @@ -119,8 +120,9 @@ TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithRefreshRe mFlinger.onActiveDisplayChanged(mDisplay); - mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), kModeId90.value(), - false, 0.f, 120.f, 0.f, 120.f); + mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), + mock::createDisplayModeSpecs(kModeId90.value(), false, 0, + 120)); ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value()); ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId90); @@ -157,8 +159,9 @@ TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithoutRefres mFlinger.onActiveDisplayChanged(mDisplay); - mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), kModeId90.value(), - true, 0.f, 120.f, 0.f, 120.f); + mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), + mock::createDisplayModeSpecs(kModeId90.value(), true, 0, + 120)); ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value()); ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId90); @@ -191,8 +194,9 @@ TEST_F(DisplayModeSwitchingTest, twoConsecutiveSetDesiredDisplayModeSpecs) { mFlinger.onActiveDisplayChanged(mDisplay); - mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), kModeId90.value(), - false, 0.f, 120.f, 0.f, 120.f); + mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), + mock::createDisplayModeSpecs(kModeId90.value(), false, 0, + 120)); const VsyncPeriodChangeTimeline timeline{.refreshRequired = true}; EXPECT_CALL(*mComposer, @@ -202,8 +206,9 @@ TEST_F(DisplayModeSwitchingTest, twoConsecutiveSetDesiredDisplayModeSpecs) { mFlinger.commit(); - mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), kModeId120.value(), - false, 0.f, 180.f, 0.f, 180.f); + mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), + mock::createDisplayModeSpecs(kModeId120.value(), false, 0, + 180)); ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value()); ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId120); @@ -232,8 +237,9 @@ TEST_F(DisplayModeSwitchingTest, changeResolution_OnActiveDisplay_WithoutRefresh mFlinger.onActiveDisplayChanged(mDisplay); - mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), kModeId90_4K.value(), - false, 0.f, 120.f, 0.f, 120.f); + mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), + mock::createDisplayModeSpecs(kModeId90_4K.value(), false, 0, + 120)); ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value()); ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId90_4K); diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h index 26b2b673a2..95c99150b3 100644 --- a/services/surfaceflinger/tests/unittests/TestableScheduler.h +++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h @@ -32,17 +32,17 @@ namespace android::scheduler { class TestableScheduler : public Scheduler, private ICompositor { public: - TestableScheduler(std::shared_ptr<RefreshRateConfigs> configs, ISchedulerCallback& callback) + TestableScheduler(RefreshRateSelectorPtr selectorPtr, ISchedulerCallback& callback) : TestableScheduler(std::make_unique<mock::VsyncController>(), - std::make_unique<mock::VSyncTracker>(), std::move(configs), + std::make_unique<mock::VSyncTracker>(), std::move(selectorPtr), callback) {} TestableScheduler(std::unique_ptr<VsyncController> controller, - std::unique_ptr<VSyncTracker> tracker, - std::shared_ptr<RefreshRateConfigs> configs, ISchedulerCallback& callback) + std::unique_ptr<VSyncTracker> tracker, RefreshRateSelectorPtr selectorPtr, + ISchedulerCallback& callback) : Scheduler(*this, callback, Feature::kContentDetection) { mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker), nullptr, std::move(controller))); - setRefreshRateConfigs(std::move(configs)); + setRefreshRateSelector(std::move(selectorPtr)); ON_CALL(*this, postMessage).WillByDefault([](sp<MessageHandler>&& handler) { // Execute task to prevent broken promise exception on destruction. @@ -66,16 +66,15 @@ public: auto& mutablePrimaryHWVsyncEnabled() { return mPrimaryHWVsyncEnabled; } auto& mutableHWVsyncAvailable() { return mHWVsyncAvailable; } - auto& mutableLayerHistory() { return mLayerHistory; } + auto refreshRateSelector() { return holdRefreshRateSelector(); } + bool hasRefreshRateSelectors() const { return !mRefreshRateSelectors.empty(); } - auto& mutableDisplays() { return mDisplays; } + auto& mutableLayerHistory() { return mLayerHistory; } size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS { return mLayerHistory.mActiveLayerInfos.size() + mLayerHistory.mInactiveLayerInfos.size(); } - auto refreshRateConfigs() { return holdRefreshRateConfigs(); } - size_t getNumActiveLayers() NO_THREAD_SAFETY_ANALYSIS { return mLayerHistory.mActiveLayerInfos.size(); } @@ -102,7 +101,7 @@ public: mPolicy.idleTimer = globalSignals.idle ? TimerState::Expired : TimerState::Reset; } - void setContentRequirements(std::vector<RefreshRateConfigs::LayerRequirement> layers) { + void setContentRequirements(std::vector<RefreshRateSelector::LayerRequirement> layers) { std::lock_guard<std::mutex> lock(mPolicyLock); mPolicy.contentRequirements = std::move(layers); } diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index 7f471bc8b8..46eca69c6a 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -38,7 +38,7 @@ #include "Layer.h" #include "NativeWindowSurface.h" #include "Scheduler/MessageQueue.h" -#include "Scheduler/RefreshRateConfigs.h" +#include "Scheduler/RefreshRateSelector.h" #include "StartPropertySetThread.h" #include "SurfaceFlinger.h" #include "SurfaceFlingerDefaultFactory.h" @@ -168,7 +168,8 @@ public: // functions. void setupRenderEngine(std::unique_ptr<renderengine::RenderEngine> renderEngine) { - mFlinger->mCompositionEngine->setRenderEngine(std::move(renderEngine)); + mFlinger->mRenderEngine = std::move(renderEngine); + mFlinger->mCompositionEngine->setRenderEngine(mFlinger->mRenderEngine.get()); } void setupComposer(std::unique_ptr<Hwc2::Composer> composer) { @@ -192,10 +193,10 @@ public: static constexpr struct TwoDisplayModes { } kTwoDisplayModes; - using RefreshRateConfigsPtr = std::shared_ptr<scheduler::RefreshRateConfigs>; + using RefreshRateSelectorPtr = std::shared_ptr<scheduler::RefreshRateSelector>; using DisplayModesVariant = - std::variant<OneDisplayMode, TwoDisplayModes, RefreshRateConfigsPtr>; + std::variant<OneDisplayMode, TwoDisplayModes, RefreshRateSelectorPtr>; void setupScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController, std::unique_ptr<scheduler::VSyncTracker> vsyncTracker, @@ -204,9 +205,9 @@ public: SchedulerCallbackImpl callbackImpl = SchedulerCallbackImpl::kNoOp, DisplayModesVariant modesVariant = kOneDisplayMode, bool useNiceMock = false) { - RefreshRateConfigsPtr configs; - if (std::holds_alternative<RefreshRateConfigsPtr>(modesVariant)) { - configs = std::move(std::get<RefreshRateConfigsPtr>(modesVariant)); + RefreshRateSelectorPtr selectorPtr; + if (std::holds_alternative<RefreshRateSelectorPtr>(modesVariant)) { + selectorPtr = std::move(std::get<RefreshRateSelectorPtr>(modesVariant)); } else { constexpr DisplayModeId kModeId60{0}; DisplayModes modes = makeModes(mock::createDisplayMode(kModeId60, 60_Hz)); @@ -216,10 +217,10 @@ public: modes.try_emplace(kModeId90, mock::createDisplayMode(kModeId90, 90_Hz)); } - configs = std::make_shared<scheduler::RefreshRateConfigs>(modes, kModeId60); + selectorPtr = std::make_shared<scheduler::RefreshRateSelector>(modes, kModeId60); } - const auto fps = FTL_FAKE_GUARD(kMainThreadContext, configs->getActiveMode().getFps()); + const auto fps = FTL_FAKE_GUARD(kMainThreadContext, selectorPtr->getActiveMode().getFps()); mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps); mFlinger->mVsyncModulator = sp<scheduler::VsyncModulator>::make( mFlinger->mVsyncConfiguration->getCurrentConfigs()); @@ -237,12 +238,12 @@ public: mScheduler = new testing::NiceMock<scheduler::TestableScheduler>(std::move(vsyncController), std::move(vsyncTracker), - std::move(configs), + std::move(selectorPtr), callback); } else { mScheduler = new scheduler::TestableScheduler(std::move(vsyncController), std::move(vsyncTracker), - std::move(configs), callback); + std::move(selectorPtr), callback); } mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread)); @@ -454,14 +455,9 @@ public: return SurfaceFlinger::calculateMaxAcquiredBufferCount(refreshRate, presentLatency); } - auto setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, ui::DisplayModeId defaultMode, - bool allowGroupSwitching, float primaryRefreshRateMin, - float primaryRefreshRateMax, float appRequestRefreshRateMin, - float appRequestRefreshRateMax) { - return mFlinger->setDesiredDisplayModeSpecs(displayToken, defaultMode, allowGroupSwitching, - primaryRefreshRateMin, primaryRefreshRateMax, - appRequestRefreshRateMin, - appRequestRefreshRateMax); + auto setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, + const gui::DisplayModeSpecs& specs) { + return mFlinger->setDesiredDisplayModeSpecs(displayToken, specs); } void onActiveDisplayChanged(const sp<DisplayDevice>& activeDisplay) { @@ -543,8 +539,8 @@ public: mutableDrawingState().displays.clear(); mFlinger->mScheduler.reset(); mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>()); - mFlinger->mCompositionEngine->setRenderEngine( - std::unique_ptr<renderengine::RenderEngine>()); + mFlinger->mRenderEngine = std::unique_ptr<renderengine::RenderEngine>(); + mFlinger->mCompositionEngine->setRenderEngine(mFlinger->mRenderEngine.get()); } /* ------------------------------------------------------------------------ @@ -756,16 +752,17 @@ public: return mFlinger.mutableDisplays().get(mDisplayToken)->get(); } - // If `configs` is nullptr, the injector creates RefreshRateConfigs from the `modes`. - // Otherwise, it uses `configs`, which the caller must create using the same `modes`. + // If `selectorPtr` is nullptr, the injector creates RefreshRateSelector from the `modes`. + // Otherwise, it uses `selectorPtr`, which the caller must create using the same `modes`. // - // TODO(b/182939859): Once `modes` can be retrieved from RefreshRateConfigs, remove - // the `configs` parameter in favor of an alternative setRefreshRateConfigs API. - auto& setDisplayModes(DisplayModes modes, DisplayModeId activeModeId, - std::shared_ptr<scheduler::RefreshRateConfigs> configs = nullptr) { + // TODO(b/182939859): Once `modes` can be retrieved from RefreshRateSelector, remove + // the `selectorPtr` parameter in favor of an alternative setRefreshRateSelector API. + auto& setDisplayModes( + DisplayModes modes, DisplayModeId activeModeId, + std::shared_ptr<scheduler::RefreshRateSelector> selectorPtr = nullptr) { mDisplayModes = std::move(modes); mCreationArgs.activeModeId = activeModeId; - mCreationArgs.refreshRateConfigs = std::move(configs); + mCreationArgs.refreshRateSelector = std::move(selectorPtr); return *this; } @@ -812,7 +809,7 @@ public: auto& modes = mDisplayModes; auto& activeModeId = mCreationArgs.activeModeId; - if (displayId && !mCreationArgs.refreshRateConfigs) { + if (displayId && !mCreationArgs.refreshRateSelector) { if (const auto physicalId = PhysicalDisplayId::tryCast(*displayId)) { if (modes.empty()) { constexpr DisplayModeId kModeId{0}; @@ -832,15 +829,16 @@ public: activeModeId = kModeId; } - mCreationArgs.refreshRateConfigs = - std::make_shared<scheduler::RefreshRateConfigs>(modes, activeModeId); + mCreationArgs.refreshRateSelector = + std::make_shared<scheduler::RefreshRateSelector>(modes, activeModeId); } } sp<DisplayDevice> display = sp<DisplayDevice>::make(mCreationArgs); mFlinger.mutableDisplays().emplace_or_replace(mDisplayToken, display); if (mFlinger.scheduler()) { - mFlinger.scheduler()->registerDisplay(display); + mFlinger.scheduler()->registerDisplay(display->getPhysicalId(), + display->holdRefreshRateSelector()); } DisplayDeviceState state; diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h index 439f6f4e75..f4ded216cb 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h @@ -23,6 +23,7 @@ using android::binder::Status; using android::hardware::power::IPowerHintSession; +using android::hardware::power::SessionHint; using namespace android::hardware::power; @@ -40,6 +41,8 @@ public: MOCK_METHOD(std::string, getInterfaceHash, (), (override)); MOCK_METHOD(Status, updateTargetWorkDuration, (int64_t), (override)); MOCK_METHOD(Status, reportActualWorkDuration, (const ::std::vector<WorkDuration>&), (override)); + MOCK_METHOD(Status, sendHint, (SessionHint), (override)); + MOCK_METHOD(Status, setThreads, (const ::std::vector<int32_t>&), (override)); }; } // namespace android::Hwc2::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockDisplayModeSpecs.h b/services/surfaceflinger/tests/unittests/mock/MockDisplayModeSpecs.h new file mode 100644 index 0000000000..a71e82cc75 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/mock/MockDisplayModeSpecs.h @@ -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. + */ + +#pragma once + +#include <android/gui/DisplayModeSpecs.h> + +namespace android::mock { + +inline gui::DisplayModeSpecs createDisplayModeSpecs(int32_t defaultMode, bool allowGroupSwitching, + float minFps, float maxFps) { + gui::DisplayModeSpecs specs; + specs.defaultMode = defaultMode; + specs.allowGroupSwitching = allowGroupSwitching; + specs.primaryRanges.physical.min = minFps; + specs.primaryRanges.physical.max = maxFps; + specs.primaryRanges.render = specs.primaryRanges.physical; + specs.appRequestRanges = specs.primaryRanges; + return specs; +} + +} // namespace android::mock |