Merge "SF: Rename HwcConfigIndexType to DisplayModeId"
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/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 */