summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--aidl/gui/android/view/Surface.aidl2
-rw-r--r--cmds/servicemanager/ServiceManager.cpp3
-rw-r--r--data/etc/android.software.opengles.deqp.level-2023-03-01.xml21
-rw-r--r--data/etc/android.software.vulkan.deqp.level-2023-03-01.xml21
-rw-r--r--include/android/font.h3
-rw-r--r--include/android/font_matcher.h3
-rw-r--r--include/android/system_fonts.h3
-rw-r--r--include/input/Input.h5
-rw-r--r--libs/binder/ndk/include_cpp/android/binder_auto_utils.h9
-rw-r--r--libs/binder/ndk/tests/binderVendorDoubleLoadTest.cpp2
-rw-r--r--libs/binder/rust/Android.bp1
-rw-r--r--libs/binder/rust/src/lib.rs1
-rw-r--r--libs/binder/rust/tests/parcel_fuzzer/Android.bp25
-rw-r--r--libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs161
-rw-r--r--libs/binder/rust/tests/parcel_fuzzer/random_parcel/Android.bp52
-rw-r--r--libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp33
-rw-r--r--libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/ITestService.aidl19
-rw-r--r--libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/service_fuzzer.rs39
-rw-r--r--libs/binder/rust/tests/parcel_fuzzer/random_parcel/src/lib.rs44
-rw-r--r--libs/binder/rust/tests/parcel_fuzzer/random_parcel/wrappers/RandomParcelWrapper.hpp25
-rw-r--r--libs/binder/rust/tests/parcel_fuzzer/read_utils.rs133
-rw-r--r--libs/binder/tests/parcel_fuzzer/Android.bp3
-rw-r--r--libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp9
-rw-r--r--libs/binder/tests/parcel_fuzzer/rust_interface/Android.bp24
-rw-r--r--libs/binder/tests/parcel_fuzzer/rust_interface/RandomParcelWrapper.cpp34
-rw-r--r--libs/gralloc/types/Android.bp2
-rw-r--r--libs/gui/BLASTBufferQueue.cpp22
-rw-r--r--libs/gui/SurfaceComposerClient.cpp34
-rw-r--r--libs/gui/aidl/android/gui/DisplayModeSpecs.aidl56
-rw-r--r--libs/gui/aidl/android/gui/ISurfaceComposer.aidl22
-rw-r--r--libs/gui/fuzzer/Android.bp2
-rw-r--r--libs/gui/fuzzer/libgui_fuzzer_utils.h4
-rw-r--r--libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp34
-rw-r--r--libs/gui/include/gui/BLASTBufferQueue.h3
-rw-r--r--libs/gui/include/gui/Surface.h18
-rw-r--r--libs/gui/include/gui/SurfaceComposerClient.h13
-rw-r--r--libs/gui/tests/Surface_test.cpp8
-rw-r--r--libs/input/Input.cpp8
-rw-r--r--libs/jpegrecoverymap/Android.bp7
-rw-r--r--libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h23
-rw-r--r--libs/jpegrecoverymap/include/jpegrecoverymap/jpegencoder.h9
-rw-r--r--libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h48
-rw-r--r--libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h136
-rw-r--r--libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h106
-rw-r--r--libs/jpegrecoverymap/jpegdecoder.cpp15
-rw-r--r--libs/jpegrecoverymap/jpegencoder.cpp4
-rw-r--r--libs/jpegrecoverymap/recoverymap.cpp416
-rw-r--r--libs/jpegrecoverymap/recoverymapmath.cpp169
-rw-r--r--libs/jpegrecoverymap/tests/Android.bp2
-rw-r--r--libs/nativewindow/ANativeWindow.cpp48
-rw-r--r--libs/nativewindow/Android.bp2
-rw-r--r--libs/nativewindow/include/android/native_window.h2
-rw-r--r--libs/nativewindow/include/android/native_window_aidl.h161
-rw-r--r--libs/nativewindow/libnativewindow.map.txt2
-rw-r--r--libs/renderengine/RenderEngine.cpp13
-rw-r--r--libs/renderengine/gl/GLESRenderEngine.h2
-rw-r--r--libs/renderengine/include/renderengine/RenderEngine.h11
-rw-r--r--libs/renderengine/skia/SkiaRenderEngine.h3
-rw-r--r--libs/renderengine/tests/RenderEngineThreadedTest.cpp98
-rw-r--r--libs/renderengine/threaded/RenderEngineThreaded.cpp32
-rw-r--r--libs/renderengine/threaded/RenderEngineThreaded.h6
-rw-r--r--libs/vibrator/ExternalVibration.cpp29
-rw-r--r--libs/vibrator/include/vibrator/ExternalVibration.h7
-rw-r--r--libs/vibrator/include/vibrator/ExternalVibrationUtils.h2
-rw-r--r--services/inputflinger/InputCommonConverter.cpp4
-rw-r--r--services/inputflinger/dispatcher/InputDispatcher.cpp222
-rw-r--r--services/inputflinger/dispatcher/InputDispatcher.h8
-rw-r--r--services/inputflinger/dispatcher/TouchState.cpp20
-rw-r--r--services/inputflinger/dispatcher/TouchState.h9
-rw-r--r--services/inputflinger/reader/Android.bp2
-rw-r--r--services/inputflinger/reader/EventHub.cpp36
-rw-r--r--services/inputflinger/reader/InputDevice.cpp5
-rw-r--r--services/inputflinger/reader/include/EventHub.h25
-rw-r--r--services/inputflinger/reader/include/InputDevice.h7
-rw-r--r--services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp6
-rw-r--r--services/inputflinger/reader/mapper/KeyboardInputMapper.cpp234
-rw-r--r--services/inputflinger/reader/mapper/KeyboardInputMapper.h49
-rw-r--r--services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp157
-rw-r--r--services/inputflinger/reader/mapper/MultiTouchInputMapper.h69
-rw-r--r--services/inputflinger/reader/mapper/TouchInputMapper.cpp204
-rw-r--r--services/inputflinger/reader/mapper/TouchInputMapper.h194
-rw-r--r--services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.cpp39
-rw-r--r--services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.h40
-rw-r--r--services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp176
-rw-r--r--services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h96
-rw-r--r--services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp92
-rw-r--r--services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h53
-rw-r--r--services/inputflinger/tests/InputReader_test.cpp353
-rw-r--r--services/inputflinger/tests/TestInputListener.cpp6
-rw-r--r--services/inputflinger/tests/TestInputListener.h2
-rw-r--r--services/inputflinger/tests/TestInputListenerMatchers.h18
-rw-r--r--services/inputflinger/tests/UinputDevice.cpp34
-rw-r--r--services/inputflinger/tests/UinputDevice.h48
-rw-r--r--services/stats/StatsAidl.cpp29
-rw-r--r--services/surfaceflinger/Android.bp4
-rw-r--r--services/surfaceflinger/Client.cpp35
-rw-r--r--services/surfaceflinger/Client.h9
-rw-r--r--services/surfaceflinger/CompositionEngine/Android.bp2
-rw-r--r--services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h4
-rw-r--r--services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h6
-rw-r--r--services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h4
-rw-r--r--services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp10
-rw-r--r--services/surfaceflinger/CompositionEngine/src/Display.cpp22
-rw-r--r--services/surfaceflinger/CompositionEngine/src/Output.cpp19
-rw-r--r--services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp9
-rw-r--r--services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp67
-rw-r--r--services/surfaceflinger/DisplayDevice.cpp18
-rw-r--r--services/surfaceflinger/DisplayDevice.h19
-rw-r--r--services/surfaceflinger/Layer.cpp13
-rw-r--r--services/surfaceflinger/LayerFE.cpp4
-rw-r--r--services/surfaceflinger/LayerFE.h1
-rw-r--r--services/surfaceflinger/RegionSamplingThread.cpp9
-rw-r--r--services/surfaceflinger/RegionSamplingThread.h5
-rw-r--r--services/surfaceflinger/Scheduler/LayerHistory.cpp4
-rw-r--r--services/surfaceflinger/Scheduler/LayerHistory.h8
-rw-r--r--services/surfaceflinger/Scheduler/LayerInfo.cpp10
-rw-r--r--services/surfaceflinger/Scheduler/LayerInfo.h6
-rw-r--r--services/surfaceflinger/Scheduler/RefreshRateSelector.cpp (renamed from services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp)253
-rw-r--r--services/surfaceflinger/Scheduler/RefreshRateSelector.h (renamed from services/surfaceflinger/Scheduler/RefreshRateConfigs.h)93
-rw-r--r--services/surfaceflinger/Scheduler/Scheduler.cpp107
-rw-r--r--services/surfaceflinger/Scheduler/Scheduler.h46
-rw-r--r--services/surfaceflinger/Scheduler/VSyncPredictor.cpp4
-rw-r--r--services/surfaceflinger/Scheduler/include/scheduler/Fps.h31
-rw-r--r--services/surfaceflinger/SurfaceFlinger.cpp245
-rw-r--r--services/surfaceflinger/SurfaceFlinger.h32
-rw-r--r--services/surfaceflinger/SurfaceFlingerFactory.h1
-rw-r--r--services/surfaceflinger/SurfaceFlingerProperties.cpp4
-rw-r--r--services/surfaceflinger/SurfaceFlingerProperties.h2
-rw-r--r--services/surfaceflinger/Tracing/LayerTracing.h1
-rw-r--r--services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp1
-rw-r--r--services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h51
-rw-r--r--services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp63
-rw-r--r--services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h1
-rw-r--r--services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop12
-rw-r--r--services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt4
-rw-r--r--services/surfaceflinger/tests/Credentials_test.cpp19
-rw-r--r--services/surfaceflinger/tests/DisplayConfigs_test.cpp92
-rw-r--r--services/surfaceflinger/tests/unittests/Android.bp4
-rw-r--r--services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp22
-rw-r--r--services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp (renamed from services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp)1263
-rw-r--r--services/surfaceflinger/tests/unittests/SchedulerTest.cpp81
-rw-r--r--services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp4
-rw-r--r--services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp38
-rw-r--r--services/surfaceflinger/tests/unittests/TestableScheduler.h19
-rw-r--r--services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h62
-rw-r--r--services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h3
-rw-r--r--services/surfaceflinger/tests/unittests/mock/MockDisplayModeSpecs.h35
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