Merge "Wait for buffers to be latched in ChildLayerTest"
diff --git a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
index 85e6969..50c1624 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
@@ -21,8 +21,6 @@
*
* <p>When bugreport creation is complete one of {@code onError} or {@code onFinished} is called.
*
- * <p>These methods are synchronous by design in order to make dumpstate's lifecycle simpler
- * to handle.
*
* {@hide}
*/
@@ -54,10 +52,8 @@
/**
* Called on an error condition with one of the error codes listed above.
- * This is not an asynchronous method since it can race with dumpstate exiting, thus triggering
- * death recipient.
*/
- void onError(int errorCode);
+ oneway void onError(int errorCode);
/**
* Called when taking bugreport finishes successfully.
diff --git a/include/android/thermal.h b/include/android/thermal.h
index eb81534..0ea13d3 100644
--- a/include/android/thermal.h
+++ b/include/android/thermal.h
@@ -109,8 +109,6 @@
*/
typedef void (*AThermal_StatusCallback)(void *data, AThermalStatus status);
-#if __ANDROID_API__ >= 30
-
/**
* Acquire an instance of the thermal manager. This must be freed using
* {@link AThermal_releaseManager}.
@@ -179,10 +177,6 @@
int AThermal_unregisterThermalStatusListener(AThermalManager *manager,
AThermal_StatusCallback callback, void *data) __INTRODUCED_IN(30);
-#endif // __ANDROID_API__ >= 30
-
-#if __ANDROID_API__ >= 31
-
/**
* Provides an estimate of how much thermal headroom the device currently has before
* hitting severe throttling.
@@ -224,8 +218,6 @@
float AThermal_getThermalHeadroom(AThermalManager *manager,
int forecastSeconds) __INTRODUCED_IN(31);
-#endif // __ANDROID_API__ >= 31
-
#ifdef __cplusplus
}
#endif
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index b038feb..5c34069 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -486,6 +486,19 @@
}
}
+bool IPCThreadState::flushIfNeeded()
+{
+ if (mIsLooper || mServingStackPointer != nullptr) {
+ return false;
+ }
+ // In case this thread is not a looper and is not currently serving a binder transaction,
+ // there's no guarantee that this thread will call back into the kernel driver any time
+ // soon. Therefore, flush pending commands such as BC_FREE_BUFFER, to prevent them from getting
+ // stuck in this thread's out buffer.
+ flushCommands();
+ return true;
+}
+
void IPCThreadState::blockUntilThreadAvailable()
{
pthread_mutex_lock(&mProcess->mThreadCountLock);
@@ -604,6 +617,7 @@
mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);
+ mIsLooper = true;
status_t result;
do {
processPendingDerefs();
@@ -626,6 +640,7 @@
(void*)pthread_self(), getpid(), result);
mOut.writeInt32(BC_EXIT_LOOPER);
+ mIsLooper = false;
talkWithDriver(false);
}
@@ -739,9 +754,11 @@
LOG_REMOTEREFS("IPCThreadState::incStrongHandle(%d)\n", handle);
mOut.writeInt32(BC_ACQUIRE);
mOut.writeInt32(handle);
- // Create a temp reference until the driver has handled this command.
- proxy->incStrong(mProcess.get());
- mPostWriteStrongDerefs.push(proxy);
+ if (!flushIfNeeded()) {
+ // Create a temp reference until the driver has handled this command.
+ proxy->incStrong(mProcess.get());
+ mPostWriteStrongDerefs.push(proxy);
+ }
}
void IPCThreadState::decStrongHandle(int32_t handle)
@@ -749,6 +766,7 @@
LOG_REMOTEREFS("IPCThreadState::decStrongHandle(%d)\n", handle);
mOut.writeInt32(BC_RELEASE);
mOut.writeInt32(handle);
+ flushIfNeeded();
}
void IPCThreadState::incWeakHandle(int32_t handle, BpBinder *proxy)
@@ -756,9 +774,11 @@
LOG_REMOTEREFS("IPCThreadState::incWeakHandle(%d)\n", handle);
mOut.writeInt32(BC_INCREFS);
mOut.writeInt32(handle);
- // Create a temp reference until the driver has handled this command.
- proxy->getWeakRefs()->incWeak(mProcess.get());
- mPostWriteWeakDerefs.push(proxy->getWeakRefs());
+ if (!flushIfNeeded()) {
+ // Create a temp reference until the driver has handled this command.
+ proxy->getWeakRefs()->incWeak(mProcess.get());
+ mPostWriteWeakDerefs.push(proxy->getWeakRefs());
+ }
}
void IPCThreadState::decWeakHandle(int32_t handle)
@@ -766,6 +786,7 @@
LOG_REMOTEREFS("IPCThreadState::decWeakHandle(%d)\n", handle);
mOut.writeInt32(BC_DECREFS);
mOut.writeInt32(handle);
+ flushIfNeeded();
}
status_t IPCThreadState::attemptIncStrongHandle(int32_t handle)
@@ -821,6 +842,7 @@
mServingStackPointer(nullptr),
mWorkSource(kUnsetWorkSource),
mPropagateWorkSource(false),
+ mIsLooper(false),
mStrictModePolicy(0),
mLastTransactionBinderFlags(0),
mCallRestriction(mProcess->mCallRestriction)
@@ -1401,6 +1423,7 @@
IPCThreadState* state = self();
state->mOut.writeInt32(BC_FREE_BUFFER);
state->mOut.writePointer((uintptr_t)data);
+ state->flushIfNeeded();
}
} // namespace android
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index 00a14f4..2e90142 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -65,6 +65,9 @@
},
{
"name": "binderRustNdkInteropTest"
+ },
+ {
+ "name": "rustBinderSerializationTest"
}
]
}
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index e2a0f87..f930d29 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -240,17 +240,9 @@
"android.hardware.ICameraRecordingProxyListener",
"android.hardware.ICrypto",
"android.hardware.IOMXObserver",
- "android.hardware.ISoundTrigger",
- "android.hardware.ISoundTriggerClient",
- "android.hardware.ISoundTriggerHwService",
"android.hardware.IStreamListener",
"android.hardware.IStreamSource",
- "android.media.IAudioFlinger",
- "android.media.IAudioFlingerClient",
- "android.media.IAudioPolicyService",
- "android.media.IAudioPolicyServiceClient",
"android.media.IAudioService",
- "android.media.IAudioTrack",
"android.media.IDataSource",
"android.media.IDrmClient",
"android.media.IMediaCodecList",
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 4da8aa1..0183324 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -110,6 +110,7 @@
status_t setupPolling(int* fd);
status_t handlePolledCommands();
void flushCommands();
+ bool flushIfNeeded();
void joinThreadPool(bool isMain = true);
@@ -204,6 +205,7 @@
int32_t mWorkSource;
// Whether the work source should be propagated.
bool mPropagateWorkSource;
+ bool mIsLooper;
int32_t mStrictModePolicy;
int32_t mLastTransactionBinderFlags;
CallRestriction mCallRestriction;
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index b7df115..0d1989e 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -375,8 +375,7 @@
AIBinder_decStrong(binder);
- // assert because would need to decStrong if non-null and we shouldn't need to add a no-op here
- ASSERT_NE(nullptr, AIBinder_Weak_promote(wBinder));
+ ASSERT_EQ(nullptr, AIBinder_Weak_promote(wBinder));
AIBinder_Weak_delete(wBinder);
}
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index ed3b9ec..42c1e0a 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -613,7 +613,7 @@
impl $crate::parcel::Serialize for dyn $interface + '_
where
- $interface: $crate::Interface
+ dyn $interface: $crate::Interface
{
fn serialize(&self, parcel: &mut $crate::parcel::Parcel) -> $crate::Result<()> {
let binder = $crate::Interface::as_binder(self);
diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs
index 138b360..8d18fb4 100644
--- a/libs/binder/rust/src/parcel/parcelable.rs
+++ b/libs/binder/rust/src/parcel/parcelable.rs
@@ -15,15 +15,17 @@
*/
use crate::binder::{AsNative, FromIBinder};
-use crate::error::{status_result, Result, Status, StatusCode};
+use crate::error::{status_result, status_t, Result, Status, StatusCode};
use crate::parcel::Parcel;
use crate::proxy::SpIBinder;
use crate::sys;
use std::convert::TryInto;
use std::ffi::c_void;
-use std::os::raw::c_char;
+use std::os::raw::{c_char, c_ulong};
+use std::mem::{self, MaybeUninit};
use std::ptr;
+use std::slice;
/// A struct whose instances can be written to a [`Parcel`].
// Might be able to hook this up as a serde backend in the future?
@@ -49,38 +51,109 @@
pub trait SerializeArray: Serialize + Sized {
/// Serialize an array of this type into the given [`Parcel`].
fn serialize_array(slice: &[Self], parcel: &mut Parcel) -> Result<()> {
- parcel.write_slice_size(Some(slice))?;
-
- for item in slice {
- parcel.write(item)?;
- }
-
- Ok(())
+ let res = unsafe {
+ // Safety: Safe FFI, slice will always be a safe pointer to pass.
+ sys::AParcel_writeParcelableArray(
+ parcel.as_native_mut(),
+ slice.as_ptr() as *const c_void,
+ slice.len().try_into().or(Err(StatusCode::BAD_VALUE))?,
+ Some(serialize_element::<Self>),
+ )
+ };
+ status_result(res)
}
}
+/// Callback to serialize an element of a generic parcelable array.
+///
+/// Safety: We are relying on binder_ndk to not overrun our slice. As long as it
+/// doesn't provide an index larger than the length of the original slice in
+/// serialize_array, this operation is safe. The index provided is zero-based.
+unsafe extern "C" fn serialize_element<T: Serialize>(
+ parcel: *mut sys::AParcel,
+ array: *const c_void,
+ index: c_ulong,
+) -> status_t {
+ // c_ulong and usize are the same, but we need the explicitly sized version
+ // so the function signature matches what bindgen generates.
+ let index = index as usize;
+
+ let slice: &[T] = slice::from_raw_parts(array.cast(), index+1);
+
+ let mut parcel = match Parcel::borrowed(parcel) {
+ None => return StatusCode::UNEXPECTED_NULL as status_t,
+ Some(p) => p,
+ };
+
+ slice[index].serialize(&mut parcel)
+ .err()
+ .unwrap_or(StatusCode::OK)
+ as status_t
+}
+
/// Helper trait for types that can be deserialized as arrays.
/// Defaults to calling Deserialize::deserialize() manually for every element,
/// but can be overridden for custom implementations like `readByteArray`.
pub trait DeserializeArray: Deserialize {
/// Deserialize an array of type from the given [`Parcel`].
fn deserialize_array(parcel: &Parcel) -> Result<Option<Vec<Self>>> {
- let len: i32 = parcel.read()?;
- if len < 0 {
- return Ok(None);
- }
-
- // TODO: Assumes that usize is at least 32 bits
- let mut vec = Vec::with_capacity(len as usize);
-
- for _ in 0..len {
- vec.push(parcel.read()?);
- }
-
- Ok(Some(vec))
+ let mut vec: Option<Vec<MaybeUninit<Self>>> = None;
+ let res = unsafe {
+ // Safety: Safe FFI, vec is the correct opaque type expected by
+ // allocate_vec and deserialize_element.
+ sys::AParcel_readParcelableArray(
+ parcel.as_native(),
+ &mut vec as *mut _ as *mut c_void,
+ Some(allocate_vec::<Self>),
+ Some(deserialize_element::<Self>),
+ )
+ };
+ status_result(res)?;
+ let vec: Option<Vec<Self>> = unsafe {
+ // Safety: We are assuming that the NDK correctly initialized every
+ // element of the vector by now, so we know that all the
+ // MaybeUninits are now properly initialized. We can transmute from
+ // Vec<MaybeUninit<T>> to Vec<T> because MaybeUninit<T> has the same
+ // alignment and size as T, so the pointer to the vector allocation
+ // will be compatible.
+ mem::transmute(vec)
+ };
+ Ok(vec)
}
}
+/// Callback to deserialize a parcelable element.
+///
+/// The opaque array data pointer must be a mutable pointer to an
+/// `Option<Vec<MaybeUninit<T>>>` with at least enough elements for `index` to be valid
+/// (zero-based).
+unsafe extern "C" fn deserialize_element<T: Deserialize>(
+ parcel: *const sys::AParcel,
+ array: *mut c_void,
+ index: c_ulong,
+) -> status_t {
+ // c_ulong and usize are the same, but we need the explicitly sized version
+ // so the function signature matches what bindgen generates.
+ let index = index as usize;
+
+ let vec = &mut *(array as *mut Option<Vec<MaybeUninit<T>>>);
+ let vec = match vec {
+ Some(v) => v,
+ None => return StatusCode::BAD_INDEX as status_t,
+ };
+
+ let parcel = match Parcel::borrowed(parcel as *mut _) {
+ None => return StatusCode::UNEXPECTED_NULL as status_t,
+ Some(p) => p,
+ };
+ let element = match parcel.read() {
+ Ok(e) => e,
+ Err(code) => return code as status_t,
+ };
+ ptr::write(vec[index].as_mut_ptr(), element);
+ StatusCode::OK as status_t
+}
+
/// Helper trait for types that can be nullable when serialized.
// We really need this trait instead of implementing `Serialize for Option<T>`
// because of the Rust orphan rule which prevents us from doing
@@ -115,28 +188,54 @@
/// Callback to allocate a vector for parcel array read functions.
///
+/// This variant is for APIs which use an out buffer pointer.
+///
/// # Safety
///
/// The opaque data pointer passed to the array read function must be a mutable
-/// pointer to an `Option<Vec<T>>`. `buffer` will be assigned a mutable pointer
+/// pointer to an `Option<Vec<MaybeUninit<T>>>`. `buffer` will be assigned a mutable pointer
/// to the allocated vector data if this function returns true.
-unsafe extern "C" fn allocate_vec<T: Clone + Default>(
+unsafe extern "C" fn allocate_vec_with_buffer<T>(
data: *mut c_void,
len: i32,
buffer: *mut *mut T,
) -> bool {
- let vec = &mut *(data as *mut Option<Vec<T>>);
+ let res = allocate_vec::<T>(data, len);
+ let vec = &mut *(data as *mut Option<Vec<MaybeUninit<T>>>);
+ if let Some(new_vec) = vec {
+ *buffer = new_vec.as_mut_ptr() as *mut T;
+ }
+ res
+}
+
+/// Callback to allocate a vector for parcel array read functions.
+///
+/// # Safety
+///
+/// The opaque data pointer passed to the array read function must be a mutable
+/// pointer to an `Option<Vec<MaybeUninit<T>>>`.
+unsafe extern "C" fn allocate_vec<T>(
+ data: *mut c_void,
+ len: i32,
+) -> bool {
+ let vec = &mut *(data as *mut Option<Vec<MaybeUninit<T>>>);
if len < 0 {
*vec = None;
return true;
}
- let mut new_vec: Vec<T> = Vec::with_capacity(len as usize);
- new_vec.resize_with(len as usize, Default::default);
- *buffer = new_vec.as_mut_ptr();
- *vec = Some(new_vec);
+ let mut new_vec: Vec<MaybeUninit<T>> = Vec::with_capacity(len as usize);
+
+ // Safety: We are filling the vector with uninitialized data here, but this
+ // is safe because the vector contains MaybeUninit elements which can be
+ // uninitialized. We're putting off the actual unsafe bit, transmuting the
+ // vector to a Vec<T> until the contents are initialized.
+ new_vec.set_len(len as usize);
+
+ ptr::write(vec, Some(new_vec));
true
}
+
macro_rules! parcelable_primitives {
{
$(
@@ -204,19 +303,29 @@
{DeserializeArray, $ty:ty, $read_array_fn:path} => {
impl DeserializeArray for $ty {
fn deserialize_array(parcel: &Parcel) -> Result<Option<Vec<Self>>> {
- let mut vec: Option<Vec<Self>> = None;
+ let mut vec: Option<Vec<MaybeUninit<Self>>> = None;
let status = unsafe {
// Safety: `Parcel` always contains a valid pointer to an
// `AParcel`. `allocate_vec<T>` expects the opaque pointer to
- // be of type `*mut Option<Vec<T>>`, so `&mut vec` is
+ // be of type `*mut Option<Vec<MaybeUninit<T>>>`, so `&mut vec` is
// correct for it.
$read_array_fn(
parcel.as_native(),
&mut vec as *mut _ as *mut c_void,
- Some(allocate_vec),
+ Some(allocate_vec_with_buffer),
)
};
status_result(status)?;
+ let vec: Option<Vec<Self>> = unsafe {
+ // Safety: We are assuming that the NDK correctly
+ // initialized every element of the vector by now, so we
+ // know that all the MaybeUninits are now properly
+ // initialized. We can transmute from Vec<MaybeUninit<T>> to
+ // Vec<T> because MaybeUninit<T> has the same alignment and
+ // size as T, so the pointer to the vector allocation will
+ // be compatible.
+ mem::transmute(vec)
+ };
Ok(vec)
}
}
@@ -414,7 +523,7 @@
sys::AParcel_readString(
parcel.as_native(),
&mut vec as *mut _ as *mut c_void,
- Some(allocate_vec),
+ Some(allocate_vec_with_buffer),
)
};
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index 17af099..e9e74c0 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -70,6 +70,20 @@
ptr.as_mut().map(|p| Self(p))
}
+ /// Extract a raw `AIBinder` pointer from this wrapper.
+ ///
+ /// This method should _only_ be used for testing. Do not try to use the NDK
+ /// interface directly for anything else.
+ ///
+ /// # Safety
+ ///
+ /// The resulting pointer is valid only as long as the SpIBinder is alive.
+ /// The SpIBinder object retains ownership of the AIBinder and the caller
+ /// should not attempt to free the returned pointer.
+ pub unsafe fn as_raw(&self) -> *mut sys::AIBinder {
+ self.0
+ }
+
/// Return true if this binder object is hosted in a different process than
/// the current one.
pub fn is_remote(&self) -> bool {
diff --git a/libs/binder/rust/tests/Android.bp b/libs/binder/rust/tests/Android.bp
index 5ae9c53..8810b5d 100644
--- a/libs/binder/rust/tests/Android.bp
+++ b/libs/binder/rust/tests/Android.bp
@@ -79,3 +79,50 @@
"IBinderRustNdkInteropTest-rust",
],
}
+
+cc_test {
+ name: "rustBinderSerializationTest",
+ shared_libs: [
+ "libbinder",
+ "libbinder_ndk",
+ "libutils",
+ "libbase",
+ ],
+ static_libs: [
+ "libbinder_rs_serialization_test"
+ ],
+ srcs: [
+ "serialization.cpp",
+ ],
+ auto_gen_config: true,
+ test_suites: ["general-tests"],
+}
+
+rust_bindgen {
+ name: "libbinder_rs_serialization_bindgen",
+ crate_name: "binder_rs_serialization_bindgen",
+ wrapper_src: "serialization.hpp",
+ source_stem: "bindings",
+ cpp_std: "gnu++17",
+ bindgen_flags: [
+ "--whitelist-type", "Transaction",
+ "--whitelist-var", "TESTDATA_.*",
+ ],
+
+ shared_libs: [
+ "libbinder",
+ "libc++",
+ ],
+}
+
+rust_ffi_static {
+ name: "libbinder_rs_serialization_test",
+ crate_name: "binder_rs_serialization_test",
+ srcs: [
+ "serialization.rs",
+ ":libbinder_rs_serialization_bindgen",
+ ],
+ rustlibs: [
+ "libbinder_rs",
+ ],
+}
diff --git a/libs/binder/rust/tests/serialization.cpp b/libs/binder/rust/tests/serialization.cpp
new file mode 100644
index 0000000..ec780f2
--- /dev/null
+++ b/libs/binder/rust/tests/serialization.cpp
@@ -0,0 +1,454 @@
+/*
+ * Copyright (C) 2020 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/binder_ibinder_platform.h>
+#include <android/binder_libbinder.h>
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/ParcelFileDescriptor.h>
+#include <binder/ProcessState.h>
+#include <binder/Status.h>
+#include <gtest/gtest.h>
+#include <utils/Errors.h>
+#include <utils/String16.h>
+#include "android-base/file.h"
+#include "serialization.hpp"
+
+#include <cmath>
+#include <cstdint>
+#include <iostream>
+#include <optional>
+
+using namespace std;
+using namespace android;
+using android::base::unique_fd;
+using android::os::ParcelFileDescriptor;
+
+// defined in Rust
+extern "C" AIBinder *rust_service();
+
+
+const int8_t TESTDATA_I8[4] = {-128, 0, 117, 127};
+const uint8_t TESTDATA_U8[4] = {0, 42, 117, 255};
+const char16_t TESTDATA_CHARS[4] = {0, 42, 117, numeric_limits<char16_t>::max()};
+const int32_t TESTDATA_I32[4] = {numeric_limits<int32_t>::min(), 0, 117, numeric_limits<int32_t>::max()};
+const int64_t TESTDATA_I64[4] = {numeric_limits<int64_t>::min(), 0, 117, numeric_limits<int64_t>::max()};
+const uint64_t TESTDATA_U64[4] = {0, 42, 117, numeric_limits<uint64_t>::max()};
+const float TESTDATA_FLOAT[4] = {
+ numeric_limits<float>::quiet_NaN(),
+ -numeric_limits<float>::infinity(),
+ 117.0,
+ numeric_limits<float>::infinity(),
+};
+const double TESTDATA_DOUBLE[4] = {
+ numeric_limits<double>::quiet_NaN(),
+ -numeric_limits<double>::infinity(),
+ 117.0,
+ numeric_limits<double>::infinity(),
+};
+const bool TESTDATA_BOOL[4] = {true, false, false, true};
+const char* const TESTDATA_STRS[4] = {"", nullptr, "test", ""};
+
+static ::testing::Environment* gEnvironment;
+
+class SerializationEnvironment : public ::testing::Environment {
+public:
+ void SetUp() override {
+ m_server = AIBinder_toPlatformBinder(rust_service());
+ }
+
+ sp<IBinder> getServer(void) { return m_server; }
+
+private:
+ sp<IBinder> m_server;
+};
+
+
+class SerializationTest : public ::testing::Test {
+protected:
+ void SetUp() override {
+ ASSERT_NE(gEnvironment, nullptr);
+ m_server = static_cast<SerializationEnvironment *>(gEnvironment)->getServer();
+ }
+
+ sp<IBinder> m_server;
+};
+
+
+TEST_F(SerializationTest, SerializeBool) {
+ android::Parcel data;
+ data.writeInterfaceToken(String16("read_parcel_test"));
+
+ vector<bool> bools(begin(TESTDATA_BOOL), end(TESTDATA_BOOL));
+ ASSERT_EQ(data.writeBool(true), OK);
+ ASSERT_EQ(data.writeBool(false), OK);
+ ASSERT_EQ(data.writeBoolVector(bools), OK);
+ ASSERT_EQ(data.writeBoolVector(nullopt), OK);
+
+ android::Parcel reply;
+ ASSERT_EQ(m_server->transact(TEST_BOOL, data, &reply), OK);
+
+ vector<bool> read_bools;
+ optional<vector<bool>> maybe_bools;
+ ASSERT_EQ(reply.readBool(), true);
+ ASSERT_EQ(reply.readBool(), false);
+ ASSERT_EQ(reply.readBoolVector(&read_bools), OK);
+ ASSERT_EQ(read_bools, bools);
+ ASSERT_EQ(reply.readBoolVector(&maybe_bools), OK);
+ ASSERT_EQ(maybe_bools, nullopt);
+
+ int32_t end;
+ ASSERT_EQ(reply.readInt32(&end), NOT_ENOUGH_DATA);
+}
+
+TEST_F(SerializationTest, SerializeByte) {
+ android::Parcel data;
+ data.writeInterfaceToken(String16("read_parcel_test"));
+
+ vector<int8_t> i8s(begin(TESTDATA_I8), end(TESTDATA_I8));
+ vector<uint8_t> u8s(begin(TESTDATA_U8), end(TESTDATA_U8));
+ data.writeByte(0);
+ data.writeByte(1);
+ data.writeByte(numeric_limits<int8_t>::max());
+ data.writeByteVector(i8s);
+ data.writeByteVector(u8s);
+ data.writeByteVector(optional<vector<int8_t>>({}));
+
+ android::Parcel reply;
+ ASSERT_EQ(m_server->transact(TEST_BYTE, data, &reply), OK);
+
+ vector<int8_t> read_i8s;
+ vector<uint8_t> read_u8s;
+ optional<vector<int8_t>> maybe_i8s;
+ ASSERT_EQ(reply.readByte(), 0);
+ ASSERT_EQ(reply.readByte(), 1);
+ ASSERT_EQ(reply.readByte(), numeric_limits<int8_t>::max());
+ ASSERT_EQ(reply.readByteVector(&read_i8s), OK);
+ ASSERT_EQ(read_i8s, i8s);
+ ASSERT_EQ(reply.readByteVector(&read_u8s), OK);
+ ASSERT_EQ(read_u8s, u8s);
+ ASSERT_EQ(reply.readByteVector(&maybe_i8s), OK);
+ ASSERT_EQ(maybe_i8s, nullopt);
+
+ int32_t end;
+ ASSERT_EQ(reply.readInt32(&end), NOT_ENOUGH_DATA);
+}
+
+TEST_F(SerializationTest, SerializeU16) {
+ android::Parcel data;
+ data.writeInterfaceToken(String16("read_parcel_test"));
+
+ vector<char16_t> chars(begin(TESTDATA_CHARS), end(TESTDATA_CHARS));
+ data.writeChar(0);
+ data.writeChar(1);
+ data.writeChar(numeric_limits<char16_t>::max());
+ data.writeCharVector(chars);
+ data.writeCharVector(nullopt);
+
+ android::Parcel reply;
+ ASSERT_EQ(m_server->transact(TEST_U16, data, &reply), OK);
+
+ vector<char16_t> read_chars;
+ optional<vector<char16_t>> maybe_chars;
+ ASSERT_EQ(reply.readChar(), 0);
+ ASSERT_EQ(reply.readChar(), 1);
+ ASSERT_EQ(reply.readChar(), numeric_limits<char16_t>::max());
+ ASSERT_EQ(reply.readCharVector(&read_chars), OK);
+ ASSERT_EQ(read_chars, chars);
+ ASSERT_EQ(reply.readCharVector(&maybe_chars), OK);
+ ASSERT_EQ(maybe_chars, nullopt);
+
+ int32_t end;
+ ASSERT_EQ(reply.readInt32(&end), NOT_ENOUGH_DATA);
+}
+
+TEST_F(SerializationTest, SerializeI32) {
+ android::Parcel data;
+ data.writeInterfaceToken(String16("read_parcel_test"));
+
+ vector<int32_t> i32s(begin(TESTDATA_I32), end(TESTDATA_I32));
+ data.writeInt32(0);
+ data.writeInt32(1);
+ data.writeInt32(numeric_limits<int32_t>::max());
+ data.writeInt32Vector(i32s);
+ data.writeInt32Vector(nullopt);
+
+ android::Parcel reply;
+ ASSERT_EQ(m_server->transact(TEST_I32, data, &reply), OK);
+
+ vector<int32_t> read_i32s;
+ optional<vector<int32_t>> maybe_i32s;
+ ASSERT_EQ(reply.readInt32(), 0);
+ ASSERT_EQ(reply.readInt32(), 1);
+ ASSERT_EQ(reply.readInt32(), numeric_limits<int32_t>::max());
+ ASSERT_EQ(reply.readInt32Vector(&read_i32s), OK);
+ ASSERT_EQ(read_i32s, i32s);
+ ASSERT_EQ(reply.readInt32Vector(&maybe_i32s), OK);
+ ASSERT_EQ(maybe_i32s, nullopt);
+
+ int32_t end;
+ ASSERT_EQ(reply.readInt32(&end), NOT_ENOUGH_DATA);
+}
+
+TEST_F(SerializationTest, SerializeI64) {
+ android::Parcel data;
+ data.writeInterfaceToken(String16("read_parcel_test"));
+
+ vector<int64_t> i64s(begin(TESTDATA_I64), end(TESTDATA_I64));
+ data.writeInt64(0);
+ data.writeInt64(1);
+ data.writeInt64(numeric_limits<int64_t>::max());
+ data.writeInt64Vector(i64s);
+ data.writeInt64Vector(nullopt);
+
+ android::Parcel reply;
+ ASSERT_EQ(m_server->transact(TEST_I64, data, &reply), OK);
+
+ vector<int64_t> read_i64s;
+ optional<vector<int64_t>> maybe_i64s;
+ ASSERT_EQ(reply.readInt64(), 0);
+ ASSERT_EQ(reply.readInt64(), 1);
+ ASSERT_EQ(reply.readInt64(), numeric_limits<int64_t>::max());
+ ASSERT_EQ(reply.readInt64Vector(&read_i64s), OK);
+ ASSERT_EQ(read_i64s, i64s);
+ ASSERT_EQ(reply.readInt64Vector(&maybe_i64s), OK);
+ ASSERT_EQ(maybe_i64s, nullopt);
+
+ int32_t end;
+ ASSERT_EQ(reply.readInt32(&end), NOT_ENOUGH_DATA);
+}
+
+TEST_F(SerializationTest, SerializeU64) {
+ android::Parcel data;
+ data.writeInterfaceToken(String16("read_parcel_test"));
+
+ vector<uint64_t> u64s(begin(TESTDATA_U64), end(TESTDATA_U64));
+ data.writeUint64(0);
+ data.writeUint64(1);
+ data.writeUint64(numeric_limits<uint64_t>::max());
+ data.writeUint64Vector(u64s);
+ data.writeUint64Vector(nullopt);
+
+ android::Parcel reply;
+ ASSERT_EQ(m_server->transact(TEST_U64, data, &reply), OK);
+
+ vector<uint64_t> read_u64s;
+ optional<vector<uint64_t>> maybe_u64s;
+ ASSERT_EQ(reply.readUint64(), 0);
+ ASSERT_EQ(reply.readUint64(), 1);
+ ASSERT_EQ(reply.readUint64(), numeric_limits<uint64_t>::max());
+ ASSERT_EQ(reply.readUint64Vector(&read_u64s), OK);
+ ASSERT_EQ(read_u64s, u64s);
+ ASSERT_EQ(reply.readUint64Vector(&maybe_u64s), OK);
+ ASSERT_EQ(maybe_u64s, nullopt);
+
+ int32_t end;
+ ASSERT_EQ(reply.readInt32(&end), NOT_ENOUGH_DATA);
+}
+
+TEST_F(SerializationTest, SerializeF32) {
+ android::Parcel data;
+ data.writeInterfaceToken(String16("read_parcel_test"));
+
+ vector<float> floats(begin(TESTDATA_FLOAT), end(TESTDATA_FLOAT));
+ data.writeFloat(0);
+ data.writeFloatVector(floats);
+ data.writeFloatVector(nullopt);
+
+ android::Parcel reply;
+ ASSERT_EQ(m_server->transact(TEST_F32, data, &reply), OK);
+
+ vector<float> read_floats;
+ optional<vector<float>> maybe_floats;
+ ASSERT_EQ(reply.readFloat(), 0);
+ ASSERT_EQ(reply.readFloatVector(&read_floats), OK);
+ ASSERT_TRUE(isnan(read_floats[0]));
+ ASSERT_EQ(read_floats[1], floats[1]);
+ ASSERT_EQ(read_floats[2], floats[2]);
+ ASSERT_EQ(read_floats[3], floats[3]);
+ ASSERT_EQ(reply.readFloatVector(&maybe_floats), OK);
+ ASSERT_EQ(maybe_floats, nullopt);
+
+ int32_t end;
+ ASSERT_EQ(reply.readInt32(&end), NOT_ENOUGH_DATA);
+}
+
+TEST_F(SerializationTest, SerializeF64) {
+ android::Parcel data;
+ data.writeInterfaceToken(String16("read_parcel_test"));
+
+ vector<double> doubles(begin(TESTDATA_DOUBLE), end(TESTDATA_DOUBLE));
+ data.writeDouble(0);
+ data.writeDoubleVector(doubles);
+ data.writeDoubleVector(nullopt);
+
+ android::Parcel reply;
+ ASSERT_EQ(m_server->transact(TEST_F64, data, &reply), OK);
+
+ vector<double> read_doubles;
+ optional<vector<double>> maybe_doubles;
+ ASSERT_EQ(reply.readDouble(), 0);
+ ASSERT_EQ(reply.readDoubleVector(&read_doubles), OK);
+ ASSERT_TRUE(isnan(read_doubles[0]));
+ ASSERT_EQ(read_doubles[1], doubles[1]);
+ ASSERT_EQ(read_doubles[2], doubles[2]);
+ ASSERT_EQ(read_doubles[3], doubles[3]);
+ ASSERT_EQ(reply.readDoubleVector(&maybe_doubles), OK);
+ ASSERT_EQ(maybe_doubles, nullopt);
+
+ int32_t end;
+ ASSERT_EQ(reply.readInt32(&end), NOT_ENOUGH_DATA);
+}
+
+TEST_F(SerializationTest, SerializeString) {
+ android::Parcel data;
+ data.writeInterfaceToken(String16("read_parcel_test"));
+
+ vector<optional<String16>> strings;
+ for (auto I = begin(TESTDATA_STRS), E = end(TESTDATA_STRS); I != E; ++I) {
+ if (*I == nullptr) {
+ strings.push_back(optional<String16>());
+ } else {
+ strings.emplace_back(*I);
+ }
+ }
+ data.writeUtf8AsUtf16(string("testing"));
+ data.writeString16(nullopt);
+ data.writeString16Vector(strings);
+ data.writeString16Vector(nullopt);
+
+ android::Parcel reply;
+ ASSERT_EQ(m_server->transact(TEST_STRING, data, &reply), OK);
+
+ optional<String16> maybe_string;
+ optional<vector<optional<String16>>> read_strings;
+ ASSERT_EQ(reply.readString16(), String16("testing"));
+ ASSERT_EQ(reply.readString16(&maybe_string), OK);
+ ASSERT_EQ(maybe_string, nullopt);
+ ASSERT_EQ(reply.readString16Vector(&read_strings), OK);
+ ASSERT_EQ(read_strings, strings);
+ ASSERT_EQ(reply.readString16Vector(&read_strings), OK);
+ ASSERT_EQ(read_strings, nullopt);
+
+ int32_t end;
+ ASSERT_EQ(reply.readInt32(&end), NOT_ENOUGH_DATA);
+}
+
+TEST_F(SerializationTest, SerializeFileDescriptor) {
+ unique_fd out_file, in_file;
+ ASSERT_TRUE(base::Pipe(&out_file, &in_file));
+
+ vector<ParcelFileDescriptor> file_descriptors;
+ file_descriptors.push_back(ParcelFileDescriptor(std::move(out_file)));
+ file_descriptors.push_back(ParcelFileDescriptor(std::move(in_file)));
+
+ android::Parcel data;
+ data.writeInterfaceToken(String16("read_parcel_test"));
+
+ data.writeParcelable(file_descriptors[0]);
+ data.writeParcelable(file_descriptors[1]);
+ data.writeParcelableVector(file_descriptors);
+
+ android::Parcel reply;
+ ASSERT_EQ(m_server->transact(TEST_FILE_DESCRIPTOR, data, &reply), OK);
+
+ ParcelFileDescriptor returned_fd1, returned_fd2;
+ vector<ParcelFileDescriptor> returned_file_descriptors;
+ ASSERT_EQ(reply.readParcelable(&returned_fd1), OK);
+ ASSERT_EQ(reply.readParcelable(&returned_fd2), OK);
+ ASSERT_EQ(reply.readParcelableVector(&returned_file_descriptors), OK);
+
+ int32_t end;
+ ASSERT_EQ(reply.readInt32(&end), NOT_ENOUGH_DATA);
+
+ base::WriteStringToFd("Testing", returned_fd2.get());
+ base::WriteStringToFd("File", returned_file_descriptors[1].get());
+ base::WriteStringToFd("Descriptors", file_descriptors[1].get());
+
+ string expected = "TestingFileDescriptors";
+ vector<char> buf(expected.length());
+ base::ReadFully(file_descriptors[0].release(), buf.data(), buf.size());
+ ASSERT_EQ(expected, string(buf.data()));
+}
+
+TEST_F(SerializationTest, SerializeIBinder) {
+ android::Parcel data;
+ data.writeInterfaceToken(String16("read_parcel_test"));
+
+ data.writeStrongBinder(m_server);
+ data.writeStrongBinder(nullptr);
+ data.writeStrongBinderVector({m_server, nullptr});
+ data.writeStrongBinderVector(nullopt);
+
+ android::Parcel reply;
+ ASSERT_EQ(m_server->transact(TEST_IBINDER, data, &reply), OK);
+
+ optional<vector<sp<IBinder>>> binders;
+ ASSERT_TRUE(reply.readStrongBinder());
+ ASSERT_FALSE(reply.readStrongBinder());
+ ASSERT_EQ(reply.readStrongBinderVector(&binders), OK);
+ ASSERT_EQ(binders->size(), 2);
+ ASSERT_TRUE((*binders)[0]);
+ ASSERT_FALSE((*binders)[1]);
+ ASSERT_EQ(reply.readStrongBinderVector(&binders), OK);
+ ASSERT_FALSE(binders);
+
+ int32_t end;
+ ASSERT_EQ(reply.readInt32(&end), NOT_ENOUGH_DATA);
+}
+
+TEST_F(SerializationTest, SerializeStatus) {
+ android::Parcel data;
+ data.writeInterfaceToken(String16("read_parcel_test"));
+
+ binder::Status::ok().writeToParcel(&data);
+ binder::Status::fromExceptionCode(binder::Status::EX_NULL_POINTER, "a status message")
+ .writeToParcel(&data);
+ binder::Status::fromServiceSpecificError(42, "a service-specific error").writeToParcel(&data);
+
+ android::Parcel reply;
+ ASSERT_EQ(m_server->transact(TEST_STATUS, data, &reply), OK);
+
+ binder::Status status;
+
+ ASSERT_EQ(status.readFromParcel(reply), OK);
+ ASSERT_TRUE(status.isOk());
+
+ ASSERT_EQ(status.readFromParcel(reply), OK);
+ ASSERT_EQ(status.exceptionCode(), binder::Status::EX_NULL_POINTER);
+ ASSERT_EQ(status.exceptionMessage(), "a status message");
+
+ ASSERT_EQ(status.readFromParcel(reply), OK);
+ ASSERT_EQ(status.serviceSpecificErrorCode(), 42);
+ ASSERT_EQ(status.exceptionMessage(), "a service-specific error");
+
+ int32_t end;
+ ASSERT_EQ(reply.readInt32(&end), NOT_ENOUGH_DATA);
+}
+
+// Test that failures from Rust properly propagate to C++
+TEST_F(SerializationTest, SerializeRustFail) {
+ android::Parcel data;
+ data.writeInterfaceToken(String16("read_parcel_test"));
+ ASSERT_EQ(m_server->transact(TEST_FAIL, data, nullptr), FAILED_TRANSACTION);
+}
+
+int main(int argc, char **argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ gEnvironment = AddGlobalTestEnvironment(new SerializationEnvironment());
+ ProcessState::self()->startThreadPool();
+ return RUN_ALL_TESTS();
+}
diff --git a/libs/binder/rust/tests/serialization.hpp b/libs/binder/rust/tests/serialization.hpp
new file mode 100644
index 0000000..0041608
--- /dev/null
+++ b/libs/binder/rust/tests/serialization.hpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 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 <binder/IBinder.h>
+
+using namespace android;
+
+enum Transaction {
+ TEST_BOOL = IBinder::FIRST_CALL_TRANSACTION,
+ TEST_BYTE,
+ TEST_U16,
+ TEST_I32,
+ TEST_I64,
+ TEST_U64,
+ TEST_F32,
+ TEST_F64,
+ TEST_STRING,
+ TEST_FILE_DESCRIPTOR,
+ TEST_IBINDER,
+ TEST_STATUS,
+ TEST_FAIL,
+};
+
+extern const int8_t TESTDATA_I8[4];
+extern const uint8_t TESTDATA_U8[4];
+extern const char16_t TESTDATA_CHARS[4];
+extern const int32_t TESTDATA_I32[4];
+extern const int64_t TESTDATA_I64[4];
+extern const uint64_t TESTDATA_U64[4];
+extern const float TESTDATA_FLOAT[4];
+extern const double TESTDATA_DOUBLE[4];
+extern const bool TESTDATA_BOOL[4];
+extern const char* const TESTDATA_STRS[4];
diff --git a/libs/binder/rust/tests/serialization.rs b/libs/binder/rust/tests/serialization.rs
new file mode 100644
index 0000000..2ae13f4
--- /dev/null
+++ b/libs/binder/rust/tests/serialization.rs
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+//! Included as a module in the binder crate internal tests for internal API
+//! access.
+
+use binder::declare_binder_interface;
+use binder::{
+ Binder, ExceptionCode, Interface, Parcel, Result, SpIBinder, Status,
+ StatusCode, TransactionCode,
+};
+use binder::parcel::ParcelFileDescriptor;
+
+use std::ffi::{c_void, CStr, CString};
+use std::panic::{self, AssertUnwindSafe};
+use std::sync::Once;
+
+#[allow(
+ non_camel_case_types,
+ non_snake_case,
+ non_upper_case_globals,
+ unused,
+ improper_ctypes,
+ missing_docs,
+ clippy::all
+)]
+mod bindings {
+ include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
+}
+
+static SERVICE_ONCE: Once = Once::new();
+static mut SERVICE: Option<SpIBinder> = None;
+
+/// Start binder service and return a raw AIBinder pointer to it.
+///
+/// Safe to call multiple times, only creates the service once.
+#[no_mangle]
+pub extern "C" fn rust_service() -> *mut c_void {
+ unsafe {
+ SERVICE_ONCE.call_once(|| {
+ SERVICE = Some(BnReadParcelTest::new_binder(()).as_binder());
+ });
+ SERVICE.as_ref().unwrap().as_raw().cast()
+ }
+}
+
+/// Empty interface just to use the declare_binder_interface macro
+pub trait ReadParcelTest: Interface {}
+
+declare_binder_interface! {
+ ReadParcelTest["read_parcel_test"] {
+ native: BnReadParcelTest(on_transact),
+ proxy: BpReadParcelTest,
+ }
+}
+
+impl ReadParcelTest for Binder<BnReadParcelTest> {}
+
+impl ReadParcelTest for BpReadParcelTest {}
+
+impl ReadParcelTest for () {}
+
+fn on_transact(
+ _service: &dyn ReadParcelTest,
+ code: TransactionCode,
+ parcel: &Parcel,
+ reply: &mut Parcel,
+) -> Result<()> {
+ panic::catch_unwind(AssertUnwindSafe(|| transact_inner(code, parcel, reply))).unwrap_or_else(
+ |e| {
+ eprintln!("Failure in Rust: {:?}", e.downcast_ref::<String>());
+ Err(StatusCode::FAILED_TRANSACTION)
+ },
+ )
+}
+
+#[allow(clippy::float_cmp)]
+fn transact_inner(code: TransactionCode, parcel: &Parcel, reply: &mut Parcel) -> Result<()> {
+ match code {
+ bindings::Transaction_TEST_BOOL => {
+ assert_eq!(parcel.read::<bool>()?, true);
+ assert_eq!(parcel.read::<bool>()?, false);
+ assert_eq!(parcel.read::<Vec<bool>>()?, unsafe {
+ bindings::TESTDATA_BOOL
+ });
+ assert_eq!(parcel.read::<Option<Vec<bool>>>()?, None);
+
+ reply.write(&true)?;
+ reply.write(&false)?;
+ reply.write(&unsafe { bindings::TESTDATA_BOOL }[..])?;
+ reply.write(&(None as Option<Vec<bool>>))?;
+ }
+ bindings::Transaction_TEST_BYTE => {
+ assert_eq!(parcel.read::<i8>()?, 0);
+ assert_eq!(parcel.read::<i8>()?, 1);
+ assert_eq!(parcel.read::<i8>()?, i8::max_value());
+ assert_eq!(parcel.read::<Vec<i8>>()?, unsafe { bindings::TESTDATA_I8 });
+ assert_eq!(parcel.read::<Vec<u8>>()?, unsafe { bindings::TESTDATA_U8 });
+ assert_eq!(parcel.read::<Option<Vec<i8>>>()?, None);
+
+ reply.write(&0i8)?;
+ reply.write(&1i8)?;
+ reply.write(&i8::max_value())?;
+ reply.write(&unsafe { bindings::TESTDATA_I8 }[..])?;
+ reply.write(&unsafe { bindings::TESTDATA_U8 }[..])?;
+ reply.write(&(None as Option<Vec<i8>>))?;
+ }
+ bindings::Transaction_TEST_U16 => {
+ assert_eq!(parcel.read::<u16>()?, 0);
+ assert_eq!(parcel.read::<u16>()?, 1);
+ assert_eq!(parcel.read::<u16>()?, u16::max_value());
+ assert_eq!(parcel.read::<Vec<u16>>()?, unsafe {
+ bindings::TESTDATA_CHARS
+ });
+ assert_eq!(parcel.read::<Option<Vec<u16>>>()?, None);
+
+ reply.write(&0u16)?;
+ reply.write(&1u16)?;
+ reply.write(&u16::max_value())?;
+ reply.write(&unsafe { bindings::TESTDATA_CHARS }[..])?;
+ reply.write(&(None as Option<Vec<u16>>))?;
+ }
+ bindings::Transaction_TEST_I32 => {
+ assert_eq!(parcel.read::<i32>()?, 0);
+ assert_eq!(parcel.read::<i32>()?, 1);
+ assert_eq!(parcel.read::<i32>()?, i32::max_value());
+ assert_eq!(parcel.read::<Vec<i32>>()?, unsafe {
+ bindings::TESTDATA_I32
+ });
+ assert_eq!(parcel.read::<Option<Vec<i32>>>()?, None);
+
+ reply.write(&0i32)?;
+ reply.write(&1i32)?;
+ reply.write(&i32::max_value())?;
+ reply.write(&unsafe { bindings::TESTDATA_I32 }[..])?;
+ reply.write(&(None as Option<Vec<i32>>))?;
+ }
+ bindings::Transaction_TEST_I64 => {
+ assert_eq!(parcel.read::<i64>()?, 0);
+ assert_eq!(parcel.read::<i64>()?, 1);
+ assert_eq!(parcel.read::<i64>()?, i64::max_value());
+ assert_eq!(parcel.read::<Vec<i64>>()?, unsafe {
+ bindings::TESTDATA_I64
+ });
+ assert_eq!(parcel.read::<Option<Vec<i64>>>()?, None);
+
+ reply.write(&0i64)?;
+ reply.write(&1i64)?;
+ reply.write(&i64::max_value())?;
+ reply.write(&unsafe { bindings::TESTDATA_I64 }[..])?;
+ reply.write(&(None as Option<Vec<i64>>))?;
+ }
+ bindings::Transaction_TEST_U64 => {
+ assert_eq!(parcel.read::<u64>()?, 0);
+ assert_eq!(parcel.read::<u64>()?, 1);
+ assert_eq!(parcel.read::<u64>()?, u64::max_value());
+ assert_eq!(parcel.read::<Vec<u64>>()?, unsafe {
+ bindings::TESTDATA_U64
+ });
+ assert_eq!(parcel.read::<Option<Vec<u64>>>()?, None);
+
+ reply.write(&0u64)?;
+ reply.write(&1u64)?;
+ reply.write(&u64::max_value())?;
+ reply.write(&unsafe { bindings::TESTDATA_U64 }[..])?;
+ reply.write(&(None as Option<Vec<u64>>))?;
+ }
+ bindings::Transaction_TEST_F32 => {
+ assert_eq!(parcel.read::<f32>()?, 0f32);
+ let floats = parcel.read::<Vec<f32>>()?;
+ assert!(floats[0].is_nan());
+ assert_eq!(floats[1..], unsafe { bindings::TESTDATA_FLOAT }[1..]);
+ assert_eq!(parcel.read::<Option<Vec<f32>>>()?, None);
+
+ reply.write(&0f32)?;
+ reply.write(&unsafe { bindings::TESTDATA_FLOAT }[..])?;
+ reply.write(&(None as Option<Vec<f32>>))?;
+ }
+ bindings::Transaction_TEST_F64 => {
+ assert_eq!(parcel.read::<f64>()?, 0f64);
+ let doubles = parcel.read::<Vec<f64>>()?;
+ assert!(doubles[0].is_nan());
+ assert_eq!(doubles[1..], unsafe { bindings::TESTDATA_DOUBLE }[1..]);
+ assert_eq!(parcel.read::<Option<Vec<f64>>>()?, None);
+
+ reply.write(&0f64)?;
+ reply.write(&unsafe { bindings::TESTDATA_DOUBLE }[..])?;
+ reply.write(&(None as Option<Vec<f64>>))?;
+ }
+ bindings::Transaction_TEST_STRING => {
+ let s: Option<String> = parcel.read()?;
+ assert_eq!(s.as_deref(), Some("testing"));
+ let s: Option<String> = parcel.read()?;
+ assert_eq!(s, None);
+ let s: Option<Vec<Option<String>>> = parcel.read()?;
+ for (s, expected) in s
+ .unwrap()
+ .iter()
+ .zip(unsafe { bindings::TESTDATA_STRS }.iter())
+ {
+ let expected = unsafe {
+ expected
+ .as_ref()
+ .and_then(|e| CStr::from_ptr(e).to_str().ok())
+ };
+ assert_eq!(s.as_deref(), expected);
+ }
+ let s: Option<Vec<Option<String>>> = parcel.read()?;
+ assert_eq!(s, None);
+
+ let strings: Vec<Option<String>> = unsafe {
+ bindings::TESTDATA_STRS
+ .iter()
+ .map(|s| {
+ s.as_ref().map(|s| {
+ CStr::from_ptr(s)
+ .to_str()
+ .expect("String was not UTF-8")
+ .to_owned()
+ })
+ })
+ .collect()
+ };
+
+ reply.write("testing")?;
+ reply.write(&(None as Option<String>))?;
+ reply.write(&strings)?;
+ reply.write(&(None as Option<Vec<String>>))?;
+ }
+ bindings::Transaction_TEST_FILE_DESCRIPTOR => {
+ let file1 = parcel.read::<ParcelFileDescriptor>()?;
+ let file2 = parcel.read::<ParcelFileDescriptor>()?;
+ let files = parcel.read::<Vec<Option<ParcelFileDescriptor>>>()?;
+
+ reply.write(&file1)?;
+ reply.write(&file2)?;
+ reply.write(&files)?;
+ }
+ bindings::Transaction_TEST_IBINDER => {
+ assert!(parcel.read::<Option<SpIBinder>>()?.is_some());
+ assert!(parcel.read::<Option<SpIBinder>>()?.is_none());
+ let ibinders = parcel.read::<Option<Vec<Option<SpIBinder>>>>()?.unwrap();
+ assert_eq!(ibinders.len(), 2);
+ assert!(ibinders[0].is_some());
+ assert!(ibinders[1].is_none());
+ assert!(parcel.read::<Option<Vec<Option<SpIBinder>>>>()?.is_none());
+
+ let service = unsafe {
+ SERVICE
+ .as_ref()
+ .expect("Global binder service not initialized")
+ .clone()
+ };
+ reply.write(&service)?;
+ reply.write(&(None as Option<&SpIBinder>))?;
+ reply.write(&[Some(&service), None][..])?;
+ reply.write(&(None as Option<Vec<Option<&SpIBinder>>>))?;
+ }
+ bindings::Transaction_TEST_STATUS => {
+ let status: Status = parcel.read()?;
+ assert!(status.is_ok());
+ let status: Status = parcel.read()?;
+ assert_eq!(status.exception_code(), ExceptionCode::NULL_POINTER);
+ assert_eq!(
+ status.get_description(),
+ "Status(-4, EX_NULL_POINTER): 'a status message'"
+ );
+ let status: Status = parcel.read()?;
+ assert_eq!(status.service_specific_error(), 42);
+ assert_eq!(
+ status.get_description(),
+ "Status(-8, EX_SERVICE_SPECIFIC): '42: a service-specific error'"
+ );
+
+ reply.write(&Status::ok())?;
+ reply.write(&Status::new_exception(
+ ExceptionCode::NULL_POINTER,
+ Some(&CString::new("a status message").unwrap()),
+ ))?;
+ reply.write(&Status::new_service_specific_error(
+ 42,
+ Some(&CString::new("a service-specific error").unwrap()),
+ ))?;
+ }
+ bindings::Transaction_TEST_FAIL => {
+ panic!("Testing expected failure");
+ }
+ _ => return Err(StatusCode::UNKNOWN_TRANSACTION),
+ }
+
+ assert_eq!(parcel.read::<i32>(), Err(StatusCode::NOT_ENOUGH_DATA));
+ Ok(())
+}
diff --git a/libs/binder/tests/binderClearBufTest.cpp b/libs/binder/tests/binderClearBufTest.cpp
index a565e72..2d30c8d 100644
--- a/libs/binder/tests/binderClearBufTest.cpp
+++ b/libs/binder/tests/binderClearBufTest.cpp
@@ -83,7 +83,6 @@
lastReply = reply.data();
lastReplySize = reply.dataSize();
}
- IPCThreadState::self()->flushCommands();
*outBuffer = hexString(lastReply, lastReplySize);
return result;
}
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index d467692..e5b131e 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -25,6 +25,7 @@
"AnrTracker.cpp",
"Connection.cpp",
"Entry.cpp",
+ "FocusResolver.cpp",
"InjectionState.cpp",
"InputDispatcher.cpp",
"InputDispatcherFactory.cpp",
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 6953d04..a19b04f 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -102,7 +102,7 @@
// Focus notifications always go to apps, so set the flag POLICY_FLAG_PASS_TO_USER for all entries
FocusEntry::FocusEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool hasFocus,
- std::string_view reason)
+ const std::string& reason)
: EventEntry(id, Type::FOCUS, eventTime, POLICY_FLAG_PASS_TO_USER),
connectionToken(connectionToken),
hasFocus(hasFocus),
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 26b641d..499f42e 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -93,10 +93,10 @@
struct FocusEntry : EventEntry {
sp<IBinder> connectionToken;
bool hasFocus;
- std::string_view reason;
+ std::string reason;
FocusEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool hasFocus,
- std::string_view reason);
+ const std::string& reason);
std::string getDescription() const override;
virtual ~FocusEntry();
diff --git a/services/inputflinger/dispatcher/FocusResolver.cpp b/services/inputflinger/dispatcher/FocusResolver.cpp
new file mode 100644
index 0000000..ee6b38b
--- /dev/null
+++ b/services/inputflinger/dispatcher/FocusResolver.cpp
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "FocusResolver"
+#define ATRACE_TAG ATRACE_TAG_INPUT
+
+#define INDENT " "
+#define INDENT2 " "
+
+// Log debug messages about input focus tracking.
+static constexpr bool DEBUG_FOCUS = false;
+
+#include <inttypes.h>
+
+#include <android-base/stringprintf.h>
+#include <binder/Binder.h>
+#include <input/InputWindow.h>
+#include <input/NamedEnum.h>
+#include <log/log.h>
+
+#include "FocusResolver.h"
+
+namespace android::inputdispatcher {
+
+sp<IBinder> FocusResolver::getFocusedWindowToken(int32_t displayId) const {
+ auto it = mFocusedWindowTokenByDisplay.find(displayId);
+ return it != mFocusedWindowTokenByDisplay.end() ? it->second.second : nullptr;
+}
+
+std::optional<FocusRequest> FocusResolver::getPendingRequest(int32_t displayId) {
+ auto it = mPendingFocusRequests.find(displayId);
+ return it != mPendingFocusRequests.end() ? std::make_optional<>(it->second) : std::nullopt;
+}
+
+std::optional<FocusResolver::FocusChanges> FocusResolver::setInputWindows(
+ int32_t displayId, const std::vector<sp<InputWindowHandle>>& windows) {
+ // If the current focused window becomes unfocusable, remove focus.
+ sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
+ if (currentFocus) {
+ FocusResult result = isTokenFocusable(currentFocus, windows);
+ if (result != FocusResult::OK) {
+ return updateFocusedWindow(displayId, NamedEnum::string(result), nullptr);
+ }
+ }
+
+ // Check if any pending focus requests can be resolved.
+ std::optional<FocusRequest> pendingRequest = getPendingRequest(displayId);
+ if (!pendingRequest) {
+ return std::nullopt;
+ }
+
+ sp<IBinder> requestedFocus = pendingRequest->token;
+ std::string windowName = pendingRequest->windowName;
+ if (currentFocus == requestedFocus) {
+ ALOGD_IF(DEBUG_FOCUS,
+ "setFocusedWindow %s on display %" PRId32 " ignored, reason: already focused",
+ windowName.c_str(), displayId);
+ mPendingFocusRequests.erase(displayId);
+ return std::nullopt;
+ }
+
+ FocusResult result = isTokenFocusable(requestedFocus, windows);
+ // If the window from the pending request is now visible, provide it focus.
+ if (result == FocusResult::OK) {
+ mPendingFocusRequests.erase(displayId);
+ return updateFocusedWindow(displayId, "Window became visible", requestedFocus, windowName);
+ }
+
+ if (result != FocusResult::NOT_VISIBLE) {
+ // Drop the request if we are unable to change the focus for a reason other than visibility.
+ ALOGW("Focus request %s on display %" PRId32 " ignored, reason:%s", windowName.c_str(),
+ displayId, NamedEnum::string(result).c_str());
+ mPendingFocusRequests.erase(displayId);
+ }
+ return std::nullopt;
+}
+
+std::optional<FocusResolver::FocusChanges> FocusResolver::setFocusedWindow(
+ const FocusRequest& request, const std::vector<sp<InputWindowHandle>>& windows) {
+ const int32_t displayId = request.displayId;
+ const sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
+ if (request.focusedToken && currentFocus != request.focusedToken) {
+ ALOGW("setFocusedWindow %s on display %" PRId32
+ " ignored, reason: focusedToken %s is not focused",
+ request.windowName.c_str(), displayId, request.focusedWindowName.c_str());
+ return std::nullopt;
+ }
+
+ std::optional<FocusRequest> pendingRequest = getPendingRequest(displayId);
+ if (pendingRequest) {
+ ALOGW("Pending focus request %s on display %" PRId32
+ " ignored, reason:replaced by new request",
+ pendingRequest->windowName.c_str(), displayId);
+
+ // clear any pending focus requests
+ mPendingFocusRequests.erase(displayId);
+ }
+
+ if (currentFocus == request.token) {
+ ALOGD_IF(DEBUG_FOCUS,
+ "setFocusedWindow %s on display %" PRId32 " ignored, reason:already focused",
+ request.windowName.c_str(), displayId);
+ return std::nullopt;
+ }
+
+ FocusResult result = isTokenFocusable(request.token, windows);
+ if (result == FocusResult::OK) {
+ std::string reason =
+ (request.focusedToken) ? "setFocusedWindow with focus check" : "setFocusedWindow";
+ return updateFocusedWindow(displayId, reason, request.token, request.windowName);
+ }
+
+ if (result == FocusResult::NOT_VISIBLE) {
+ // The requested window is not currently visible. Wait for the window to become visible
+ // and then provide it focus. This is to handle situations where a user action triggers
+ // a new window to appear. We want to be able to queue any key events after the user
+ // action and deliver it to the newly focused window. In order for this to happen, we
+ // take focus from the currently focused window so key events can be queued.
+ ALOGD_IF(DEBUG_FOCUS,
+ "setFocusedWindow %s on display %" PRId32
+ " pending, reason: window is not visible",
+ request.windowName.c_str(), displayId);
+ mPendingFocusRequests[displayId] = request;
+ return updateFocusedWindow(displayId, "Waiting for window to be visible", nullptr);
+ } else {
+ ALOGW("setFocusedWindow %s on display %" PRId32 " ignored, reason:%s",
+ request.windowName.c_str(), displayId, NamedEnum::string(result).c_str());
+ }
+
+ return std::nullopt;
+}
+
+FocusResolver::FocusResult FocusResolver::isTokenFocusable(
+ const sp<IBinder>& token, const std::vector<sp<InputWindowHandle>>& windows) {
+ bool allWindowsAreFocusable = true;
+ bool visibleWindowFound = false;
+ bool windowFound = false;
+ for (const sp<InputWindowHandle>& window : windows) {
+ if (window->getToken() != token) {
+ continue;
+ }
+ windowFound = true;
+ if (window->getInfo()->visible) {
+ // Check if at least a single window is visible.
+ visibleWindowFound = true;
+ }
+ if (!window->getInfo()->focusable) {
+ // Check if all windows with the window token are focusable.
+ allWindowsAreFocusable = false;
+ break;
+ }
+ }
+
+ if (!windowFound) {
+ return FocusResult::NO_WINDOW;
+ }
+ if (!allWindowsAreFocusable) {
+ return FocusResult::NOT_FOCUSABLE;
+ }
+ if (!visibleWindowFound) {
+ return FocusResult::NOT_VISIBLE;
+ }
+
+ return FocusResult::OK;
+}
+
+std::optional<FocusResolver::FocusChanges> FocusResolver::updateFocusedWindow(
+ int32_t displayId, const std::string& reason, const sp<IBinder>& newFocus,
+ const std::string& tokenName) {
+ sp<IBinder> oldFocus = getFocusedWindowToken(displayId);
+ if (newFocus == oldFocus) {
+ return std::nullopt;
+ }
+ if (newFocus) {
+ mFocusedWindowTokenByDisplay[displayId] = {tokenName, newFocus};
+ } else {
+ mFocusedWindowTokenByDisplay.erase(displayId);
+ }
+
+ return {{oldFocus, newFocus, displayId, reason}};
+}
+
+std::string FocusResolver::dumpFocusedWindows() const {
+ if (mFocusedWindowTokenByDisplay.empty()) {
+ return INDENT "FocusedWindows: <none>\n";
+ }
+
+ std::string dump;
+ dump += INDENT "FocusedWindows:\n";
+ for (const auto& [displayId, namedToken] : mFocusedWindowTokenByDisplay) {
+ dump += base::StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", displayId,
+ namedToken.first.c_str());
+ }
+ return dump;
+}
+
+std::string FocusResolver::dump() const {
+ std::string dump = dumpFocusedWindows();
+
+ if (mPendingFocusRequests.empty()) {
+ return dump + INDENT "PendingFocusRequests: <none>\n";
+ }
+
+ dump += INDENT "PendingFocusRequests:\n";
+ for (const auto& [displayId, request] : mPendingFocusRequests) {
+ dump += base::StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", displayId,
+ request.windowName.c_str());
+ }
+ return dump;
+}
+
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/FocusResolver.h b/services/inputflinger/dispatcher/FocusResolver.h
new file mode 100644
index 0000000..e067ad9
--- /dev/null
+++ b/services/inputflinger/dispatcher/FocusResolver.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <optional>
+#include <unordered_map>
+
+#include <android/FocusRequest.h>
+#include <binder/Binder.h>
+#include <input/InputWindow.h>
+
+namespace android::inputdispatcher {
+
+// Keeps track of the focused window per display. The class listens to updates from input dispatcher
+// and provides focus changes.
+//
+// Focus Policy
+// Window focusabilty - A window token can be focused if there is at least one window handle that
+// is visible with the same token and all window handles with the same token are focusable.
+// See FocusResolver::isTokenFocusable
+//
+// Focus request - Request will be granted if the window is focusable. If the window is not
+// visible, then the request is kept in a pending state and granted when it becomes visible.
+// If window becomes not focusable, or another request comes in, the pending request is dropped.
+//
+// Window handle updates - Focus is lost when the currently focused window becomes not focusable.
+class FocusResolver {
+public:
+ // Returns the focused window token on the specified display.
+ sp<IBinder> getFocusedWindowToken(int32_t displayId) const;
+
+ struct FocusChanges {
+ sp<IBinder> oldFocus;
+ sp<IBinder> newFocus;
+ int32_t displayId;
+ std::string reason;
+ };
+ std::optional<FocusResolver::FocusChanges> setInputWindows(
+ int32_t displayId, const std::vector<sp<InputWindowHandle>>& windows);
+ std::optional<FocusResolver::FocusChanges> setFocusedWindow(
+ const FocusRequest& request, const std::vector<sp<InputWindowHandle>>& windows);
+
+ // exposed for debugging
+ bool hasFocusedWindowTokens() const { return !mFocusedWindowTokenByDisplay.empty(); }
+ std::string dumpFocusedWindows() const;
+ std::string dump() const;
+
+private:
+ enum class FocusResult {
+ OK,
+ NO_WINDOW,
+ NOT_FOCUSABLE,
+ NOT_VISIBLE,
+ };
+
+ // Checks if the window token can be focused on a display. The token can be focused if there is
+ // at least one window handle that is visible with the same token and all window handles with
+ // the same token are focusable.
+ //
+ // In the case of mirroring, two windows may share the same window token and their visibility
+ // might be different. Example, the mirrored window can cover the window its mirroring. However,
+ // we expect the focusability of the windows to match since its hard to reason why one window
+ // can receive focus events and the other cannot when both are backed by the same input channel.
+ //
+ static FocusResult isTokenFocusable(const sp<IBinder>& token,
+ const std::vector<sp<InputWindowHandle>>& windows);
+
+ // Focus tracking for keys, trackball, etc. A window token can be associated with one or
+ // more InputWindowHandles. If a window is mirrored, the window and its mirror will share
+ // the same token. Focus is tracked by the token per display and the events are dispatched
+ // to the channel associated by this token.
+ typedef std::pair<std::string /* name */, sp<IBinder>> NamedToken;
+ std::unordered_map<int32_t /* displayId */, NamedToken> mFocusedWindowTokenByDisplay;
+
+ // This map will store a single pending focus request per display that cannot be currently
+ // processed. This can happen if the window requested to be focused is not currently visible.
+ // Such a window might become visible later, and these requests would be processed at that time.
+ std::unordered_map<int32_t /* displayId */, FocusRequest> mPendingFocusRequests;
+
+ std::optional<FocusResolver::FocusChanges> updateFocusedWindow(
+ int32_t displayId, const std::string& reason, const sp<IBinder>& token,
+ const std::string& tokenName = "");
+ std::optional<FocusRequest> getPendingRequest(int32_t displayId);
+};
+
+} // namespace android::inputdispatcher
\ No newline at end of file
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index b3558c6..0495d68 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -127,6 +127,13 @@
return value ? "true" : "false";
}
+static inline const std::string toString(sp<IBinder> binder) {
+ if (binder == nullptr) {
+ return "<null>";
+ }
+ return StringPrintf("%p", binder.get());
+}
+
static inline int32_t getMotionEventActionPointerIndex(int32_t action) {
return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >>
AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
@@ -293,15 +300,6 @@
return removed;
}
-/**
- * Find the entry in std::unordered_map by key and return the value as an optional.
- */
-template <typename K, typename V>
-static std::optional<V> getOptionalValueByKey(const std::unordered_map<K, V>& map, K key) {
- auto it = map.find(key);
- return it != map.end() ? std::optional<V>{it->second} : std::nullopt;
-}
-
static bool haveSameToken(const sp<InputWindowHandle>& first, const sp<InputWindowHandle>& second) {
if (first == second) {
return true;
@@ -420,19 +418,6 @@
return result;
}
-const char* InputDispatcher::typeToString(InputDispatcher::FocusResult result) {
- switch (result) {
- case InputDispatcher::FocusResult::OK:
- return "Ok";
- case InputDispatcher::FocusResult::NO_WINDOW:
- return "Window not found";
- case InputDispatcher::FocusResult::NOT_FOCUSABLE:
- return "Window not focusable";
- case InputDispatcher::FocusResult::NOT_VISIBLE:
- return "Window not visible";
- }
-}
-
template <typename T>
static bool sharedPointersEqual(const std::shared_ptr<T>& lhs, const std::shared_ptr<T>& rhs) {
if (lhs == nullptr && rhs == nullptr) {
@@ -1218,7 +1203,7 @@
}
void InputDispatcher::enqueueFocusEventLocked(const sp<IBinder>& windowToken, bool hasFocus,
- std::string_view reason) {
+ const std::string& reason) {
if (mPendingEvent != nullptr) {
// Move the pending event to the front of the queue. This will give the chance
// for the pending event to get dispatched to the newly focused window
@@ -1281,7 +1266,7 @@
ALOGW("No window requested Pointer Capture.");
return;
}
- token = getValueByKey(mFocusedWindowTokenByDisplay, mFocusedDisplayId);
+ token = mFocusResolver.getFocusedWindowToken(mFocusedDisplayId);
LOG_ALWAYS_FATAL_IF(!token, "Cannot find focused window for Pointer Capture.");
mWindowTokenWithPointerCapture = token;
} else {
@@ -1380,7 +1365,7 @@
std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
&InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
sp<IBinder> focusedWindowToken =
- getValueByKey(mFocusedWindowTokenByDisplay, getTargetDisplayId(*entry));
+ mFocusResolver.getFocusedWindowToken(getTargetDisplayId(*entry));
commandEntry->connectionToken = focusedWindowToken;
commandEntry->keyEntry = entry;
postCommandLocked(std::move(commandEntry));
@@ -2470,19 +2455,20 @@
std::string InputDispatcher::dumpWindowForTouchOcclusion(const InputWindowInfo* info,
bool isTouchedWindow) const {
- return StringPrintf(INDENT2 "* %stype=%s, package=%s/%" PRId32 ", id=%" PRId32
- ", mode=%s, alpha=%.2f, "
- "frame=[%" PRId32 ",%" PRId32 "][%" PRId32 ",%" PRId32
- "], touchableRegion=%s, window={%s}, applicationInfo=%s, "
- "flags={%s}, inputFeatures={%s}, hasToken=%s\n",
+ return StringPrintf(INDENT2
+ "* %stype=%s, package=%s/%" PRId32 ", id=%" PRId32 ", mode=%s, alpha=%.2f, "
+ "frame=[%" PRId32 ",%" PRId32 "][%" PRId32 ",%" PRId32
+ "], touchableRegion=%s, window={%s}, flags={%s}, inputFeatures={%s}, "
+ "hasToken=%s, applicationInfo.name=%s, applicationInfo.token=%s\n",
(isTouchedWindow) ? "[TOUCHED] " : "",
NamedEnum::string(info->type, "%" PRId32).c_str(),
info->packageName.c_str(), info->ownerUid, info->id,
toString(info->touchOcclusionMode).c_str(), info->alpha, info->frameLeft,
info->frameTop, info->frameRight, info->frameBottom,
dumpRegion(info->touchableRegion).c_str(), info->name.c_str(),
- info->applicationInfo.name.c_str(), info->flags.string().c_str(),
- info->inputFeatures.string().c_str(), toString(info->token != nullptr));
+ info->flags.string().c_str(), info->inputFeatures.string().c_str(),
+ toString(info->token != nullptr), info->applicationInfo.name.c_str(),
+ toString(info->applicationInfo.token).c_str());
}
bool InputDispatcher::isTouchTrustedLocked(const TouchOcclusionInfo& occlusionInfo) const {
@@ -2910,7 +2896,7 @@
return;
}
- sp<IBinder> focusedToken = getValueByKey(mFocusedWindowTokenByDisplay, mFocusedDisplayId);
+ sp<IBinder> focusedToken = mFocusResolver.getFocusedWindowToken(mFocusedDisplayId);
if (focusedToken == token) {
// ignore since token is focused
return;
@@ -4159,7 +4145,7 @@
}
sp<InputWindowHandle> InputDispatcher::getFocusedWindowHandleLocked(int displayId) const {
- sp<IBinder> focusedToken = getValueByKey(mFocusedWindowTokenByDisplay, displayId);
+ sp<IBinder> focusedToken = mFocusResolver.getFocusedWindowToken(displayId);
return getWindowHandleLocked(focusedToken, displayId);
}
@@ -4323,24 +4309,10 @@
mLastHoverWindowHandle = nullptr;
}
- sp<IBinder> focusedToken = getValueByKey(mFocusedWindowTokenByDisplay, displayId);
- if (focusedToken) {
- FocusResult result = checkTokenFocusableLocked(focusedToken, displayId);
- if (result != FocusResult::OK) {
- onFocusChangedLocked(focusedToken, nullptr, displayId, typeToString(result));
- }
- }
-
- std::optional<FocusRequest> focusRequest =
- getOptionalValueByKey(mPendingFocusRequests, displayId);
- if (focusRequest) {
- // If the window from the pending request is now visible, provide it focus.
- FocusResult result = handleFocusRequestLocked(*focusRequest);
- if (result != FocusResult::NOT_VISIBLE) {
- // Drop the request if we were able to change the focus or we cannot change
- // it for another reason.
- mPendingFocusRequests.erase(displayId);
- }
+ std::optional<FocusResolver::FocusChanges> changes =
+ mFocusResolver.setInputWindows(displayId, windowHandles);
+ if (changes) {
+ onFocusChangedLocked(*changes);
}
std::unordered_map<int32_t, TouchState>::iterator stateIt =
@@ -4444,7 +4416,7 @@
if (mFocusedDisplayId != displayId) {
sp<IBinder> oldFocusedWindowToken =
- getValueByKey(mFocusedWindowTokenByDisplay, mFocusedDisplayId);
+ mFocusResolver.getFocusedWindowToken(mFocusedDisplayId);
if (oldFocusedWindowToken != nullptr) {
std::shared_ptr<InputChannel> inputChannel =
getInputChannelLocked(oldFocusedWindowToken);
@@ -4459,15 +4431,14 @@
mFocusedDisplayId = displayId;
// Find new focused window and validate
- sp<IBinder> newFocusedWindowToken =
- getValueByKey(mFocusedWindowTokenByDisplay, displayId);
+ sp<IBinder> newFocusedWindowToken = mFocusResolver.getFocusedWindowToken(displayId);
notifyFocusChangedLocked(oldFocusedWindowToken, newFocusedWindowToken);
if (newFocusedWindowToken == nullptr) {
ALOGW("Focused display #%" PRId32 " does not have a focused window.", displayId);
- if (!mFocusedWindowTokenByDisplay.empty()) {
+ if (mFocusResolver.hasFocusedWindowTokens()) {
ALOGE("But another display has a focused window\n%s",
- dumpFocusedWindowsLocked().c_str());
+ mFocusResolver.dumpFocusedWindows().c_str());
}
}
}
@@ -4667,46 +4638,6 @@
}
}
-std::string InputDispatcher::dumpFocusedWindowsLocked() {
- if (mFocusedWindowTokenByDisplay.empty()) {
- return INDENT "FocusedWindows: <none>\n";
- }
-
- std::string dump;
- dump += INDENT "FocusedWindows:\n";
- for (auto& it : mFocusedWindowTokenByDisplay) {
- const int32_t displayId = it.first;
- const sp<InputWindowHandle> windowHandle = getFocusedWindowHandleLocked(displayId);
- if (windowHandle) {
- dump += StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", displayId,
- windowHandle->getName().c_str());
- } else {
- dump += StringPrintf(INDENT2 "displayId=%" PRId32
- " has focused token without a window'\n",
- displayId);
- }
- }
- return dump;
-}
-
-std::string InputDispatcher::dumpPendingFocusRequestsLocked() {
- if (mPendingFocusRequests.empty()) {
- return INDENT "mPendingFocusRequests: <none>\n";
- }
-
- std::string dump;
- dump += INDENT "mPendingFocusRequests:\n";
- for (const auto& [displayId, focusRequest] : mPendingFocusRequests) {
- // Rather than printing raw values for focusRequest.token and focusRequest.focusedToken,
- // try to resolve them to actual windows.
- std::string windowName = getConnectionNameLocked(focusRequest.token);
- std::string focusedWindowName = getConnectionNameLocked(focusRequest.focusedToken);
- dump += StringPrintf(INDENT2 "displayId=%" PRId32 ", token->%s, focusedToken->%s\n",
- displayId, windowName.c_str(), focusedWindowName.c_str());
- }
- return dump;
-}
-
std::string InputDispatcher::dumpPointerCaptureStateLocked() {
std::string dump;
@@ -4746,8 +4677,7 @@
dump += StringPrintf(INDENT "FocusedApplications: <none>\n");
}
- dump += dumpFocusedWindowsLocked();
- dump += dumpPendingFocusRequestsLocked();
+ dump += mFocusResolver.dump();
dump += dumpPointerCaptureStateLocked();
if (!mTouchStatesByDisplay.empty()) {
@@ -4797,7 +4727,8 @@
"hasWallpaper=%s, visible=%s, alpha=%.2f, "
"flags=%s, type=%s, "
"frame=[%d,%d][%d,%d], globalScale=%f, "
- "applicationInfo=%s, "
+ "applicationInfo.name=%s, "
+ "applicationInfo.token=%s, "
"touchableRegion=",
i, windowInfo->name.c_str(), windowInfo->id,
windowInfo->displayId, windowInfo->portalToDisplayId,
@@ -4810,7 +4741,8 @@
windowInfo->frameLeft, windowInfo->frameTop,
windowInfo->frameRight, windowInfo->frameBottom,
windowInfo->globalScaleFactor,
- windowInfo->applicationInfo.name.c_str());
+ windowInfo->applicationInfo.name.c_str(),
+ toString(windowInfo->applicationInfo.token).c_str());
dump += dumpRegion(windowInfo->touchableRegion);
dump += StringPrintf(", inputFeatures=%s",
windowInfo->inputFeatures.string().c_str());
@@ -5140,8 +5072,7 @@
: "token without window");
}
- const sp<IBinder> focusedToken =
- getValueByKey(mFocusedWindowTokenByDisplay, mFocusedDisplayId);
+ const sp<IBinder> focusedToken = mFocusResolver.getFocusedWindowToken(mFocusedDisplayId);
if (focusedToken != windowToken) {
ALOGW("Ignoring request to %s Pointer Capture: window does not have focus.",
enabled ? "enable" : "disable");
@@ -5885,81 +5816,28 @@
void InputDispatcher::setFocusedWindow(const FocusRequest& request) {
{ // acquire lock
std::scoped_lock _l(mLock);
-
- const int32_t displayId = request.displayId;
- const sp<IBinder> oldFocusedToken = getValueByKey(mFocusedWindowTokenByDisplay, displayId);
- if (request.focusedToken && oldFocusedToken != request.focusedToken) {
- ALOGD_IF(DEBUG_FOCUS,
- "setFocusedWindow on display %" PRId32
- " ignored, reason: focusedToken is not focused",
- displayId);
- return;
- }
-
- mPendingFocusRequests.erase(displayId);
- FocusResult result = handleFocusRequestLocked(request);
- if (result == FocusResult::NOT_VISIBLE) {
- // The requested window is not currently visible. Wait for the window to become visible
- // and then provide it focus. This is to handle situations where a user action triggers
- // a new window to appear. We want to be able to queue any key events after the user
- // action and deliver it to the newly focused window. In order for this to happen, we
- // take focus from the currently focused window so key events can be queued.
- ALOGD_IF(DEBUG_FOCUS,
- "setFocusedWindow on display %" PRId32
- " pending, reason: window is not visible",
- displayId);
- mPendingFocusRequests[displayId] = request;
- onFocusChangedLocked(oldFocusedToken, nullptr, displayId,
- "setFocusedWindow_AwaitingWindowVisibility");
- } else if (result != FocusResult::OK) {
- ALOGW("setFocusedWindow on display %" PRId32 " ignored, reason:%s", displayId,
- typeToString(result));
+ std::optional<FocusResolver::FocusChanges> changes =
+ mFocusResolver.setFocusedWindow(request, getWindowHandlesLocked(request.displayId));
+ if (changes) {
+ onFocusChangedLocked(*changes);
}
} // release lock
// Wake up poll loop since it may need to make new input dispatching choices.
mLooper->wake();
}
-InputDispatcher::FocusResult InputDispatcher::handleFocusRequestLocked(
- const FocusRequest& request) {
- const int32_t displayId = request.displayId;
- const sp<IBinder> newFocusedToken = request.token;
- const sp<IBinder> oldFocusedToken = getValueByKey(mFocusedWindowTokenByDisplay, displayId);
-
- if (oldFocusedToken == request.token) {
- ALOGD_IF(DEBUG_FOCUS,
- "setFocusedWindow on display %" PRId32 " ignored, reason: already focused",
- displayId);
- return FocusResult::OK;
- }
-
- FocusResult result = checkTokenFocusableLocked(newFocusedToken, displayId);
- if (result != FocusResult::OK) {
- return result;
- }
-
- std::string_view reason =
- (request.focusedToken) ? "setFocusedWindow_FocusCheck" : "setFocusedWindow";
- onFocusChangedLocked(oldFocusedToken, newFocusedToken, displayId, reason);
- return FocusResult::OK;
-}
-
-void InputDispatcher::onFocusChangedLocked(const sp<IBinder>& oldFocusedToken,
- const sp<IBinder>& newFocusedToken, int32_t displayId,
- std::string_view reason) {
- if (oldFocusedToken) {
- std::shared_ptr<InputChannel> focusedInputChannel = getInputChannelLocked(oldFocusedToken);
+void InputDispatcher::onFocusChangedLocked(const FocusResolver::FocusChanges& changes) {
+ if (changes.oldFocus) {
+ std::shared_ptr<InputChannel> focusedInputChannel = getInputChannelLocked(changes.oldFocus);
if (focusedInputChannel) {
CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
"focus left window");
synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options);
- enqueueFocusEventLocked(oldFocusedToken, false /*hasFocus*/, reason);
+ enqueueFocusEventLocked(changes.oldFocus, false /*hasFocus*/, changes.reason);
}
- mFocusedWindowTokenByDisplay.erase(displayId);
}
- if (newFocusedToken) {
- mFocusedWindowTokenByDisplay[displayId] = newFocusedToken;
- enqueueFocusEventLocked(newFocusedToken, true /*hasFocus*/, reason);
+ if (changes.newFocus) {
+ enqueueFocusEventLocked(changes.newFocus, true /*hasFocus*/, changes.reason);
}
// If a window has pointer capture, then it must have focus. We need to ensure that this
@@ -5972,8 +5850,8 @@
// front.
disablePointerCaptureForcedLocked();
- if (mFocusedDisplayId == displayId) {
- notifyFocusChangedLocked(oldFocusedToken, newFocusedToken);
+ if (mFocusedDisplayId == changes.displayId) {
+ notifyFocusChangedLocked(changes.oldFocus, changes.newFocus);
}
}
@@ -6006,50 +5884,6 @@
mInboundQueue.push_front(std::move(entry));
}
-/**
- * Checks if the window token can be focused on a display. The token can be focused if there is
- * at least one window handle that is visible with the same token and all window handles with the
- * same token are focusable.
- *
- * In the case of mirroring, two windows may share the same window token and their visibility
- * might be different. Example, the mirrored window can cover the window its mirroring. However,
- * we expect the focusability of the windows to match since its hard to reason why one window can
- * receive focus events and the other cannot when both are backed by the same input channel.
- */
-InputDispatcher::FocusResult InputDispatcher::checkTokenFocusableLocked(const sp<IBinder>& token,
- int32_t displayId) const {
- bool allWindowsAreFocusable = true;
- bool visibleWindowFound = false;
- bool windowFound = false;
- for (const sp<InputWindowHandle>& window : getWindowHandlesLocked(displayId)) {
- if (window->getToken() != token) {
- continue;
- }
- windowFound = true;
- if (window->getInfo()->visible) {
- // Check if at least a single window is visible.
- visibleWindowFound = true;
- }
- if (!window->getInfo()->focusable) {
- // Check if all windows with the window token are focusable.
- allWindowsAreFocusable = false;
- break;
- }
- }
-
- if (!windowFound) {
- return FocusResult::NO_WINDOW;
- }
- if (!allWindowsAreFocusable) {
- return FocusResult::NOT_FOCUSABLE;
- }
- if (!visibleWindowFound) {
- return FocusResult::NOT_VISIBLE;
- }
-
- return FocusResult::OK;
-}
-
void InputDispatcher::setPointerCaptureLocked(bool enabled) {
std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
&InputDispatcher::doSetPointerCaptureLockedInterruptible);
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index c7299e9..02f5b87 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -20,6 +20,7 @@
#include "AnrTracker.h"
#include "CancelationOptions.h"
#include "Entry.h"
+#include "FocusResolver.h"
#include "InjectionState.h"
#include "InputDispatcherConfiguration.h"
#include "InputDispatcherInterface.h"
@@ -146,14 +147,6 @@
NO_POINTER_CAPTURE,
};
- enum class FocusResult {
- OK,
- NO_WINDOW,
- NOT_FOCUSABLE,
- NOT_VISIBLE,
- };
- static const char* typeToString(FocusResult result);
-
std::unique_ptr<InputThread> mThread;
sp<InputDispatcherPolicyInterface> mPolicy;
@@ -190,7 +183,7 @@
// Enqueues a focus event.
void enqueueFocusEventLocked(const sp<IBinder>& windowToken, bool hasFocus,
- std::string_view reason) REQUIRES(mLock);
+ const std::string& reason) REQUIRES(mLock);
// Adds an event to a queue of recent events for debugging purposes.
void addRecentEventLocked(std::shared_ptr<EventEntry> entry) REQUIRES(mLock);
@@ -333,9 +326,6 @@
sp<InputWindowHandle> getFocusedWindowHandleLocked(int displayId) const REQUIRES(mLock);
bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
bool hasResponsiveConnectionLocked(InputWindowHandle& windowHandle) const REQUIRES(mLock);
- FocusResult handleFocusRequestLocked(const FocusRequest&) REQUIRES(mLock);
- FocusResult checkTokenFocusableLocked(const sp<IBinder>& token, int32_t displayId) const
- REQUIRES(mLock);
/*
* Validate and update InputWindowHandles for a given display.
@@ -344,12 +334,6 @@
const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId)
REQUIRES(mLock);
- // Focus tracking for keys, trackball, etc. A window token can be associated with one or more
- // InputWindowHandles. If a window is mirrored, the window and its mirror will share the same
- // token. Focus is tracked by the token per display and the events are dispatched to the
- // channel associated by this token.
- std::unordered_map<int32_t, sp<IBinder>> mFocusedWindowTokenByDisplay GUARDED_BY(mLock);
-
std::unordered_map<int32_t, TouchState> mTouchStatesByDisplay GUARDED_BY(mLock);
// Focused applications.
@@ -359,6 +343,8 @@
// Top focused display.
int32_t mFocusedDisplayId GUARDED_BY(mLock);
+ // Keeps track of the focused window per display and determines focus changes.
+ FocusResolver mFocusResolver GUARDED_BY(mLock);
// Whether the focused window on the focused display has requested Pointer Capture.
// The state of this variable should always be in sync with the state of Pointer Capture in the
// policy, which is updated through setPointerCaptureLocked(enabled).
@@ -462,13 +448,6 @@
*/
void sendWindowResponsiveCommandLocked(sp<IBinder> connectionToken) REQUIRES(mLock);
- /**
- * This map will store the pending focus requests that cannot be currently processed. This can
- * happen if the window requested to be focused is not currently visible. Such a window might
- * become visible later, and these requests would be processed at that time.
- */
- std::unordered_map<int32_t /* displayId */, FocusRequest> mPendingFocusRequests
- GUARDED_BY(mLock);
// Optimization: AnrTracker is used to quickly find which connection is due for a timeout next.
// AnrTracker must be kept in-sync with all responsive connection.waitQueues.
@@ -586,8 +565,6 @@
void dumpDispatchStateLocked(std::string& dump) REQUIRES(mLock);
void dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors);
void logDispatchStateLocked() REQUIRES(mLock);
- std::string dumpFocusedWindowsLocked() REQUIRES(mLock);
- std::string dumpPendingFocusRequestsLocked() REQUIRES(mLock);
std::string dumpPointerCaptureStateLocked() REQUIRES(mLock);
// Registration.
@@ -603,8 +580,7 @@
uint32_t seq, bool handled) REQUIRES(mLock);
void onDispatchCycleBrokenLocked(nsecs_t currentTime, const sp<Connection>& connection)
REQUIRES(mLock);
- void onFocusChangedLocked(const sp<IBinder>& oldFocus, const sp<IBinder>& newFocus,
- int32_t displayId, std::string_view reason) REQUIRES(mLock);
+ void onFocusChangedLocked(const FocusResolver::FocusChanges& changes) REQUIRES(mLock);
void notifyFocusChangedLocked(const sp<IBinder>& oldFocus, const sp<IBinder>& newFocus)
REQUIRES(mLock);
void onAnrLocked(const sp<Connection>& connection) REQUIRES(mLock);
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index 0440f49..ca43123 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -268,6 +268,10 @@
if (id) {
outState->rawPointerData.canceledIdBits.markBit(id.value());
}
+#if DEBUG_POINTERS
+ ALOGI("Stop processing slot %zu for it received a palm event from device %s", inIndex,
+ getDeviceName().c_str());
+#endif
continue;
}
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index fb30b88..1a17bef 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -608,8 +608,7 @@
if (hasStylus()) {
mSource |= AINPUT_SOURCE_STYLUS;
}
- } else if (mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN &&
- mParameters.hasAssociatedDisplay) {
+ } else if (isTouchScreen()) {
mSource = AINPUT_SOURCE_TOUCHSCREEN;
mDeviceMode = DeviceMode::DIRECT;
if (hasStylus()) {
@@ -1453,8 +1452,9 @@
void TouchInputMapper::processRawTouches(bool timeout) {
if (mDeviceMode == DeviceMode::DISABLED) {
// Drop all input if the device is disabled.
- mCurrentRawState.clear();
- mRawStatesPending.clear();
+ cancelTouch(mCurrentRawState.when);
+ mCurrentCookedState.clear();
+ updateTouchSpots();
return;
}
@@ -1586,17 +1586,7 @@
dispatchPointerUsage(when, policyFlags, pointerUsage);
} else {
- if (mDeviceMode == DeviceMode::DIRECT && mConfig.showTouches &&
- mPointerController != nullptr) {
- mPointerController->setPresentation(PointerControllerInterface::Presentation::SPOT);
- mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
-
- mPointerController->setButtonState(mCurrentRawState.buttonState);
- mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords,
- mCurrentCookedState.cookedPointerData.idToIndex,
- mCurrentCookedState.cookedPointerData.touchingIdBits,
- mViewport.displayId);
- }
+ updateTouchSpots();
if (!mCurrentMotionAborted) {
dispatchButtonRelease(when, policyFlags);
@@ -1625,6 +1615,33 @@
mLastCookedState.copyFrom(mCurrentCookedState);
}
+void TouchInputMapper::updateTouchSpots() {
+ if (!mConfig.showTouches || mPointerController == nullptr) {
+ return;
+ }
+
+ // Update touch spots when this is a touchscreen even when it's not enabled so that we can
+ // clear touch spots.
+ if (mDeviceMode != DeviceMode::DIRECT &&
+ (mDeviceMode != DeviceMode::DISABLED || !isTouchScreen())) {
+ return;
+ }
+
+ mPointerController->setPresentation(PointerControllerInterface::Presentation::SPOT);
+ mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
+
+ mPointerController->setButtonState(mCurrentRawState.buttonState);
+ mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords,
+ mCurrentCookedState.cookedPointerData.idToIndex,
+ mCurrentCookedState.cookedPointerData.touchingIdBits,
+ mViewport.displayId);
+}
+
+bool TouchInputMapper::isTouchScreen() {
+ return mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN &&
+ mParameters.hasAssociatedDisplay;
+}
+
void TouchInputMapper::applyExternalStylusButtonState(nsecs_t when) {
if (mDeviceMode == DeviceMode::DIRECT && hasExternalStylus() && mExternalStylusId != -1) {
mCurrentRawState.buttonState |= mExternalStylusState.buttons;
@@ -1890,6 +1907,9 @@
while (!upIdBits.isEmpty()) {
uint32_t upId = upIdBits.clearFirstMarkedBit();
bool isCanceled = mCurrentCookedState.cookedPointerData.canceledIdBits.hasBit(upId);
+ if (isCanceled) {
+ ALOGI("Canceling pointer %d for the palm event was detected.", upId);
+ }
dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0,
isCanceled ? AMOTION_EVENT_FLAG_CANCELED : 0, metaState, buttonState, 0,
mLastCookedState.cookedPointerData.pointerProperties,
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index df6581d..6621825 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -757,6 +757,12 @@
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
+ // touchscreen.
+ void updateTouchSpots();
+
bool isPointInsideSurface(int32_t x, int32_t y);
const VirtualKey* findVirtualKeyHit(int32_t x, int32_t y);
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 8cb7194..959dadc 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -30,6 +30,7 @@
"AnrTracker_test.cpp",
"BlockingQueue_test.cpp",
"EventHub_test.cpp",
+ "FocusResolver_test.cpp",
"IInputFlingerQuery.aidl",
"InputClassifier_test.cpp",
"InputClassifierConverter_test.cpp",
diff --git a/services/inputflinger/tests/FocusResolver_test.cpp b/services/inputflinger/tests/FocusResolver_test.cpp
new file mode 100644
index 0000000..ef3dd65
--- /dev/null
+++ b/services/inputflinger/tests/FocusResolver_test.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "../FocusResolver.h"
+
+// atest inputflinger_tests:FocusResolverTest
+
+namespace android::inputdispatcher {
+
+class FakeWindowHandle : public InputWindowHandle {
+public:
+ FakeWindowHandle(const std::string& name, const sp<IBinder>& token, bool focusable,
+ bool visible) {
+ mInfo.token = token;
+ mInfo.name = name;
+ mInfo.visible = visible;
+ mInfo.focusable = focusable;
+ }
+
+ bool updateInfo() { return true; }
+ void setFocusable(bool focusable) { mInfo.focusable = focusable; }
+ void setVisible(bool visible) { mInfo.visible = visible; }
+};
+
+TEST(FocusResolverTest, SetFocusedWindow) {
+ sp<IBinder> focusableWindowToken = new BBinder();
+ sp<IBinder> invisibleWindowToken = new BBinder();
+ sp<IBinder> unfocusableWindowToken = new BBinder();
+ std::vector<sp<InputWindowHandle>> windows;
+ windows.push_back(new FakeWindowHandle("Focusable", focusableWindowToken, true /* focusable */,
+ true /* visible */));
+ windows.push_back(new FakeWindowHandle("Invisible", invisibleWindowToken, true /* focusable */,
+ false /* visible */));
+ windows.push_back(new FakeWindowHandle("unfocusable", unfocusableWindowToken,
+ false /* focusable */, true /* visible */));
+
+ // focusable window can get focused
+ FocusRequest request;
+ request.displayId = 42;
+ request.token = focusableWindowToken;
+ FocusResolver focusResolver;
+ std::optional<FocusResolver::FocusChanges> changes =
+ focusResolver.setFocusedWindow(request, windows);
+ ASSERT_EQ(nullptr, changes->oldFocus);
+ ASSERT_EQ(focusableWindowToken, changes->newFocus);
+ ASSERT_EQ(request.displayId, changes->displayId);
+
+ // invisible window cannot get focused
+ request.token = invisibleWindowToken;
+ changes = focusResolver.setFocusedWindow(request, windows);
+ ASSERT_EQ(focusableWindowToken, changes->oldFocus);
+ ASSERT_EQ(nullptr, changes->newFocus);
+
+ // unfocusableWindowToken window cannot get focused
+ request.token = unfocusableWindowToken;
+ changes = focusResolver.setFocusedWindow(request, windows);
+ ASSERT_FALSE(changes);
+}
+
+TEST(FocusResolverTest, SetFocusedMirroredWindow) {
+ sp<IBinder> focusableWindowToken = new BBinder();
+ sp<IBinder> invisibleWindowToken = new BBinder();
+ sp<IBinder> unfocusableWindowToken = new BBinder();
+ std::vector<sp<InputWindowHandle>> windows;
+ windows.push_back(new FakeWindowHandle("Mirror1", focusableWindowToken, true /* focusable */,
+ true /* visible */));
+ windows.push_back(new FakeWindowHandle("Mirror1", focusableWindowToken, true /* focusable */,
+ true /* visible */));
+
+ windows.push_back(new FakeWindowHandle("Mirror2Visible", invisibleWindowToken,
+ true /* focusable */, true /* visible */));
+ windows.push_back(new FakeWindowHandle("Mirror2Invisible", invisibleWindowToken,
+ true /* focusable */, false /* visible */));
+
+ windows.push_back(new FakeWindowHandle("Mirror3Focusable", unfocusableWindowToken,
+ true /* focusable */, true /* visible */));
+ windows.push_back(new FakeWindowHandle("Mirror3Unfocusable", unfocusableWindowToken,
+ false /* focusable */, true /* visible */));
+
+ // mirrored window can get focused
+ FocusRequest request;
+ request.displayId = 42;
+ request.token = focusableWindowToken;
+ FocusResolver focusResolver;
+ std::optional<FocusResolver::FocusChanges> changes =
+ focusResolver.setFocusedWindow(request, windows);
+ ASSERT_EQ(nullptr, changes->oldFocus);
+ ASSERT_EQ(focusableWindowToken, changes->newFocus);
+
+ // mirrored window with one visible window can get focused
+ request.token = invisibleWindowToken;
+ changes = focusResolver.setFocusedWindow(request, windows);
+ ASSERT_EQ(focusableWindowToken, changes->oldFocus);
+ ASSERT_EQ(invisibleWindowToken, changes->newFocus);
+
+ // mirrored window with one or more unfocusable window cannot get focused
+ request.token = unfocusableWindowToken;
+ changes = focusResolver.setFocusedWindow(request, windows);
+ ASSERT_FALSE(changes);
+}
+
+TEST(FocusResolverTest, SetInputWindows) {
+ sp<IBinder> focusableWindowToken = new BBinder();
+ std::vector<sp<InputWindowHandle>> windows;
+ sp<FakeWindowHandle> window = new FakeWindowHandle("Focusable", focusableWindowToken,
+ true /* focusable */, true /* visible */);
+ windows.push_back(window);
+
+ // focusable window can get focused
+ FocusRequest request;
+ request.displayId = 42;
+ request.token = focusableWindowToken;
+ FocusResolver focusResolver;
+ std::optional<FocusResolver::FocusChanges> changes =
+ focusResolver.setFocusedWindow(request, windows);
+ ASSERT_EQ(focusableWindowToken, changes->newFocus);
+
+ // Window visibility changes and the window loses focused
+ window->setVisible(false);
+ changes = focusResolver.setInputWindows(request.displayId, windows);
+ ASSERT_EQ(nullptr, changes->newFocus);
+ ASSERT_EQ(focusableWindowToken, changes->oldFocus);
+}
+
+TEST(FocusResolverTest, FocusRequestsCanBePending) {
+ sp<IBinder> invisibleWindowToken = new BBinder();
+ std::vector<sp<InputWindowHandle>> windows;
+
+ sp<FakeWindowHandle> invisibleWindow =
+ new FakeWindowHandle("Invisible", invisibleWindowToken, true /* focusable */,
+ false /* visible */);
+ windows.push_back(invisibleWindow);
+
+ // invisible window cannot get focused
+ FocusRequest request;
+ request.displayId = 42;
+ request.token = invisibleWindowToken;
+ FocusResolver focusResolver;
+ std::optional<FocusResolver::FocusChanges> changes =
+ focusResolver.setFocusedWindow(request, windows);
+ ASSERT_FALSE(changes);
+
+ // Window visibility changes and the window gets focused
+ invisibleWindow->setVisible(true);
+ changes = focusResolver.setInputWindows(request.displayId, windows);
+ ASSERT_EQ(nullptr, changes->oldFocus);
+ ASSERT_EQ(invisibleWindowToken, changes->newFocus);
+}
+
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 91d5864..36da8dd 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -7362,6 +7362,57 @@
mFakeListener->assertNotifyMotionWasNotCalled();
}
+TEST_F(MultiTouchInputMapperTest, Process_DeactivateViewport_AbortTouches) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_ORIENTATION_0, true /*isActive*/, UNIQUE_ID, NO_PORT,
+ ViewportType::INTERNAL);
+ std::optional<DisplayViewport> optionalDisplayViewport =
+ mFakePolicy->getDisplayViewportByUniqueId(UNIQUE_ID);
+ ASSERT_TRUE(optionalDisplayViewport.has_value());
+ DisplayViewport displayViewport = *optionalDisplayViewport;
+
+ configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ prepareAxes(POSITION);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+ // Finger down
+ int32_t x = 100, y = 100;
+ processPosition(mapper, x, y);
+ processSync(mapper);
+
+ NotifyMotionArgs motionArgs;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+
+ // Deactivate display viewport
+ displayViewport.isActive = false;
+ ASSERT_TRUE(mFakePolicy->updateViewport(displayViewport));
+ configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+
+ // Finger move
+ x += 10, y += 10;
+ processPosition(mapper, x, y);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ EXPECT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionArgs.action);
+
+ // Reactivate display viewport
+ displayViewport.isActive = true;
+ ASSERT_TRUE(mFakePolicy->updateViewport(displayViewport));
+ configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+
+ // Finger move again
+ x += 10, y += 10;
+ processPosition(mapper, x, y);
+ processSync(mapper);
+
+ // Gesture is aborted, so events after display is activated won't be dispatched until there is
+ // no pointer on the touch device.
+ mFakeListener->assertNotifyMotionWasNotCalled();
+}
+
TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) {
// Setup the first touch screen device.
prepareAxes(POSITION | ID | SLOT);
diff --git a/services/powermanager/BatterySaverPolicyConfig.cpp b/services/powermanager/BatterySaverPolicyConfig.cpp
index ee55b6b..49557bc 100644
--- a/services/powermanager/BatterySaverPolicyConfig.cpp
+++ b/services/powermanager/BatterySaverPolicyConfig.cpp
@@ -55,7 +55,6 @@
?: parcel->readBool(&mDisableAod)
?: parcel->readBool(&mDisableLaunchBoost)
?: parcel->readBool(&mDisableOptionalSensors)
- ?: parcel->readBool(&mDisableSoundTrigger)
?: parcel->readBool(&mDisableVibration)
?: parcel->readBool(&mEnableAdjustBrightness)
?: parcel->readBool(&mEnableDataSaver)
@@ -64,7 +63,8 @@
?: parcel->readBool(&mEnableQuickDoze)
?: parcel->readBool(&mForceAllAppsStandby)
?: parcel->readBool(&mForceBackgroundCheck)
- ?: parcel->readInt32(reinterpret_cast<int32_t *>(&mLocationMode));
+ ?: parcel->readInt32(reinterpret_cast<int32_t *>(&mLocationMode))
+ ?: parcel->readInt32(reinterpret_cast<int32_t *>(&mSoundTriggerMode));
}
status_t BatterySaverPolicyConfig::writeDeviceSpecificSettings(android::Parcel *parcel) const {
@@ -97,7 +97,6 @@
?: parcel->writeBool(mDisableAod)
?: parcel->writeBool(mDisableLaunchBoost)
?: parcel->writeBool(mDisableOptionalSensors)
- ?: parcel->writeBool(mDisableSoundTrigger)
?: parcel->writeBool(mDisableVibration)
?: parcel->writeBool(mEnableAdjustBrightness)
?: parcel->writeBool(mEnableDataSaver)
@@ -106,7 +105,8 @@
?: parcel->writeBool(mEnableQuickDoze)
?: parcel->writeBool(mForceAllAppsStandby)
?: parcel->writeBool(mForceBackgroundCheck)
- ?: parcel->writeInt32(static_cast<int32_t>(mLocationMode));
+ ?: parcel->writeInt32(static_cast<int32_t>(mLocationMode))
+ ?: parcel->writeInt32(static_cast<int32_t>(mSoundTriggerMode));
}
} // namespace android::os
diff --git a/services/powermanager/PowerSaveState.cpp b/services/powermanager/PowerSaveState.cpp
index 6d1830a..d705e91 100644
--- a/services/powermanager/PowerSaveState.cpp
+++ b/services/powermanager/PowerSaveState.cpp
@@ -31,6 +31,7 @@
return parcel->readBool(&mBatterySaverEnabled)
?: parcel->readBool(&mGlobalBatterySaverEnabled)
?: parcel->readInt32(reinterpret_cast<int32_t *>(&mLocationMode))
+ ?: parcel->readInt32(reinterpret_cast<int32_t *>(&mSoundTriggerMode))
?: parcel->readFloat(&mBrightnessFactor);
}
@@ -43,6 +44,7 @@
return parcel->writeBool(mBatterySaverEnabled)
?: parcel->writeBool(mGlobalBatterySaverEnabled)
?: parcel->writeInt32(static_cast<int32_t>(mLocationMode))
+ ?: parcel->writeInt32(static_cast<int32_t>(mSoundTriggerMode))
?: parcel->writeFloat(mBrightnessFactor);
}
diff --git a/services/powermanager/include/android/BatterySaverPolicyConfig.h b/services/powermanager/include/android/BatterySaverPolicyConfig.h
index 728c8a0..3a0c9d0 100644
--- a/services/powermanager/include/android/BatterySaverPolicyConfig.h
+++ b/services/powermanager/include/android/BatterySaverPolicyConfig.h
@@ -24,6 +24,7 @@
namespace android::os {
enum class LocationMode : int32_t;
+enum class SoundTriggerMode : int32_t;
/**
* BatterySaverPolicyConfig is a structure of configs to set Battery Saver policy flags.
* This file needs to be kept in sync with
@@ -40,7 +41,6 @@
bool disableAod = false,
bool disableLaunchBoost = false,
bool disableOptionalSensors = false,
- bool disableSoundTrigger = false,
bool disableVibration = false,
bool enableAdjustBrightness = false,
bool enableDataSaver = false,
@@ -49,7 +49,8 @@
bool enableQuickDoze = false,
bool forceAllAppsStandby = false,
bool forceBackgroundCheck = false,
- LocationMode locationMode = static_cast<LocationMode>(0))
+ LocationMode locationMode = static_cast<LocationMode>(0),
+ SoundTriggerMode soundTriggerMode = static_cast<SoundTriggerMode>(0))
: mAdjustBrightnessFactor(adjustBrightnessFactor),
mAdvertiseIsEnabled(advertiseIsEnabled),
mDeferFullBackup(deferFullBackup),
@@ -59,7 +60,6 @@
mDisableAod(disableAod),
mDisableLaunchBoost(disableLaunchBoost),
mDisableOptionalSensors(disableOptionalSensors),
- mDisableSoundTrigger(disableSoundTrigger),
mDisableVibration(disableVibration),
mEnableAdjustBrightness(enableAdjustBrightness),
mEnableDataSaver(enableDataSaver),
@@ -68,7 +68,8 @@
mEnableQuickDoze(enableQuickDoze),
mForceAllAppsStandby(forceAllAppsStandby),
mForceBackgroundCheck(forceBackgroundCheck),
- mLocationMode(locationMode) {
+ mLocationMode(locationMode),
+ mSoundTriggerMode(soundTriggerMode) {
}
status_t readFromParcel(const android::Parcel* parcel) override;
@@ -83,7 +84,6 @@
mDisableAod == bsp.mDisableAod &&
mDisableLaunchBoost == bsp.mDisableLaunchBoost &&
mDisableOptionalSensors == bsp.mDisableOptionalSensors &&
- mDisableSoundTrigger == bsp.mDisableSoundTrigger &&
mDisableVibration == bsp.mDisableVibration &&
mEnableAdjustBrightness == bsp.mEnableAdjustBrightness &&
mEnableDataSaver == bsp.mEnableDataSaver &&
@@ -92,7 +92,8 @@
mEnableQuickDoze == bsp.mEnableQuickDoze &&
mForceAllAppsStandby == bsp.mForceAllAppsStandby &&
mForceBackgroundCheck == bsp.mForceBackgroundCheck &&
- mLocationMode == bsp.mLocationMode;
+ mLocationMode == bsp.mLocationMode &&
+ mSoundTriggerMode == bsp.mSoundTriggerMode;
}
private:
@@ -116,8 +117,6 @@
bool mDisableLaunchBoost;
/** Disable optional sensors */
bool mDisableOptionalSensors;
- /** Disable sound trigger */
- bool mDisableSoundTrigger;
/** Disable vibration */
bool mDisableVibration;
/** Enable adjust brightness */
@@ -136,6 +135,8 @@
bool mForceBackgroundCheck;
/** Location mode */
LocationMode mLocationMode;
+ /** SoundTrigger mode */
+ SoundTriggerMode mSoundTriggerMode;
};
} // namespace android::os
diff --git a/services/powermanager/include/android/PowerSaveState.h b/services/powermanager/include/android/PowerSaveState.h
index b421f6a..1818db2 100644
--- a/services/powermanager/include/android/PowerSaveState.h
+++ b/services/powermanager/include/android/PowerSaveState.h
@@ -24,6 +24,7 @@
namespace android::os {
enum class LocationMode : int32_t;
+enum class SoundTriggerMode : int32_t;
/**
* PowerSaveState is a structure to encapsulate PowerSaveState status.
* This file needs to be kept in sync with frameworks/base/core/java/android/os/PowerSaveState.java
@@ -33,16 +34,19 @@
PowerSaveState(bool batterySaverEnabled = false,
bool globalBatterySaverEnabled = false,
LocationMode locationMode = static_cast<LocationMode>(0),
+ SoundTriggerMode soundTriggerMode = static_cast<SoundTriggerMode>(0),
float brightnessFactor = 0.5f)
: mBatterySaverEnabled(batterySaverEnabled),
mGlobalBatterySaverEnabled(globalBatterySaverEnabled),
mLocationMode(locationMode),
+ mSoundTriggerMode(soundTriggerMode),
mBrightnessFactor(brightnessFactor) {
}
bool getBatterySaverEnabled() const { return mBatterySaverEnabled; }
bool getGlobalBatterySaverEnabled() const { return mGlobalBatterySaverEnabled; }
LocationMode getLocationMode() const { return mLocationMode; }
+ SoundTriggerMode getSoundTriggerMode() const { return mSoundTriggerMode; }
float getBrightnessFactor() const { return mBrightnessFactor; }
bool operator == (const PowerSaveState &ps) const {
return mBatterySaverEnabled == ps.mBatterySaverEnabled &&
@@ -61,6 +65,8 @@
bool mGlobalBatterySaverEnabled;
/** Location mode */
LocationMode mLocationMode;
+ /** SoundTrigger mode */
+ SoundTriggerMode mSoundTriggerMode;
/** Screen brightness factor. */
float mBrightnessFactor;
};
diff --git a/services/powermanager/include/android/SoundTriggerMode.h b/services/powermanager/include/android/SoundTriggerMode.h
new file mode 100644
index 0000000..cee43e3
--- /dev/null
+++ b/services/powermanager/include/android/SoundTriggerMode.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 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_OS_SOUNDTRIGGER_MODE_H
+#define ANDROID_OS_SOUNDTRIGGER_MODE_H
+
+namespace android::os {
+
+enum class SoundTriggerMode : int32_t {
+ ALL_ENABLED = IPowerManager::SOUND_TRIGGER_MODE_ALL_ENABLED,
+ CRITICAL_ONLY = IPowerManager::SOUND_TRIGGER_MODE_CRITICAL_ONLY,
+ ALL_DISABLED = IPowerManager::SOUND_TRIGGER_MODE_ALL_DISABLED,
+ MIN = IPowerManager::SOUND_TRIGGER_MODE_ALL_ENABLED,
+ MAX = IPowerManager::SOUND_TRIGGER_MODE_ALL_DISABLED,
+};
+
+} // namespace android::os
+
+#endif /* ANDROID_OS_SOUNDTRIGGER_MODE_H */
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index d0ba8fe..4d36f0c 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -96,7 +96,7 @@
MOCK_CONST_METHOD1(isVsyncPeriodSwitchSupported, bool(PhysicalDisplayId));
MOCK_CONST_METHOD1(getDisplayVsyncPeriod, nsecs_t(PhysicalDisplayId));
MOCK_METHOD4(setActiveModeWithConstraints,
- status_t(PhysicalDisplayId, HwcConfigIndexType,
+ status_t(PhysicalDisplayId, DisplayModeId,
const hal::VsyncPeriodChangeConstraints&,
hal::VsyncPeriodChangeTimeline*));
MOCK_METHOD2(setAutoLowLatencyMode, status_t(PhysicalDisplayId, bool));
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 8551365..fe9db5a 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -139,12 +139,12 @@
return mPowerMode != hal::PowerMode::OFF;
}
-void DisplayDevice::setActiveConfig(HwcConfigIndexType mode) {
- mActiveConfig = mode;
+void DisplayDevice::setActiveMode(DisplayModeId mode) {
+ mActiveMode = mode;
}
-HwcConfigIndexType DisplayDevice::getActiveConfig() const {
- return mActiveConfig;
+DisplayModeId DisplayDevice::getActiveMode() const {
+ return mActiveMode;
}
ui::Dataspace DisplayDevice::getCompositionDataSpace() const {
@@ -210,7 +210,7 @@
result.append(" ");
StringAppendF(&result, "powerMode=%s (%d), ", to_string(mPowerMode).c_str(),
static_cast<int32_t>(mPowerMode));
- StringAppendF(&result, "activeConfig=%zu, ", mActiveConfig.value());
+ StringAppendF(&result, "activeConfig=%zu, ", mActiveMode.value());
StringAppendF(&result, "deviceProductInfo=");
if (mDeviceProductInfo) {
mDeviceProductInfo->dump(result);
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index cc38ab0..d29f97d 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -39,9 +39,9 @@
#include <utils/Timers.h>
#include "DisplayHardware/DisplayIdentification.h"
+#include "DisplayHardware/DisplayMode.h"
#include "DisplayHardware/Hal.h"
#include "DisplayHardware/PowerAdvisor.h"
-#include "Scheduler/HwcStrongTypes.h"
namespace android {
@@ -159,8 +159,8 @@
/* ------------------------------------------------------------------------
* Display active config management.
*/
- HwcConfigIndexType getActiveConfig() const;
- void setActiveConfig(HwcConfigIndexType mode);
+ DisplayModeId getActiveMode() const;
+ void setActiveMode(DisplayModeId mode);
// release HWC resources (if any) for removable displays
void disconnect();
@@ -189,7 +189,7 @@
hardware::graphics::composer::hal::PowerMode mPowerMode =
hardware::graphics::composer::hal::PowerMode::OFF;
- HwcConfigIndexType mActiveConfig;
+ DisplayModeId mActiveMode;
// TODO(b/74619554): Remove special cases for primary display.
const bool mIsPrimary;
diff --git a/services/surfaceflinger/DisplayHardware/DisplayMode.h b/services/surfaceflinger/DisplayHardware/DisplayMode.h
index 69fd00e..7ae54f1 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayMode.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayMode.h
@@ -17,11 +17,12 @@
#pragma once
#include "DisplayHardware/Hal.h"
-#include "Scheduler/HwcStrongTypes.h"
+#include "Scheduler/StrongTyping.h"
#include <android/configuration.h>
#include <utils/Timers.h>
+#include <cstddef>
#include <memory>
#include <vector>
@@ -32,6 +33,7 @@
class DisplayMode;
using DisplayModePtr = std::shared_ptr<const DisplayMode>;
using DisplayModes = std::vector<DisplayModePtr>;
+using DisplayModeId = StrongTyping<size_t, struct DisplayModeIdTag, Compare, Hash>;
class DisplayMode {
public:
@@ -43,7 +45,7 @@
return std::const_pointer_cast<const DisplayMode>(std::move(mDisplayMode));
}
- Builder& setId(HwcConfigIndexType id) {
+ Builder& setId(DisplayModeId id) {
mDisplayMode->mId = id;
return *this;
}
@@ -104,7 +106,7 @@
std::shared_ptr<DisplayMode> mDisplayMode;
};
- HwcConfigIndexType getId() const { return mId; }
+ DisplayModeId getId() const { return mId; }
hal::HWConfigId getHwcId() const { return mHwcId; }
int32_t getWidth() const { return mWidth; }
@@ -118,7 +120,7 @@
explicit DisplayMode(hal::HWConfigId id) : mHwcId(id) {}
hal::HWConfigId mHwcId;
- HwcConfigIndexType mId;
+ DisplayModeId mId;
int32_t mWidth = -1;
int32_t mHeight = -1;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index ca67935..6350144 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -318,7 +318,7 @@
displayData.modes.clear();
for (auto configId : configIds) {
auto mode = DisplayMode::Builder(configId)
- .setId(HwcConfigIndexType(displayData.modes.size()))
+ .setId(DisplayModeId(displayData.modes.size()))
.setWidth(getAttribute(hwcDisplayId, configId, hal::Attribute::WIDTH))
.setHeight(getAttribute(hwcDisplayId, configId, hal::Attribute::HEIGHT))
.setVsyncPeriod(getAttribute(hwcDisplayId, configId,
@@ -674,7 +674,7 @@
}
status_t HWComposer::setActiveModeWithConstraints(
- PhysicalDisplayId displayId, HwcConfigIndexType modeId,
+ PhysicalDisplayId displayId, DisplayModeId modeId,
const hal::VsyncPeriodChangeConstraints& constraints,
hal::VsyncPeriodChangeTimeline* outTimeline) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 2b3d2d4..59c3699 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -198,7 +198,7 @@
virtual DisplayConnectionType getDisplayConnectionType(PhysicalDisplayId) const = 0;
virtual bool isVsyncPeriodSwitchSupported(PhysicalDisplayId) const = 0;
virtual nsecs_t getDisplayVsyncPeriod(PhysicalDisplayId) const = 0;
- virtual status_t setActiveModeWithConstraints(PhysicalDisplayId, HwcConfigIndexType,
+ virtual status_t setActiveModeWithConstraints(PhysicalDisplayId, DisplayModeId,
const hal::VsyncPeriodChangeConstraints&,
hal::VsyncPeriodChangeTimeline* outTimeline) = 0;
virtual status_t setAutoLowLatencyMode(PhysicalDisplayId, bool on) = 0;
@@ -329,7 +329,7 @@
DisplayConnectionType getDisplayConnectionType(PhysicalDisplayId) const override;
bool isVsyncPeriodSwitchSupported(PhysicalDisplayId) const override;
nsecs_t getDisplayVsyncPeriod(PhysicalDisplayId displayId) const override;
- status_t setActiveModeWithConstraints(PhysicalDisplayId, HwcConfigIndexType,
+ status_t setActiveModeWithConstraints(PhysicalDisplayId, DisplayModeId,
const hal::VsyncPeriodChangeConstraints&,
hal::VsyncPeriodChangeTimeline* outTimeline) override;
status_t setAutoLowLatencyMode(PhysicalDisplayId, bool) override;
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index b63e8c8..c12a7b1 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -41,9 +41,10 @@
#include <utils/Errors.h>
#include <utils/Trace.h>
-#include "EventThread.h"
+#include "DisplayHardware/DisplayMode.h"
#include "FrameTimeline.h"
-#include "HwcStrongTypes.h"
+
+#include "EventThread.h"
#undef LOG_TAG
#define LOG_TAG "EventThread"
@@ -117,8 +118,8 @@
return event;
}
-DisplayEventReceiver::Event makeConfigChanged(PhysicalDisplayId displayId,
- HwcConfigIndexType configId, nsecs_t vsyncPeriod) {
+DisplayEventReceiver::Event makeConfigChanged(PhysicalDisplayId displayId, DisplayModeId configId,
+ nsecs_t vsyncPeriod) {
DisplayEventReceiver::Event event;
event.header = {DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED, displayId, systemTime()};
event.config.configId = configId.value();
@@ -368,7 +369,7 @@
mCondition.notify_all();
}
-void EventThread::onConfigChanged(PhysicalDisplayId displayId, HwcConfigIndexType configId,
+void EventThread::onConfigChanged(PhysicalDisplayId displayId, DisplayModeId configId,
nsecs_t vsyncPeriod) {
std::lock_guard<std::mutex> lock(mMutex);
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index e75b718..15efe21 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -31,7 +31,7 @@
#include <thread>
#include <vector>
-#include "HwcStrongTypes.h"
+#include "DisplayHardware/DisplayMode.h"
// ---------------------------------------------------------------------------
namespace android {
@@ -124,7 +124,7 @@
virtual void onHotplugReceived(PhysicalDisplayId displayId, bool connected) = 0;
// called when SF changes the active config and apps needs to be notified about the change
- virtual void onConfigChanged(PhysicalDisplayId displayId, HwcConfigIndexType configId,
+ virtual void onConfigChanged(PhysicalDisplayId displayId, DisplayModeId configId,
nsecs_t vsyncPeriod) = 0;
// called when SF updates the Frame Rate Override list
@@ -173,7 +173,7 @@
void onHotplugReceived(PhysicalDisplayId displayId, bool connected) override;
- void onConfigChanged(PhysicalDisplayId displayId, HwcConfigIndexType configId,
+ void onConfigChanged(PhysicalDisplayId displayId, DisplayModeId configId,
nsecs_t vsyncPeriod) override;
void onFrameRateOverridesChanged(PhysicalDisplayId displayId,
diff --git a/services/surfaceflinger/Scheduler/HwcStrongTypes.h b/services/surfaceflinger/Scheduler/HwcStrongTypes.h
deleted file mode 100644
index b6a33a2..0000000
--- a/services/surfaceflinger/Scheduler/HwcStrongTypes.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2019 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 "StrongTyping.h"
-
-#include <cstddef>
-
-namespace android {
-
-// Strong types for the different indexes as they are referring to a different base.
-using HwcConfigIndexType = StrongTyping<size_t, struct HwcConfigIndexTypeTag, Compare, Add, Hash>;
-
-} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index b02596a..0f1e267 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -489,7 +489,7 @@
}
std::optional<Fps> RefreshRateConfigs::onKernelTimerChanged(
- std::optional<HwcConfigIndexType> desiredActiveConfigId, bool timerExpired) const {
+ std::optional<DisplayModeId> desiredActiveConfigId, bool timerExpired) const {
std::lock_guard lock(mLock);
const auto& current = desiredActiveConfigId ? *mRefreshRates.at(*desiredActiveConfigId)
@@ -554,26 +554,25 @@
return *mRefreshRates.at(getCurrentPolicyLocked()->defaultConfig);
}
-void RefreshRateConfigs::setCurrentConfigId(HwcConfigIndexType configId) {
+void RefreshRateConfigs::setCurrentConfigId(DisplayModeId configId) {
std::lock_guard lock(mLock);
mCurrentRefreshRate = mRefreshRates.at(configId).get();
}
-RefreshRateConfigs::RefreshRateConfigs(const DisplayModes& configs,
- HwcConfigIndexType currentConfigId)
+RefreshRateConfigs::RefreshRateConfigs(const DisplayModes& configs, DisplayModeId currentConfigId)
: mKnownFrameRates(constructKnownFrameRates(configs)) {
updateDisplayConfigs(configs, currentConfigId);
}
void RefreshRateConfigs::updateDisplayConfigs(const DisplayModes& configs,
- HwcConfigIndexType currentConfigId) {
+ DisplayModeId currentConfigId) {
std::lock_guard lock(mLock);
LOG_ALWAYS_FATAL_IF(configs.empty());
LOG_ALWAYS_FATAL_IF(currentConfigId.value() >= configs.size());
mRefreshRates.clear();
- for (auto configId = HwcConfigIndexType(0); configId.value() < configs.size(); configId++) {
- const auto& config = configs.at(static_cast<size_t>(configId.value()));
+ for (const auto& config : configs) {
+ const auto configId = config->getId();
const auto fps = Fps::fromPeriodNsecs(config->getVsyncPeriod());
mRefreshRates.emplace(configId,
std::make_unique<RefreshRate>(configId, config, fps,
@@ -663,7 +662,7 @@
return mDisplayManagerPolicy;
}
-bool RefreshRateConfigs::isConfigAllowed(HwcConfigIndexType config) const {
+bool RefreshRateConfigs::isConfigAllowed(DisplayModeId config) const {
std::lock_guard lock(mLock);
for (const RefreshRate* refreshRate : mAppRequestRefreshRates) {
if (refreshRate->configId == config) {
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index a5d37c2..ef193ca 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -27,7 +27,6 @@
#include "DisplayHardware/DisplayMode.h"
#include "DisplayHardware/HWComposer.h"
#include "Fps.h"
-#include "HwcStrongTypes.h"
#include "Scheduler/SchedulerUtils.h"
#include "Scheduler/Seamlessness.h"
#include "Scheduler/StrongTyping.h"
@@ -65,10 +64,10 @@
};
public:
- RefreshRate(HwcConfigIndexType configId, DisplayModePtr config, Fps fps, ConstructorTag)
+ RefreshRate(DisplayModeId configId, DisplayModePtr config, Fps fps, ConstructorTag)
: configId(configId), hwcConfig(config), fps(std::move(fps)) {}
- HwcConfigIndexType getConfigId() const { return configId; }
+ DisplayModeId getConfigId() const { return configId; }
nsecs_t getVsyncPeriod() const { return hwcConfig->getVsyncPeriod(); }
int32_t getConfigGroup() const { return hwcConfig->getConfigGroup(); }
std::string getName() const { return to_string(fps); }
@@ -99,7 +98,7 @@
// This config ID corresponds to the position of the config in the vector that is stored
// on the device.
- const HwcConfigIndexType configId;
+ const DisplayModeId configId;
// The config itself
DisplayModePtr hwcConfig;
// Refresh rate in frames per second
@@ -107,7 +106,7 @@
};
using AllRefreshRatesMapType =
- std::unordered_map<HwcConfigIndexType, std::unique_ptr<const RefreshRate>>;
+ std::unordered_map<DisplayModeId, std::unique_ptr<const RefreshRate>>;
struct FpsRange {
Fps min{0.0f};
@@ -131,7 +130,7 @@
public:
// The default config, used to ensure we only initiate display config switches within the
// same config group as defaultConfigId's group.
- HwcConfigIndexType defaultConfig;
+ DisplayModeId defaultConfig;
// Whether or not we switch config groups to get the best frame rate.
bool allowGroupSwitching = kAllowGroupSwitchingDefault;
// The primary refresh rate range represents display manager's general guidance on the
@@ -148,18 +147,18 @@
Policy() = default;
- Policy(HwcConfigIndexType defaultConfig, const FpsRange& range)
+ Policy(DisplayModeId defaultConfig, const FpsRange& range)
: Policy(defaultConfig, kAllowGroupSwitchingDefault, range, range) {}
- Policy(HwcConfigIndexType defaultConfig, bool allowGroupSwitching, const FpsRange& range)
+ Policy(DisplayModeId defaultConfig, bool allowGroupSwitching, const FpsRange& range)
: Policy(defaultConfig, allowGroupSwitching, range, range) {}
- Policy(HwcConfigIndexType defaultConfig, const FpsRange& primaryRange,
+ Policy(DisplayModeId defaultConfig, const FpsRange& primaryRange,
const FpsRange& appRequestRange)
: Policy(defaultConfig, kAllowGroupSwitchingDefault, primaryRange, appRequestRange) {}
- Policy(HwcConfigIndexType defaultConfig, bool allowGroupSwitching,
- const FpsRange& primaryRange, const FpsRange& appRequestRange)
+ Policy(DisplayModeId defaultConfig, bool allowGroupSwitching, const FpsRange& primaryRange,
+ const FpsRange& appRequestRange)
: defaultConfig(defaultConfig),
allowGroupSwitching(allowGroupSwitching),
primaryRange(primaryRange),
@@ -199,7 +198,7 @@
Policy getDisplayManagerPolicy() const EXCLUDES(mLock);
// Returns true if config is allowed by the current policy.
- bool isConfigAllowed(HwcConfigIndexType config) const EXCLUDES(mLock);
+ bool isConfigAllowed(DisplayModeId config) const EXCLUDES(mLock);
// Describes the different options the layer voted for refresh rate
enum class LayerVoteType {
@@ -265,7 +264,7 @@
return {mMinSupportedRefreshRate->getFps(), mMaxSupportedRefreshRate->getFps()};
}
- std::optional<Fps> onKernelTimerChanged(std::optional<HwcConfigIndexType> desiredActiveConfigId,
+ std::optional<Fps> onKernelTimerChanged(std::optional<DisplayModeId> desiredActiveConfigId,
bool timerExpired) const EXCLUDES(mLock);
// Returns the highest refresh rate according to the current policy. May change at runtime. Only
@@ -279,16 +278,16 @@
// the policy.
RefreshRate getCurrentRefreshRateByPolicy() const;
- // Returns the refresh rate that corresponds to a HwcConfigIndexType. This may change at
+ // Returns the refresh rate that corresponds to a DisplayModeId. This may change at
// runtime.
// TODO(b/159590486) An invalid config id may be given here if the dipslay configs have changed.
- RefreshRate getRefreshRateFromConfigId(HwcConfigIndexType configId) const EXCLUDES(mLock) {
+ RefreshRate getRefreshRateFromConfigId(DisplayModeId configId) const EXCLUDES(mLock) {
std::lock_guard lock(mLock);
return *mRefreshRates.at(configId);
};
// Stores the current configId the device operates at
- void setCurrentConfigId(HwcConfigIndexType configId) EXCLUDES(mLock);
+ void setCurrentConfigId(DisplayModeId configId) EXCLUDES(mLock);
// Returns a string that represents the layer vote type
static std::string layerVoteTypeString(LayerVoteType vote);
@@ -296,9 +295,9 @@
// Returns a known frame rate that is the closest to frameRate
Fps findClosestKnownFrameRate(Fps frameRate) const;
- RefreshRateConfigs(const DisplayModes& configs, HwcConfigIndexType currentConfigId);
+ RefreshRateConfigs(const DisplayModes& configs, DisplayModeId currentConfigId);
- void updateDisplayConfigs(const DisplayModes& configs, HwcConfigIndexType currentConfig)
+ void updateDisplayConfigs(const DisplayModes& configs, DisplayModeId currentConfig)
EXCLUDES(mLock);
// Returns whether switching configs (refresh rate or resolution) is possible.
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 92786fd..588b83d 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -349,7 +349,7 @@
}
void Scheduler::onPrimaryDisplayConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
- HwcConfigIndexType configId, nsecs_t vsyncPeriod) {
+ DisplayModeId configId, nsecs_t vsyncPeriod) {
{
std::lock_guard<std::mutex> lock(mFeatureStateLock);
// Cache the last reported config for primary display.
@@ -389,7 +389,7 @@
void Scheduler::onNonPrimaryDisplayConfigChanged(ConnectionHandle handle,
PhysicalDisplayId displayId,
- HwcConfigIndexType configId, nsecs_t vsyncPeriod) {
+ DisplayModeId configId, nsecs_t vsyncPeriod) {
android::EventThread* thread;
{
std::lock_guard<std::mutex> lock(mConnectionsLock);
@@ -603,7 +603,7 @@
scheduler::LayerHistory::Summary summary = mLayerHistory->summarize(systemTime());
scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
- HwcConfigIndexType newConfigId;
+ DisplayModeId newConfigId;
bool frameRateChanged;
bool frameRateOverridesChanged;
{
@@ -789,7 +789,7 @@
template <class T>
bool Scheduler::handleTimerStateChanged(T* currentState, T newState) {
- HwcConfigIndexType newConfigId;
+ DisplayModeId newConfigId;
bool refreshRateChanged = false;
bool frameRateOverridesChanged;
scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
@@ -829,7 +829,7 @@
return consideredSignals.touch;
}
-HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType(
+DisplayModeId Scheduler::calculateRefreshRateConfigIndexType(
scheduler::RefreshRateConfigs::GlobalSignals* consideredSignals) {
ATRACE_CALL();
if (consideredSignals) *consideredSignals = {};
@@ -851,7 +851,7 @@
.getConfigId();
}
-std::optional<HwcConfigIndexType> Scheduler::getPreferredConfigId() {
+std::optional<DisplayModeId> Scheduler::getPreferredConfigId() {
std::lock_guard<std::mutex> lock(mFeatureStateLock);
// Make sure that the default config ID is first updated, before returned.
if (mFeatures.configId.has_value()) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index cae3fe7..340ca8e 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -87,11 +87,10 @@
sp<EventThreadConnection> getEventConnection(ConnectionHandle);
void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected);
- void onPrimaryDisplayConfigChanged(ConnectionHandle, PhysicalDisplayId,
- HwcConfigIndexType configId, nsecs_t vsyncPeriod)
- EXCLUDES(mFeatureStateLock);
+ void onPrimaryDisplayConfigChanged(ConnectionHandle, PhysicalDisplayId, DisplayModeId configId,
+ nsecs_t vsyncPeriod) EXCLUDES(mFeatureStateLock);
void onNonPrimaryDisplayConfigChanged(ConnectionHandle, PhysicalDisplayId,
- HwcConfigIndexType configId, nsecs_t vsyncPeriod);
+ DisplayModeId configId, nsecs_t vsyncPeriod);
void onScreenAcquired(ConnectionHandle);
void onScreenReleased(ConnectionHandle);
@@ -154,7 +153,7 @@
void dumpVsync(std::string&) const;
// Get the appropriate refresh for current conditions.
- std::optional<HwcConfigIndexType> getPreferredConfigId();
+ std::optional<DisplayModeId> getPreferredConfigId();
// Notifies the scheduler about a refresh rate timeline change.
void onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline);
@@ -226,9 +225,9 @@
void setVsyncPeriod(nsecs_t period);
// This function checks whether individual features that are affecting the refresh rate
- // selection were initialized, prioritizes them, and calculates the HwcConfigIndexType
+ // selection were initialized, prioritizes them, and calculates the DisplayModeId
// for the suggested refresh rate.
- HwcConfigIndexType calculateRefreshRateConfigIndexType(
+ DisplayModeId calculateRefreshRateConfigIndexType(
scheduler::RefreshRateConfigs::GlobalSignals* consideredSignals = nullptr)
REQUIRES(mFeatureStateLock);
@@ -284,7 +283,7 @@
TouchState touch = TouchState::Inactive;
TimerState displayPowerTimer = TimerState::Expired;
- std::optional<HwcConfigIndexType> configId;
+ std::optional<DisplayModeId> configId;
LayerHistory::Summary contentRequirements;
bool isDisplayPowerStateNormal = true;
@@ -293,7 +292,7 @@
struct ConfigChangedParams {
ConnectionHandle handle;
PhysicalDisplayId displayId;
- HwcConfigIndexType configId;
+ DisplayModeId configId;
nsecs_t vsyncPeriod;
};
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index d91b542..0a51659 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -981,7 +981,7 @@
Mutex::Autolock lock(mStateLock);
if (const auto display = getDisplayDeviceLocked(displayToken)) {
- activeConfig = display->getActiveConfig().value();
+ activeConfig = display->getActiveMode().value();
isPrimary = display->isPrimary();
} else {
ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
@@ -1013,7 +1013,7 @@
} else {
// Check is we are already at the desired config
const auto display = getDefaultDisplayDeviceLocked();
- if (!display || display->getActiveConfig() == refreshRate.getConfigId()) {
+ if (!display || display->getActiveMode() == refreshRate.getConfigId()) {
return;
}
@@ -1056,7 +1056,7 @@
ALOGW("Attempt to set allowed display configs for virtual display");
return INVALID_OPERATION;
} else {
- const HwcConfigIndexType config(mode);
+ const DisplayModeId config(mode);
const auto fps = mRefreshRateConfigs->getRefreshRateFromConfigId(config).getFps();
// Keep the old switching type.
const auto allowGroupSwitching =
@@ -1081,12 +1081,11 @@
return;
}
- auto oldRefreshRate =
- mRefreshRateConfigs->getRefreshRateFromConfigId(display->getActiveConfig());
+ auto oldRefreshRate = mRefreshRateConfigs->getRefreshRateFromConfigId(display->getActiveMode());
std::lock_guard<std::mutex> lock(mActiveConfigLock);
mRefreshRateConfigs->setCurrentConfigId(mUpcomingActiveConfig.configId);
- display->setActiveConfig(mUpcomingActiveConfig.configId);
+ display->setActiveMode(mUpcomingActiveConfig.configId);
auto refreshRate =
mRefreshRateConfigs->getRefreshRateFromConfigId(mUpcomingActiveConfig.configId);
@@ -1136,7 +1135,7 @@
ALOGV("performSetActiveConfig changing active config to %zu(%s)",
refreshRate.getConfigId().value(), refreshRate.getName().c_str());
const auto display = getDefaultDisplayDeviceLocked();
- if (!display || display->getActiveConfig() == desiredActiveConfig->configId) {
+ if (!display || display->getActiveMode() == desiredActiveConfig->configId) {
// display is not valid or we are already in the requested mode
// on both cases there is nothing left to do
desiredActiveConfigChangeDone();
@@ -1608,7 +1607,7 @@
*compositorTiming = getBE().mCompositorTiming;
}
-bool SurfaceFlinger::isDisplayConfigAllowed(HwcConfigIndexType configId) const {
+bool SurfaceFlinger::isDisplayConfigAllowed(DisplayModeId configId) const {
return mRefreshRateConfigs->isConfigAllowed(configId);
}
@@ -2479,7 +2478,7 @@
if (!state.isVirtual()) {
const auto physicalId = display->getPhysicalId();
auto activeConfigId = getHwComposer().getActiveMode(physicalId)->getId();
- display->setActiveConfig(activeConfigId);
+ display->setActiveMode(activeConfigId);
display->setDeviceProductInfo(state.physical->deviceProductInfo);
}
@@ -5419,7 +5418,7 @@
// mRefreshRateConfigs->getCurrentRefreshRate()
static_cast<void>(schedule([=] {
const auto desiredActiveConfig = getDesiredActiveConfig();
- const std::optional<HwcConfigIndexType> desiredConfigId = desiredActiveConfig
+ const std::optional<DisplayModeId> desiredConfigId = desiredActiveConfig
? std::make_optional(desiredActiveConfig->configId)
: std::nullopt;
@@ -6040,7 +6039,7 @@
repaintEverythingForHWC();
}
- display->setActiveConfig(policy->defaultConfig);
+ display->setActiveMode(policy->defaultConfig);
const nsecs_t vsyncPeriod = getHwComposer()
.getModes(displayId)[policy->defaultConfig.value()]
->getVsyncPeriod();
@@ -6070,11 +6069,11 @@
// TODO(b/140204874): Leave the event in until we do proper testing with all apps that might
// be depending in this callback.
const nsecs_t vsyncPeriod =
- mRefreshRateConfigs->getRefreshRateFromConfigId(display->getActiveConfig())
+ mRefreshRateConfigs->getRefreshRateFromConfigId(display->getActiveMode())
.getVsyncPeriod();
const auto physicalId = display->getPhysicalId();
mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, physicalId,
- display->getActiveConfig(), vsyncPeriod);
+ display->getActiveMode(), vsyncPeriod);
toggleKernelIdleTimer();
auto configId = mScheduler->getPreferredConfigId();
@@ -6119,7 +6118,7 @@
return INVALID_OPERATION;
} else {
using Policy = scheduler::RefreshRateConfigs::Policy;
- const Policy policy{HwcConfigIndexType(defaultConfig),
+ const Policy policy{DisplayModeId(defaultConfig),
allowGroupSwitching,
{Fps(primaryRefreshRateMin), Fps(primaryRefreshRateMax)},
{Fps(appRequestRefreshRateMin), Fps(appRequestRefreshRateMax)}};
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index b2b7cac..c90fb4a 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -414,7 +414,7 @@
};
struct ActiveConfigInfo {
- HwcConfigIndexType configId;
+ DisplayModeId configId;
Scheduler::ConfigEvent event = Scheduler::ConfigEvent::None;
bool operator!=(const ActiveConfigInfo& other) const {
@@ -918,7 +918,7 @@
// the desired refresh rate.
void changeRefreshRateLocked(const RefreshRate&, Scheduler::ConfigEvent) REQUIRES(mStateLock);
- bool isDisplayConfigAllowed(HwcConfigIndexType configId) const REQUIRES(mStateLock);
+ bool isDisplayConfigAllowed(DisplayModeId configId) const REQUIRES(mStateLock);
// Gets the fence for the previous frame.
// Must be called on the main thread.
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 0cd50ce..45bc29c 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -27,8 +27,8 @@
#include <utils/Errors.h>
#include "AsyncCallRecorder.h"
+#include "DisplayHardware/DisplayMode.h"
#include "Scheduler/EventThread.h"
-#include "Scheduler/HwcStrongTypes.h"
using namespace std::chrono_literals;
using namespace std::placeholders;
@@ -540,17 +540,17 @@
}
TEST_F(EventThreadTest, postConfigChangedPrimary) {
- mThread->onConfigChanged(INTERNAL_DISPLAY_ID, HwcConfigIndexType(7), 16666666);
+ mThread->onConfigChanged(INTERNAL_DISPLAY_ID, DisplayModeId(7), 16666666);
expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 7, 16666666);
}
TEST_F(EventThreadTest, postConfigChangedExternal) {
- mThread->onConfigChanged(EXTERNAL_DISPLAY_ID, HwcConfigIndexType(5), 16666666);
+ mThread->onConfigChanged(EXTERNAL_DISPLAY_ID, DisplayModeId(5), 16666666);
expectConfigChangedEventReceivedByConnection(EXTERNAL_DISPLAY_ID, 5, 16666666);
}
TEST_F(EventThreadTest, postConfigChangedPrimary64bit) {
- mThread->onConfigChanged(DISPLAY_ID_64BIT, HwcConfigIndexType(7), 16666666);
+ mThread->onConfigChanged(DISPLAY_ID_64BIT, DisplayModeId(7), 16666666);
expectConfigChangedEventReceivedByConnection(DISPLAY_ID_64BIT, 7, 16666666);
}
@@ -559,7 +559,7 @@
sp<MockEventThreadConnection> suppressConnection =
createConnection(suppressConnectionEventRecorder);
- mThread->onConfigChanged(INTERNAL_DISPLAY_ID, HwcConfigIndexType(9), 16666666);
+ mThread->onConfigChanged(INTERNAL_DISPLAY_ID, DisplayModeId(9), 16666666);
expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 9, 16666666);
auto args = suppressConnectionEventRecorder.waitForCall();
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index 5bab534..71986fe 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -33,6 +33,7 @@
#include <gui/LayerMetadata.h>
#include <log/log.h>
+#include "DisplayHardware/DisplayMode.h"
#include "DisplayHardware/HWComposer.h"
#include "mock/DisplayHardware/MockComposer.h"
@@ -229,7 +230,7 @@
constraints.seamlessRequired = false;
hal::VsyncPeriodChangeTimeline timeline = {0, 0, 0};
- constexpr HwcConfigIndexType kConfigIndex(0);
+ constexpr DisplayModeId kConfigIndex(0);
const auto status =
hwc.setActiveModeWithConstraints(physicalId, kConfigIndex, constraints, &timeline);
EXPECT_EQ(NO_ERROR, status);
@@ -242,9 +243,8 @@
hwc.allocatePhysicalDisplay(hwcId, physicalId);
for (size_t configIndex = 0; configIndex < kConfigs.size(); configIndex++) {
- const auto status =
- hwc.setActiveModeWithConstraints(physicalId, HwcConfigIndexType(configIndex),
- constraints, &timeline);
+ const auto status = hwc.setActiveModeWithConstraints(physicalId, DisplayModeId(configIndex),
+ constraints, &timeline);
EXPECT_EQ(NO_ERROR, status) << "Error when switching to config " << configIndex;
}
}
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 7024c1e..04cafbc 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -111,14 +111,16 @@
}
RefreshRateConfigs mConfigs{{DisplayMode::Builder(0)
+ .setId(DisplayModeId(0))
.setVsyncPeriod(int32_t(LO_FPS_PERIOD))
.setConfigGroup(0)
.build(),
DisplayMode::Builder(1)
+ .setId(DisplayModeId(1))
.setVsyncPeriod(int32_t(HI_FPS_PERIOD))
.setConfigGroup(0)
.build()},
- HwcConfigIndexType(0)};
+ DisplayModeId(0)};
mock::NoOpSchedulerCallback mSchedulerCallback;
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 27c181d..0a747ab 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -72,13 +72,13 @@
}
// Test config IDs
- static inline const HwcConfigIndexType HWC_CONFIG_ID_60 = HwcConfigIndexType(0);
- static inline const HwcConfigIndexType HWC_CONFIG_ID_90 = HwcConfigIndexType(1);
- static inline const HwcConfigIndexType HWC_CONFIG_ID_72 = HwcConfigIndexType(2);
- static inline const HwcConfigIndexType HWC_CONFIG_ID_120 = HwcConfigIndexType(3);
- static inline const HwcConfigIndexType HWC_CONFIG_ID_30 = HwcConfigIndexType(4);
- static inline const HwcConfigIndexType HWC_CONFIG_ID_25 = HwcConfigIndexType(5);
- static inline const HwcConfigIndexType HWC_CONFIG_ID_50 = HwcConfigIndexType(6);
+ static inline const DisplayModeId HWC_CONFIG_ID_60 = DisplayModeId(0);
+ static inline const DisplayModeId HWC_CONFIG_ID_90 = DisplayModeId(1);
+ static inline const DisplayModeId HWC_CONFIG_ID_72 = DisplayModeId(2);
+ static inline const DisplayModeId HWC_CONFIG_ID_120 = DisplayModeId(3);
+ static inline const DisplayModeId HWC_CONFIG_ID_30 = DisplayModeId(4);
+ static inline const DisplayModeId HWC_CONFIG_ID_25 = DisplayModeId(5);
+ static inline const DisplayModeId HWC_CONFIG_ID_50 = DisplayModeId(6);
// Test configs
DisplayModePtr mConfig60 = createConfig(HWC_CONFIG_ID_60, 0, Fps(60.0f).getPeriodNsecs());
@@ -144,8 +144,8 @@
RefreshRate mExpected120Config = {HWC_CONFIG_ID_120, mConfig120, Fps(120),
RefreshRate::ConstructorTag(0)};
private:
- DisplayModePtr createConfig(HwcConfigIndexType configId, int32_t configGroup,
- int64_t vsyncPeriod, ui::Size resolution = ui::Size());
+ DisplayModePtr createConfig(DisplayModeId configId, int32_t configGroup, int64_t vsyncPeriod,
+ ui::Size resolution = ui::Size());
};
using Builder = DisplayMode::Builder;
@@ -162,10 +162,10 @@
ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
}
-DisplayModePtr RefreshRateConfigsTest::createConfig(HwcConfigIndexType configId,
- int32_t configGroup, int64_t vsyncPeriod,
- ui::Size resolution) {
+DisplayModePtr RefreshRateConfigsTest::createConfig(DisplayModeId configId, int32_t configGroup,
+ int64_t vsyncPeriod, ui::Size resolution) {
return DisplayMode::Builder(hal::HWConfigId(configId.value()))
+ .setId(configId)
.setVsyncPeriod(int32_t(vsyncPeriod))
.setConfigGroup(configGroup)
.setHeight(resolution.height)
@@ -187,8 +187,7 @@
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m60OnlyConfigDevice,
/*currentConfigId=*/HWC_CONFIG_ID_60);
- ASSERT_LT(refreshRateConfigs->setDisplayManagerPolicy(
- {HwcConfigIndexType(10), {Fps(60), Fps(60)}}),
+ ASSERT_LT(refreshRateConfigs->setDisplayManagerPolicy({DisplayModeId(10), {Fps(60), Fps(60)}}),
0);
ASSERT_LT(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(20), Fps(40)}}),
0);
@@ -1334,7 +1333,7 @@
// Return the config ID from calling getBestRefreshRate() for a single layer with the
// given voteType and fps.
auto getFrameRate = [&](LayerVoteType voteType, Fps fps, bool touchActive = false,
- bool focused = true) -> HwcConfigIndexType {
+ bool focused = true) -> DisplayModeId {
layers[0].vote = voteType;
layers[0].desiredRefreshRate = fps;
layers[0].focused = focused;
@@ -1391,8 +1390,7 @@
auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
layers[0].name = "Test layer";
- const auto getIdleFrameRate = [&](LayerVoteType voteType,
- bool touchActive) -> HwcConfigIndexType {
+ const auto getIdleFrameRate = [&](LayerVoteType voteType, bool touchActive) -> DisplayModeId {
layers[0].vote = voteType;
layers[0].desiredRefreshRate = Fps(90.f);
RefreshRateConfigs::GlobalSignals consideredSignals;
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
index 4a96fc5..ceccd81 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
@@ -25,7 +25,7 @@
#include <log/log.h>
#include <thread>
-#include "Scheduler/HwcStrongTypes.h"
+#include "DisplayHardware/DisplayMode.h"
#include "Scheduler/RefreshRateConfigs.h"
#include "Scheduler/RefreshRateStats.h"
#include "mock/MockTimeStats.h"
@@ -40,8 +40,8 @@
class RefreshRateStatsTest : public testing::Test {
protected:
- static inline const auto CONFIG_ID_0 = HwcConfigIndexType(0);
- static inline const auto CONFIG_ID_1 = HwcConfigIndexType(1);
+ static inline const auto CONFIG_ID_0 = DisplayModeId(0);
+ static inline const auto CONFIG_ID_1 = DisplayModeId(1);
static inline const auto CONFIG_GROUP_0 = 0;
static constexpr int64_t VSYNC_90 = 11111111;
static constexpr int64_t VSYNC_60 = 16666667;
@@ -62,8 +62,7 @@
std::unique_ptr<RefreshRateConfigs> mRefreshRateConfigs;
std::unique_ptr<RefreshRateStats> mRefreshRateStats;
- DisplayModePtr createConfig(HwcConfigIndexType configId, int32_t configGroup,
- int64_t vsyncPeriod);
+ DisplayModePtr createConfig(DisplayModeId configId, int32_t configGroup, int64_t vsyncPeriod);
};
RefreshRateStatsTest::RefreshRateStatsTest() {
@@ -78,9 +77,10 @@
ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
}
-DisplayModePtr RefreshRateStatsTest::createConfig(HwcConfigIndexType configId, int32_t configGroup,
+DisplayModePtr RefreshRateStatsTest::createConfig(DisplayModeId configId, int32_t configGroup,
int64_t vsyncPeriod) {
return DisplayMode::Builder(static_cast<hal::HWConfigId>(configId.value()))
+ .setId(configId)
.setVsyncPeriod(static_cast<int32_t>(vsyncPeriod))
.setConfigGroup(configGroup)
.build();
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 757c702..e688e10 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -53,7 +53,7 @@
const scheduler::RefreshRateConfigs
mConfigs{{DisplayMode::Builder(0).setVsyncPeriod(16'666'667).setConfigGroup(0).build()},
- HwcConfigIndexType(0)};
+ DisplayModeId(0)};
mock::SchedulerCallback mSchedulerCallback;
@@ -175,7 +175,7 @@
}
TEST_F(SchedulerTest, onNonPrimaryDisplayConfigChanged_invalidParameters) {
- HwcConfigIndexType configId = HwcConfigIndexType(111);
+ DisplayModeId configId = DisplayModeId(111);
nsecs_t vsyncPeriod = 111111;
// If the handle is incorrect, the function should return before
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
index cedb404..ca7c755 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
@@ -264,8 +264,8 @@
EXPECT_EQ(Case::HdrSupport::HDR_DOLBY_VISION_SUPPORTED, device->hasDolbyVisionSupport());
// Note: This is not Case::Display::HWC_ACTIVE_CONFIG_ID as the ids are
// remapped, and the test only ever sets up one config. If there were an error
- // looking up the remapped index, device->getActiveConfig() would be -1 instead.
- EXPECT_EQ(0, device->getActiveConfig().value());
+ // looking up the remapped index, device->getActiveMode() would be -1 instead.
+ EXPECT_EQ(0, device->getActiveMode().value());
EXPECT_EQ(Case::PerFrameMetadataSupport::PER_FRAME_METADATA_KEYS,
device->getSupportedPerFrameMetadata());
}
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 2192977..d3e90e3 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -91,7 +91,7 @@
}
void onNonPrimaryDisplayConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
- HwcConfigIndexType configId, nsecs_t vsyncPeriod) {
+ DisplayModeId configId, nsecs_t vsyncPeriod) {
return Scheduler::onNonPrimaryDisplayConfigChanged(handle, displayId, configId,
vsyncPeriod);
}
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 2b8a67d..9a9eeab 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -210,15 +210,21 @@
std::unique_ptr<EventThread> appEventThread,
std::unique_ptr<EventThread> sfEventThread,
ISchedulerCallback* callback = nullptr, bool hasMultipleConfigs = false) {
- DisplayModes configs{
- DisplayMode::Builder(0).setVsyncPeriod(16'666'667).setConfigGroup(0).build()};
+ DisplayModes configs{DisplayMode::Builder(0)
+ .setId(DisplayModeId(0))
+ .setVsyncPeriod(16'666'667)
+ .setConfigGroup(0)
+ .build()};
if (hasMultipleConfigs) {
- configs.emplace_back(
- DisplayMode::Builder(1).setVsyncPeriod(11'111'111).setConfigGroup(0).build());
+ configs.emplace_back(DisplayMode::Builder(1)
+ .setId(DisplayModeId(1))
+ .setVsyncPeriod(11'111'111)
+ .setConfigGroup(0)
+ .build());
}
- const auto currConfig = HwcConfigIndexType(0);
+ const auto currConfig = DisplayModeId(0);
mFlinger->mRefreshRateConfigs =
std::make_unique<scheduler::RefreshRateConfigs>(configs, currConfig);
const auto currFps =
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index 650d52d..40437bf 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -33,7 +33,7 @@
MOCK_METHOD0(onScreenReleased, void());
MOCK_METHOD0(onScreenAcquired, void());
MOCK_METHOD2(onHotplugReceived, void(PhysicalDisplayId, bool));
- MOCK_METHOD3(onConfigChanged, void(PhysicalDisplayId, HwcConfigIndexType, nsecs_t));
+ MOCK_METHOD3(onConfigChanged, void(PhysicalDisplayId, DisplayModeId, nsecs_t));
MOCK_METHOD2(onFrameRateOverridesChanged,
void(PhysicalDisplayId, std::vector<FrameRateOverride>));
MOCK_CONST_METHOD1(dump, void(std::string&));