Merge "Remove ableist language"
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index 0712c0a..28e5ee2 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -240,11 +240,11 @@
     EXPECT_GE(st.st_size, 1000000 /* 1MB */);
 }
 
-TEST_F(ZippedBugreportGenerationTest, TakesBetween30And300Seconds) {
-    EXPECT_GE(duration, 30s) << "Expected completion in more than 30s. Actual time "
-                             << duration.count() << " s.";
+TEST_F(ZippedBugreportGenerationTest, TakesBetween20And300Seconds) {
+    EXPECT_GE(duration, 20s) << "Expected completion in more than 20s. Actual time "
+                             << duration.count() << " ms.";
     EXPECT_LE(duration, 300s) << "Expected completion in less than 300s. Actual time "
-                              << duration.count() << " s.";
+                              << duration.count() << " ms.";
 }
 
 /**
diff --git a/cmds/ip-up-vpn/Android.bp b/cmds/ip-up-vpn/Android.bp
new file mode 100644
index 0000000..c746f7f
--- /dev/null
+++ b/cmds/ip-up-vpn/Android.bp
@@ -0,0 +1,31 @@
+// Copyright 2011 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.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_binary {
+    name: "ip-up-vpn",
+
+    srcs: ["ip-up-vpn.c"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    shared_libs: [
+        "libcutils",
+        "liblog",
+    ],
+}
diff --git a/cmds/ip-up-vpn/Android.mk b/cmds/ip-up-vpn/Android.mk
deleted file mode 100644
index 396ae9d..0000000
--- a/cmds/ip-up-vpn/Android.mk
+++ /dev/null
@@ -1,30 +0,0 @@
-#
-# Copyright (C) 2011 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := ip-up-vpn.c
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_SHARED_LIBRARIES := libcutils liblog
-LOCAL_MODULE := ip-up-vpn
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/ppp
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_EXECUTABLE)
diff --git a/cmds/service/service.cpp b/cmds/service/service.cpp
index fe417a3..d5ca725 100644
--- a/cmds/service/service.cpp
+++ b/cmds/service/service.cpp
@@ -21,6 +21,7 @@
 #include <cutils/ashmem.h>
 
 #include <getopt.h>
+#include <libgen.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp
index 80c0548..32922ca 100644
--- a/cmds/servicemanager/Android.bp
+++ b/cmds/servicemanager/Android.bp
@@ -47,6 +47,14 @@
 }
 
 cc_binary {
+    name: "servicemanager.microdroid",
+    defaults: ["servicemanager_defaults"],
+    init_rc: ["servicemanager.microdroid.rc"],
+    srcs: ["main.cpp"],
+    bootstrap: true,
+}
+
+cc_binary {
     name: "servicemanager.recovery",
     stem: "servicemanager",
     recovery: true,
diff --git a/cmds/servicemanager/servicemanager.microdroid.rc b/cmds/servicemanager/servicemanager.microdroid.rc
new file mode 100644
index 0000000..e01f132
--- /dev/null
+++ b/cmds/servicemanager/servicemanager.microdroid.rc
@@ -0,0 +1,8 @@
+service servicemanager /system/bin/servicemanager.microdroid
+    class core
+    user system
+    group system readproc
+    critical
+    onrestart restart apexd
+    task_profiles ServiceCapacityLow
+    shutdown critical
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 1be3a69..31dee23 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -245,6 +245,12 @@
 }
 
 prebuilt_etc {
+    name: "go_handheld_core_hardware.prebuilt.xml",
+    src: "go_handheld_core_hardware.xml",
+    defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
     name: "handheld_core_hardware.prebuilt.xml",
     src: "handheld_core_hardware.xml",
     defaults: ["frameworks_native_data_etc_defaults"],
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index e4df98a..355b3b4 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -32,7 +32,7 @@
         "com.android.uwb",
         "com.android.virt",
     ],
-    min_sdk_version: "current",
+    min_sdk_version: "Tiramisu",
 }
 
 rust_library {
@@ -44,6 +44,7 @@
         "libtokio",
     ],
     host_supported: true,
+    vendor_available: true,
     target: {
         darwin: {
             enabled: false,
@@ -52,8 +53,10 @@
     apex_available: [
         "//apex_available:platform",
         "com.android.compos",
+        "com.android.uwb",
         "com.android.virt",
     ],
+    min_sdk_version: "Tiramisu",
 }
 
 rust_library {
@@ -79,7 +82,7 @@
         "com.android.uwb",
         "com.android.virt",
     ],
-    min_sdk_version: "current",
+    min_sdk_version: "Tiramisu",
     lints: "none",
     clippy_lints: "none",
 }
@@ -137,7 +140,7 @@
         "com.android.uwb",
         "com.android.virt",
     ],
-    min_sdk_version: "current",
+    min_sdk_version: "Tiramisu",
 }
 
 // TODO(b/184872979): remove once the Rust API is created.
@@ -154,7 +157,7 @@
         "com.android.uwb",
         "com.android.virt",
     ],
-    min_sdk_version: "current",
+    min_sdk_version: "Tiramisu",
 }
 
 rust_test {
diff --git a/libs/binder/rust/binder_tokio/lib.rs b/libs/binder/rust/binder_tokio/lib.rs
index 47dcdc2..9dcef42 100644
--- a/libs/binder/rust/binder_tokio/lib.rs
+++ b/libs/binder/rust/binder_tokio/lib.rs
@@ -28,8 +28,8 @@
 //!
 //! [`Tokio`]: crate::Tokio
 
-use binder::public_api::{BinderAsyncPool, BoxFuture, Strong};
-use binder::{FromIBinder, StatusCode, BinderAsyncRuntime};
+use binder::{BinderAsyncPool, BoxFuture, FromIBinder, StatusCode, Strong};
+use binder::binder_impl::BinderAsyncRuntime;
 use std::future::Future;
 
 /// Retrieve an existing service for a particular interface, sleeping for a few
@@ -37,12 +37,12 @@
 pub async fn get_interface<T: FromIBinder + ?Sized + 'static>(name: &str) -> Result<Strong<T>, StatusCode> {
     if binder::is_handling_transaction() {
         // See comment in the BinderAsyncPool impl.
-        return binder::public_api::get_interface::<T>(name);
+        return binder::get_interface::<T>(name);
     }
 
     let name = name.to_string();
     let res = tokio::task::spawn_blocking(move || {
-        binder::public_api::get_interface::<T>(&name)
+        binder::get_interface::<T>(&name)
     }).await;
 
     // The `is_panic` branch is not actually reachable in Android as we compile
@@ -61,12 +61,12 @@
 pub async fn wait_for_interface<T: FromIBinder + ?Sized + 'static>(name: &str) -> Result<Strong<T>, StatusCode> {
     if binder::is_handling_transaction() {
         // See comment in the BinderAsyncPool impl.
-        return binder::public_api::wait_for_interface::<T>(name);
+        return binder::wait_for_interface::<T>(name);
     }
 
     let name = name.to_string();
     let res = tokio::task::spawn_blocking(move || {
-        binder::public_api::wait_for_interface::<T>(&name)
+        binder::wait_for_interface::<T>(&name)
     }).await;
 
     // The `is_panic` branch is not actually reachable in Android as we compile
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 4d6b294..467e51e 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -192,9 +192,6 @@
     /// Is this object still alive?
     fn is_binder_alive(&self) -> bool;
 
-    /// Send a ping transaction to this object
-    fn ping_binder(&mut self) -> Result<()>;
-
     /// Indicate that the service intends to receive caller security contexts.
     #[cfg(not(android_vndk))]
     fn set_requesting_sid(&mut self, enable: bool);
@@ -270,6 +267,9 @@
     /// The recipient will no longer be called if this object
     /// dies.
     fn unlink_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()>;
+
+    /// Send a ping transaction to this object
+    fn ping_binder(&mut self) -> Result<()>;
 }
 
 /// Opaque reference to the type of a Binder interface.
@@ -536,13 +536,13 @@
 /// ```
 macro_rules! binder_fn_get_class {
     ($class:ty) => {
-        binder_fn_get_class!($crate::InterfaceClass::new::<$class>());
+        binder_fn_get_class!($crate::binder_impl::InterfaceClass::new::<$class>());
     };
 
     ($constructor:expr) => {
-        fn get_class() -> $crate::InterfaceClass {
+        fn get_class() -> $crate::binder_impl::InterfaceClass {
             static CLASS_INIT: std::sync::Once = std::sync::Once::new();
-            static mut CLASS: Option<$crate::InterfaceClass> = None;
+            static mut CLASS: Option<$crate::binder_impl::InterfaceClass> = None;
 
             CLASS_INIT.call_once(|| unsafe {
                 // Safety: This assignment is guarded by the `CLASS_INIT` `Once`
@@ -772,7 +772,7 @@
                 native: $native($on_transact),
                 proxy: $proxy {},
                 $(async: $async_interface,)?
-                stability: $crate::Stability::default(),
+                stability: $crate::binder_impl::Stability::default(),
             }
         }
     };
@@ -811,7 +811,7 @@
                     $($fname: $fty = $finit),*
                 },
                 $(async: $async_interface,)?
-                stability: $crate::Stability::default(),
+                stability: $crate::binder_impl::Stability::default(),
             }
         }
     };
@@ -828,9 +828,9 @@
     } => {
         $crate::declare_binder_interface! {
             $interface[$descriptor] {
-                @doc[concat!("A binder [`Remotable`]($crate::Remotable) that holds an [`", stringify!($interface), "`] object.")]
+                @doc[concat!("A binder [`Remotable`]($crate::binder_impl::Remotable) that holds an [`", stringify!($interface), "`] object.")]
                 native: $native($on_transact),
-                @doc[concat!("A binder [`Proxy`]($crate::Proxy) that holds an [`", stringify!($interface), "`] remote interface.")]
+                @doc[concat!("A binder [`Proxy`]($crate::binder_impl::Proxy) that holds an [`", stringify!($interface), "`] remote interface.")]
                 proxy: $proxy {
                     $($fname: $fty = $finit),*
                 },
@@ -867,7 +867,7 @@
             }
         }
 
-        impl $crate::Proxy for $proxy
+        impl $crate::binder_impl::Proxy for $proxy
         where
             $proxy: $interface,
         {
@@ -875,7 +875,7 @@
                 $descriptor
             }
 
-            fn from_binder(mut binder: $crate::SpIBinder) -> $crate::Result<Self> {
+            fn from_binder(mut binder: $crate::SpIBinder) -> std::result::Result<Self, $crate::StatusCode> {
                 Ok(Self { binder, $($fname: $finit),* })
             }
         }
@@ -887,19 +887,19 @@
         impl $native {
             /// Create a new binder service.
             pub fn new_binder<T: $interface + Sync + Send + 'static>(inner: T, features: $crate::BinderFeatures) -> $crate::Strong<dyn $interface> {
-                let mut binder = $crate::Binder::new_with_stability($native(Box::new(inner)), $stability);
+                let mut binder = $crate::binder_impl::Binder::new_with_stability($native(Box::new(inner)), $stability);
                 #[cfg(not(android_vndk))]
-                $crate::IBinderInternal::set_requesting_sid(&mut binder, features.set_requesting_sid);
+                $crate::binder_impl::IBinderInternal::set_requesting_sid(&mut binder, features.set_requesting_sid);
                 $crate::Strong::new(Box::new(binder))
             }
         }
 
-        impl $crate::Remotable for $native {
+        impl $crate::binder_impl::Remotable for $native {
             fn get_descriptor() -> &'static str {
                 $descriptor
             }
 
-            fn on_transact(&self, code: $crate::TransactionCode, data: &$crate::BorrowedParcel<'_>, reply: &mut $crate::BorrowedParcel<'_>) -> $crate::Result<()> {
+            fn on_transact(&self, code: $crate::binder_impl::TransactionCode, data: &$crate::binder_impl::BorrowedParcel<'_>, reply: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
                 match $on_transact(&*self.0, code, data, reply) {
                     // The C++ backend converts UNEXPECTED_NULL into an exception
                     Err($crate::StatusCode::UNEXPECTED_NULL) => {
@@ -913,19 +913,19 @@
                 }
             }
 
-            fn on_dump(&self, file: &std::fs::File, args: &[&std::ffi::CStr]) -> $crate::Result<()> {
+            fn on_dump(&self, file: &std::fs::File, args: &[&std::ffi::CStr]) -> std::result::Result<(), $crate::StatusCode> {
                 self.0.dump(file, args)
             }
 
-            fn get_class() -> $crate::InterfaceClass {
+            fn get_class() -> $crate::binder_impl::InterfaceClass {
                 static CLASS_INIT: std::sync::Once = std::sync::Once::new();
-                static mut CLASS: Option<$crate::InterfaceClass> = None;
+                static mut CLASS: Option<$crate::binder_impl::InterfaceClass> = None;
 
                 CLASS_INIT.call_once(|| unsafe {
                     // Safety: This assignment is guarded by the `CLASS_INIT` `Once`
                     // variable, and therefore is thread-safe, as it can only occur
                     // once.
-                    CLASS = Some($crate::InterfaceClass::new::<$crate::Binder<$native>>());
+                    CLASS = Some($crate::binder_impl::InterfaceClass::new::<$crate::binder_impl::Binder<$native>>());
                 });
                 unsafe {
                     // Safety: The `CLASS` variable can only be mutated once, above,
@@ -936,25 +936,25 @@
         }
 
         impl $crate::FromIBinder for dyn $interface {
-            fn try_from(mut ibinder: $crate::SpIBinder) -> $crate::Result<$crate::Strong<dyn $interface>> {
-                use $crate::AssociateClass;
+            fn try_from(mut ibinder: $crate::SpIBinder) -> std::result::Result<$crate::Strong<dyn $interface>, $crate::StatusCode> {
+                use $crate::binder_impl::AssociateClass;
 
                 let existing_class = ibinder.get_class();
                 if let Some(class) = existing_class {
-                    if class != <$native as $crate::Remotable>::get_class() &&
-                        class.get_descriptor() == <$native as $crate::Remotable>::get_descriptor()
+                    if class != <$native as $crate::binder_impl::Remotable>::get_class() &&
+                        class.get_descriptor() == <$native as $crate::binder_impl::Remotable>::get_descriptor()
                     {
                         // The binder object's descriptor string matches what we
                         // expect. We still need to treat this local or already
                         // associated object as remote, because we can't cast it
                         // into a Rust service object without a matching class
                         // pointer.
-                        return Ok($crate::Strong::new(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?)));
+                        return Ok($crate::Strong::new(Box::new(<$proxy as $crate::binder_impl::Proxy>::from_binder(ibinder)?)));
                     }
                 }
 
-                if ibinder.associate_class(<$native as $crate::Remotable>::get_class()) {
-                    let service: $crate::Result<$crate::Binder<$native>> =
+                if ibinder.associate_class(<$native as $crate::binder_impl::Remotable>::get_class()) {
+                    let service: std::result::Result<$crate::binder_impl::Binder<$native>, $crate::StatusCode> =
                         std::convert::TryFrom::try_from(ibinder.clone());
                     if let Ok(service) = service {
                         // We were able to associate with our expected class and
@@ -962,7 +962,7 @@
                         return Ok($crate::Strong::new(Box::new(service)));
                     } else {
                         // Service is remote
-                        return Ok($crate::Strong::new(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?)));
+                        return Ok($crate::Strong::new(Box::new(<$proxy as $crate::binder_impl::Proxy>::from_binder(ibinder)?)));
                     }
                 }
 
@@ -970,18 +970,18 @@
             }
         }
 
-        impl $crate::parcel::Serialize for dyn $interface + '_
+        impl $crate::binder_impl::Serialize for dyn $interface + '_
         where
             dyn $interface: $crate::Interface
         {
-            fn serialize(&self, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
+            fn serialize(&self, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
                 let binder = $crate::Interface::as_binder(self);
                 parcel.write(&binder)
             }
         }
 
-        impl $crate::parcel::SerializeOption for dyn $interface + '_ {
-            fn serialize_option(this: Option<&Self>, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
+        impl $crate::binder_impl::SerializeOption for dyn $interface + '_ {
+            fn serialize_option(this: Option<&Self>, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
                 parcel.write(&this.map($crate::Interface::as_binder))
             }
         }
@@ -1004,25 +1004,25 @@
         $(
         // Async interface trait implementations.
         impl<P: $crate::BinderAsyncPool> $crate::FromIBinder for dyn $async_interface<P> {
-            fn try_from(mut ibinder: $crate::SpIBinder) -> $crate::Result<$crate::Strong<dyn $async_interface<P>>> {
-                use $crate::AssociateClass;
+            fn try_from(mut ibinder: $crate::SpIBinder) -> std::result::Result<$crate::Strong<dyn $async_interface<P>>, $crate::StatusCode> {
+                use $crate::binder_impl::AssociateClass;
 
                 let existing_class = ibinder.get_class();
                 if let Some(class) = existing_class {
-                    if class != <$native as $crate::Remotable>::get_class() &&
-                        class.get_descriptor() == <$native as $crate::Remotable>::get_descriptor()
+                    if class != <$native as $crate::binder_impl::Remotable>::get_class() &&
+                        class.get_descriptor() == <$native as $crate::binder_impl::Remotable>::get_descriptor()
                     {
                         // The binder object's descriptor string matches what we
                         // expect. We still need to treat this local or already
                         // associated object as remote, because we can't cast it
                         // into a Rust service object without a matching class
                         // pointer.
-                        return Ok($crate::Strong::new(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?)));
+                        return Ok($crate::Strong::new(Box::new(<$proxy as $crate::binder_impl::Proxy>::from_binder(ibinder)?)));
                     }
                 }
 
-                if ibinder.associate_class(<$native as $crate::Remotable>::get_class()) {
-                    let service: $crate::Result<$crate::Binder<$native>> =
+                if ibinder.associate_class(<$native as $crate::binder_impl::Remotable>::get_class()) {
+                    let service: std::result::Result<$crate::binder_impl::Binder<$native>, $crate::StatusCode> =
                         std::convert::TryFrom::try_from(ibinder.clone());
                     if let Ok(service) = service {
                         // We were able to associate with our expected class and
@@ -1031,7 +1031,7 @@
                         //return Ok($crate::Strong::new(Box::new(service)));
                     } else {
                         // Service is remote
-                        return Ok($crate::Strong::new(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?)));
+                        return Ok($crate::Strong::new(Box::new(<$proxy as $crate::binder_impl::Proxy>::from_binder(ibinder)?)));
                     }
                 }
 
@@ -1039,15 +1039,15 @@
             }
         }
 
-        impl<P: $crate::BinderAsyncPool> $crate::parcel::Serialize for dyn $async_interface<P> + '_ {
-            fn serialize(&self, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
+        impl<P: $crate::BinderAsyncPool> $crate::binder_impl::Serialize for dyn $async_interface<P> + '_ {
+            fn serialize(&self, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
                 let binder = $crate::Interface::as_binder(self);
                 parcel.write(&binder)
             }
         }
 
-        impl<P: $crate::BinderAsyncPool> $crate::parcel::SerializeOption for dyn $async_interface<P> + '_ {
-            fn serialize_option(this: Option<&Self>, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
+        impl<P: $crate::BinderAsyncPool> $crate::binder_impl::SerializeOption for dyn $async_interface<P> + '_ {
+            fn serialize_option(this: Option<&Self>, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
                 parcel.write(&this.map($crate::Interface::as_binder))
             }
         }
@@ -1067,11 +1067,11 @@
             }
         }
 
-        impl<P: $crate::BinderAsyncPool> $crate::ToAsyncInterface<P> for dyn $interface {
+        impl<P: $crate::BinderAsyncPool> $crate::binder_impl::ToAsyncInterface<P> for dyn $interface {
             type Target = dyn $async_interface<P>;
         }
 
-        impl<P: $crate::BinderAsyncPool> $crate::ToSyncInterface for dyn $async_interface<P> {
+        impl<P: $crate::BinderAsyncPool> $crate::binder_impl::ToSyncInterface for dyn $async_interface<P> {
             type Target = dyn $interface;
         }
         )?
@@ -1103,29 +1103,29 @@
             }
         }
 
-        impl $crate::parcel::Serialize for $enum {
-            fn serialize(&self, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
+        impl $crate::binder_impl::Serialize for $enum {
+            fn serialize(&self, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
                 parcel.write(&self.0)
             }
         }
 
-        impl $crate::parcel::SerializeArray for $enum {
-            fn serialize_array(slice: &[Self], parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
+        impl $crate::binder_impl::SerializeArray for $enum {
+            fn serialize_array(slice: &[Self], parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
                 let v: Vec<$backing> = slice.iter().map(|x| x.0).collect();
-                <$backing as binder::parcel::SerializeArray>::serialize_array(&v[..], parcel)
+                <$backing as $crate::binder_impl::SerializeArray>::serialize_array(&v[..], parcel)
             }
         }
 
-        impl $crate::parcel::Deserialize for $enum {
-            fn deserialize(parcel: &$crate::parcel::BorrowedParcel<'_>) -> $crate::Result<Self> {
+        impl $crate::binder_impl::Deserialize for $enum {
+            fn deserialize(parcel: &$crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<Self, $crate::StatusCode> {
                 parcel.read().map(Self)
             }
         }
 
-        impl $crate::parcel::DeserializeArray for $enum {
-            fn deserialize_array(parcel: &$crate::parcel::BorrowedParcel<'_>) -> $crate::Result<Option<Vec<Self>>> {
+        impl $crate::binder_impl::DeserializeArray for $enum {
+            fn deserialize_array(parcel: &$crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<Option<Vec<Self>>, $crate::StatusCode> {
                 let v: Option<Vec<$backing>> =
-                    <$backing as binder::parcel::DeserializeArray>::deserialize_array(parcel)?;
+                    <$backing as $crate::binder_impl::DeserializeArray>::deserialize_array(parcel)?;
                 Ok(v.map(|v| v.into_iter().map(Self).collect()))
             }
         }
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 20d90f7..1d7de98 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -101,45 +101,50 @@
 mod binder_async;
 mod error;
 mod native;
+mod parcel;
 mod state;
 
 use binder_ndk_sys as sys;
 
-pub mod parcel;
-
-pub use crate::binder::{
-    BinderFeatures, FromIBinder, IBinder, IBinderInternal, Interface, InterfaceClass, Remotable,
-    Stability, Strong, ToAsyncInterface, ToSyncInterface, TransactionCode, TransactionFlags, Weak,
-    FIRST_CALL_TRANSACTION, FLAG_CLEAR_BUF, FLAG_ONEWAY, FLAG_PRIVATE_LOCAL, LAST_CALL_TRANSACTION,
+pub use binder::{BinderFeatures, FromIBinder, IBinder, Interface, Strong, Weak};
+pub use crate::binder_async::{BinderAsyncPool, BoxFuture};
+pub use error::{ExceptionCode, Status, StatusCode};
+pub use native::{
+    add_service, force_lazy_services_persist, is_handling_transaction, register_lazy_service,
 };
-pub use crate::binder_async::{BoxFuture, BinderAsyncPool, BinderAsyncRuntime};
-pub use error::{status_t, ExceptionCode, Result, Status, StatusCode};
-pub use native::{add_service, force_lazy_services_persist, is_handling_transaction, register_lazy_service, Binder};
-pub use parcel::{BorrowedParcel, Parcel};
-pub use proxy::{get_interface, get_service, wait_for_interface, wait_for_service};
-pub use proxy::{AssociateClass, DeathRecipient, Proxy, SpIBinder, WpIBinder};
+pub use parcel::{ParcelFileDescriptor, Parcelable, ParcelableHolder};
+pub use proxy::{
+    get_interface, get_service, wait_for_interface, wait_for_service, DeathRecipient, SpIBinder,
+    WpIBinder,
+};
 pub use state::{ProcessState, ThreadState};
 
+/// Binder result containing a [`Status`] on error.
+pub type Result<T> = std::result::Result<T, Status>;
+
+/// Advanced Binder APIs needed internally by AIDL or when manually using Binder
+/// without AIDL.
+pub mod binder_impl {
+    pub use crate::binder::{
+        IBinderInternal, InterfaceClass, Remotable, Stability, ToAsyncInterface, ToSyncInterface,
+        TransactionCode, TransactionFlags, FIRST_CALL_TRANSACTION, FLAG_CLEAR_BUF, FLAG_ONEWAY,
+        FLAG_PRIVATE_LOCAL, LAST_CALL_TRANSACTION,
+    };
+    pub use crate::binder_async::BinderAsyncRuntime;
+    pub use crate::error::status_t;
+    pub use crate::native::Binder;
+    pub use crate::parcel::{
+        BorrowedParcel, Deserialize, DeserializeArray, DeserializeOption, Parcel,
+        ParcelableMetadata, Serialize, SerializeArray, SerializeOption, NON_NULL_PARCELABLE_FLAG,
+        NULL_PARCELABLE_FLAG,
+    };
+    pub use crate::proxy::{AssociateClass, Proxy};
+}
+
 /// Unstable, in-development API that only allowlisted clients are allowed to use.
+#[doc(hidden)]
 pub mod unstable_api {
     pub use crate::binder::AsNative;
     pub use crate::proxy::unstable_api::new_spibinder;
     pub use crate::sys::AIBinder;
 }
-
-/// The public API usable outside AIDL-generated interface crates.
-pub mod public_api {
-    pub use super::parcel::{ParcelFileDescriptor, ParcelableHolder};
-    pub use super::{
-        add_service, force_lazy_services_persist, get_interface, register_lazy_service,
-        wait_for_interface,
-    };
-    pub use super::{
-        BinderAsyncPool, BinderFeatures, BoxFuture, DeathRecipient, ExceptionCode, IBinder,
-        Interface, ProcessState, SpIBinder, Status, StatusCode, Strong, ThreadState, Weak,
-        WpIBinder,
-    };
-
-    /// Binder result containing a [`Status`] on error.
-    pub type Result<T> = std::result::Result<T, Status>;
-}
diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs
index 61f88b6..0c7e48d 100644
--- a/libs/binder/rust/src/parcel/parcelable.rs
+++ b/libs/binder/rust/src/parcel/parcelable.rs
@@ -802,35 +802,32 @@
 #[macro_export]
 macro_rules! impl_serialize_for_parcelable {
     ($parcelable:ident) => {
-        impl $crate::parcel::Serialize for $parcelable {
+        impl $crate::binder_impl::Serialize for $parcelable {
             fn serialize(
                 &self,
-                parcel: &mut $crate::parcel::BorrowedParcel<'_>,
-            ) -> $crate::Result<()> {
-                <Self as $crate::parcel::SerializeOption>::serialize_option(
-                    Some(self),
-                    parcel,
-                )
+                parcel: &mut $crate::binder_impl::BorrowedParcel<'_>,
+            ) -> std::result::Result<(), $crate::StatusCode> {
+                <Self as $crate::binder_impl::SerializeOption>::serialize_option(Some(self), parcel)
             }
         }
 
-        impl $crate::parcel::SerializeArray for $parcelable {}
+        impl $crate::binder_impl::SerializeArray for $parcelable {}
 
-        impl $crate::parcel::SerializeOption for $parcelable {
+        impl $crate::binder_impl::SerializeOption for $parcelable {
             fn serialize_option(
                 this: Option<&Self>,
-                parcel: &mut $crate::parcel::BorrowedParcel<'_>,
-            ) -> $crate::Result<()> {
+                parcel: &mut $crate::binder_impl::BorrowedParcel<'_>,
+            ) -> std::result::Result<(), $crate::StatusCode> {
                 if let Some(this) = this {
-                    use $crate::parcel::Parcelable;
-                    parcel.write(&$crate::parcel::NON_NULL_PARCELABLE_FLAG)?;
+                    use $crate::Parcelable;
+                    parcel.write(&$crate::binder_impl::NON_NULL_PARCELABLE_FLAG)?;
                     this.write_to_parcel(parcel)
                 } else {
-                    parcel.write(&$crate::parcel::NULL_PARCELABLE_FLAG)
+                    parcel.write(&$crate::binder_impl::NULL_PARCELABLE_FLAG)
                 }
             }
         }
-    }
+    };
 }
 
 /// Implement `Deserialize` trait and friends for a parcelable
@@ -842,54 +839,54 @@
 #[macro_export]
 macro_rules! impl_deserialize_for_parcelable {
     ($parcelable:ident) => {
-        impl $crate::parcel::Deserialize for $parcelable {
+        impl $crate::binder_impl::Deserialize for $parcelable {
             fn deserialize(
-                parcel: &$crate::parcel::BorrowedParcel<'_>,
-            ) -> $crate::Result<Self> {
-                $crate::parcel::DeserializeOption::deserialize_option(parcel)
+                parcel: &$crate::binder_impl::BorrowedParcel<'_>,
+            ) -> std::result::Result<Self, $crate::StatusCode> {
+                $crate::binder_impl::DeserializeOption::deserialize_option(parcel)
                     .transpose()
                     .unwrap_or(Err($crate::StatusCode::UNEXPECTED_NULL))
             }
             fn deserialize_from(
                 &mut self,
-                parcel: &$crate::parcel::BorrowedParcel<'_>,
-            ) -> $crate::Result<()> {
+                parcel: &$crate::binder_impl::BorrowedParcel<'_>,
+            ) -> std::result::Result<(), $crate::StatusCode> {
                 let status: i32 = parcel.read()?;
-                if status == $crate::parcel::NULL_PARCELABLE_FLAG {
+                if status == $crate::binder_impl::NULL_PARCELABLE_FLAG {
                     Err($crate::StatusCode::UNEXPECTED_NULL)
                 } else {
-                    use $crate::parcel::Parcelable;
+                    use $crate::Parcelable;
                     self.read_from_parcel(parcel)
                 }
             }
         }
 
-        impl $crate::parcel::DeserializeArray for $parcelable {}
+        impl $crate::binder_impl::DeserializeArray for $parcelable {}
 
-        impl $crate::parcel::DeserializeOption for $parcelable {
+        impl $crate::binder_impl::DeserializeOption for $parcelable {
             fn deserialize_option(
-                parcel: &$crate::parcel::BorrowedParcel<'_>,
-            ) -> $crate::Result<Option<Self>> {
+                parcel: &$crate::binder_impl::BorrowedParcel<'_>,
+            ) -> std::result::Result<Option<Self>, $crate::StatusCode> {
                 let mut result = None;
                 Self::deserialize_option_from(&mut result, parcel)?;
                 Ok(result)
             }
             fn deserialize_option_from(
                 this: &mut Option<Self>,
-                parcel: &$crate::parcel::BorrowedParcel<'_>,
-            ) -> $crate::Result<()> {
+                parcel: &$crate::binder_impl::BorrowedParcel<'_>,
+            ) -> std::result::Result<(), $crate::StatusCode> {
                 let status: i32 = parcel.read()?;
-                if status == $crate::parcel::NULL_PARCELABLE_FLAG {
+                if status == $crate::binder_impl::NULL_PARCELABLE_FLAG {
                     *this = None;
                     Ok(())
                 } else {
-                    use $crate::parcel::Parcelable;
+                    use $crate::Parcelable;
                     this.get_or_insert_with(Self::default)
                         .read_from_parcel(parcel)
                 }
             }
         }
-    }
+    };
 }
 
 impl<T: Serialize> Serialize for Box<T> {
@@ -918,7 +915,7 @@
 
 #[cfg(test)]
 mod tests {
-    use crate::Parcel;
+    use crate::parcel::Parcel;
     use super::*;
 
     #[test]
diff --git a/libs/binder/rust/src/parcel/parcelable_holder.rs b/libs/binder/rust/src/parcel/parcelable_holder.rs
index b4282b2..d58e839 100644
--- a/libs/binder/rust/src/parcel/parcelable_holder.rs
+++ b/libs/binder/rust/src/parcel/parcelable_holder.rs
@@ -15,9 +15,11 @@
  */
 
 use crate::binder::Stability;
-use crate::error::{Result, StatusCode};
-use crate::parcel::{Parcel, BorrowedParcel, Parcelable};
-use crate::{impl_deserialize_for_parcelable, impl_serialize_for_parcelable};
+use crate::error::StatusCode;
+use crate::parcel::{
+    BorrowedParcel, Deserialize, Parcel, Parcelable, Serialize, NON_NULL_PARCELABLE_FLAG,
+    NULL_PARCELABLE_FLAG,
+};
 
 use downcast_rs::{impl_downcast, DowncastSync};
 use std::any::Any;
@@ -53,12 +55,6 @@
     Parcel(Parcel),
 }
 
-impl Default for ParcelableHolderData {
-    fn default() -> Self {
-        ParcelableHolderData::Empty
-    }
-}
-
 /// A container that can hold any arbitrary `Parcelable`.
 ///
 /// This type is currently used for AIDL parcelable fields.
@@ -66,7 +62,7 @@
 /// `ParcelableHolder` is currently not thread-safe (neither
 /// `Send` nor `Sync`), mainly because it internally contains
 /// a `Parcel` which in turn is not thread-safe.
-#[derive(Debug, Default)]
+#[derive(Debug)]
 pub struct ParcelableHolder {
     // This is a `Mutex` because of `get_parcelable`
     // which takes `&self` for consistency with C++.
@@ -97,7 +93,7 @@
     }
 
     /// Set the parcelable contained in this `ParcelableHolder`.
-    pub fn set_parcelable<T>(&mut self, p: Arc<T>) -> Result<()>
+    pub fn set_parcelable<T>(&mut self, p: Arc<T>) -> Result<(), StatusCode>
     where
         T: Any + Parcelable + ParcelableMetadata + std::fmt::Debug + Send + Sync,
     {
@@ -126,7 +122,7 @@
     /// * `Ok(None)` if the holder is empty or the descriptor does not match
     /// * `Ok(Some(_))` if the object holds a parcelable of type `T`
     ///   with the correct descriptor
-    pub fn get_parcelable<T>(&self) -> Result<Option<Arc<T>>>
+    pub fn get_parcelable<T>(&self) -> Result<Option<Arc<T>>, StatusCode>
     where
         T: Any + Parcelable + ParcelableMetadata + Default + std::fmt::Debug + Send + Sync,
     {
@@ -176,11 +172,28 @@
     }
 }
 
-impl_serialize_for_parcelable!(ParcelableHolder);
-impl_deserialize_for_parcelable!(ParcelableHolder);
+impl Serialize for ParcelableHolder {
+    fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<(), StatusCode> {
+        parcel.write(&NON_NULL_PARCELABLE_FLAG)?;
+        self.write_to_parcel(parcel)
+    }
+}
+
+impl Deserialize for ParcelableHolder {
+    fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self, StatusCode> {
+        let status: i32 = parcel.read()?;
+        if status == NULL_PARCELABLE_FLAG {
+            Err(StatusCode::UNEXPECTED_NULL)
+        } else {
+            let mut parcelable = ParcelableHolder::new(Default::default());
+            parcelable.read_from_parcel(parcel)?;
+            Ok(parcelable)
+        }
+    }
+}
 
 impl Parcelable for ParcelableHolder {
-    fn write_to_parcel(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
+    fn write_to_parcel(&self, parcel: &mut BorrowedParcel<'_>) -> Result<(), StatusCode> {
         parcel.write(&self.stability)?;
 
         let mut data = self.data.lock().unwrap();
@@ -219,7 +232,7 @@
         }
     }
 
-    fn read_from_parcel(&mut self, parcel: &BorrowedParcel<'_>) -> Result<()> {
+    fn read_from_parcel(&mut self, parcel: &BorrowedParcel<'_>) -> Result<(), StatusCode> {
         self.stability = parcel.read()?;
 
         let data_size: i32 = parcel.read()?;
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index 760d862..12bfde7 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -312,17 +312,6 @@
         }
     }
 
-    fn ping_binder(&mut self) -> Result<()> {
-        let status = unsafe {
-            // Safety: `SpIBinder` guarantees that `self` always contains a
-            // valid pointer to an `AIBinder`.
-            //
-            // This call does not affect ownership of its pointer parameter.
-            sys::AIBinder_ping(self.as_native_mut())
-        };
-        status_result(status)
-    }
-
     #[cfg(not(android_vndk))]
     fn set_requesting_sid(&mut self, enable: bool) {
         unsafe { sys::AIBinder_setRequestingSid(self.as_native_mut(), enable) };
@@ -412,6 +401,17 @@
             )
         })
     }
+
+    fn ping_binder(&mut self) -> Result<()> {
+        let status = unsafe {
+            // Safety: `SpIBinder` guarantees that `self` always contains a
+            // valid pointer to an `AIBinder`.
+            //
+            // This call does not affect ownership of its pointer parameter.
+            sys::AIBinder_ping(self.as_native_mut())
+        };
+        status_result(status)
+    }
 }
 
 impl Serialize for SpIBinder {
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index 80dc476..50daf1c 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -17,11 +17,13 @@
 //! Rust Binder crate integration tests
 
 use binder::{declare_binder_enum, declare_binder_interface};
-use binder::parcel::BorrowedParcel;
-use binder::{
-    Binder, BinderFeatures, IBinderInternal, Interface, StatusCode, ThreadState, TransactionCode,
-    FIRST_CALL_TRANSACTION,
+use binder::{BinderFeatures, Interface, StatusCode, ThreadState};
+// Import from internal API for testing only, do not use this module in
+// production.
+use binder::binder_impl::{
+    Binder, BorrowedParcel, IBinderInternal, TransactionCode, FIRST_CALL_TRANSACTION,
 };
+
 use std::convert::{TryFrom, TryInto};
 use std::ffi::CStr;
 use std::fs::File;
@@ -120,7 +122,7 @@
 }
 
 impl Interface for TestService {
-    fn dump(&self, _file: &File, args: &[&CStr]) -> binder::Result<()> {
+    fn dump(&self, _file: &File, args: &[&CStr]) -> Result<(), StatusCode> {
         let mut dump_args = self.dump_args.lock().unwrap();
         dump_args.extend(args.iter().map(|s| s.to_str().unwrap().to_owned()));
         Ok(())
@@ -128,22 +130,22 @@
 }
 
 impl ITest for TestService {
-    fn test(&self) -> binder::Result<String> {
+    fn test(&self) -> Result<String, StatusCode> {
         Ok(self.s.clone())
     }
 
-    fn get_dump_args(&self) -> binder::Result<Vec<String>> {
+    fn get_dump_args(&self) -> Result<Vec<String>, StatusCode> {
         let args = self.dump_args.lock().unwrap().clone();
         Ok(args)
     }
 
-    fn get_selinux_context(&self) -> binder::Result<String> {
+    fn get_selinux_context(&self) -> Result<String, StatusCode> {
         let sid =
             ThreadState::with_calling_sid(|sid| sid.map(|s| s.to_string_lossy().into_owned()));
         sid.ok_or(StatusCode::UNEXPECTED_NULL)
     }
 
-    fn get_is_handling_transaction(&self) -> binder::Result<bool> {
+    fn get_is_handling_transaction(&self) -> Result<bool, StatusCode> {
         Ok(binder::is_handling_transaction())
     }
 }
@@ -151,31 +153,31 @@
 /// Trivial testing binder interface
 pub trait ITest: Interface {
     /// Returns a test string
-    fn test(&self) -> binder::Result<String>;
+    fn test(&self) -> Result<String, StatusCode>;
 
     /// Return the arguments sent via dump
-    fn get_dump_args(&self) -> binder::Result<Vec<String>>;
+    fn get_dump_args(&self) -> Result<Vec<String>, StatusCode>;
 
     /// Returns the caller's SELinux context
-    fn get_selinux_context(&self) -> binder::Result<String>;
+    fn get_selinux_context(&self) -> Result<String, StatusCode>;
 
     /// Returns the value of calling `is_handling_transaction`.
-    fn get_is_handling_transaction(&self) -> binder::Result<bool>;
+    fn get_is_handling_transaction(&self) -> Result<bool, StatusCode>;
 }
 
 /// Async trivial testing binder interface
 pub trait IATest<P>: Interface {
     /// Returns a test string
-    fn test(&self) -> binder::BoxFuture<'static, binder::Result<String>>;
+    fn test(&self) -> binder::BoxFuture<'static, Result<String, StatusCode>>;
 
     /// Return the arguments sent via dump
-    fn get_dump_args(&self) -> binder::BoxFuture<'static, binder::Result<Vec<String>>>;
+    fn get_dump_args(&self) -> binder::BoxFuture<'static, Result<Vec<String>, StatusCode>>;
 
     /// Returns the caller's SELinux context
-    fn get_selinux_context(&self) -> binder::BoxFuture<'static, binder::Result<String>>;
+    fn get_selinux_context(&self) -> binder::BoxFuture<'static, Result<String, StatusCode>>;
 
     /// Returns the value of calling `is_handling_transaction`.
-    fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, binder::Result<bool>>;
+    fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, Result<bool, StatusCode>>;
 }
 
 declare_binder_interface! {
@@ -193,7 +195,7 @@
     code: TransactionCode,
     _data: &BorrowedParcel<'_>,
     reply: &mut BorrowedParcel<'_>,
-) -> binder::Result<()> {
+) -> Result<(), StatusCode> {
     match code.try_into()? {
         TestTransactionCode::Test => reply.write(&service.test()?),
         TestTransactionCode::GetDumpArgs => reply.write(&service.get_dump_args()?),
@@ -203,21 +205,21 @@
 }
 
 impl ITest for BpTest {
-    fn test(&self) -> binder::Result<String> {
+    fn test(&self) -> Result<String, StatusCode> {
         let reply =
             self.binder
                 .transact(TestTransactionCode::Test as TransactionCode, 0, |_| Ok(()))?;
         reply.read()
     }
 
-    fn get_dump_args(&self) -> binder::Result<Vec<String>> {
+    fn get_dump_args(&self) -> Result<Vec<String>, StatusCode> {
         let reply =
             self.binder
                 .transact(TestTransactionCode::GetDumpArgs as TransactionCode, 0, |_| Ok(()))?;
         reply.read()
     }
 
-    fn get_selinux_context(&self) -> binder::Result<String> {
+    fn get_selinux_context(&self) -> Result<String, StatusCode> {
         let reply = self.binder.transact(
             TestTransactionCode::GetSelinuxContext as TransactionCode,
             0,
@@ -226,7 +228,7 @@
         reply.read()
     }
 
-    fn get_is_handling_transaction(&self) -> binder::Result<bool> {
+    fn get_is_handling_transaction(&self) -> Result<bool, StatusCode> {
         let reply = self.binder.transact(
             TestTransactionCode::GetIsHandlingTransaction as TransactionCode,
             0,
@@ -237,7 +239,7 @@
 }
 
 impl<P: binder::BinderAsyncPool> IATest<P> for BpTest {
-    fn test(&self) -> binder::BoxFuture<'static, binder::Result<String>> {
+    fn test(&self) -> binder::BoxFuture<'static, Result<String, StatusCode>> {
         let binder = self.binder.clone();
         P::spawn(
             move || binder.transact(TestTransactionCode::Test as TransactionCode, 0, |_| Ok(())),
@@ -245,7 +247,7 @@
         )
     }
 
-    fn get_dump_args(&self) -> binder::BoxFuture<'static, binder::Result<Vec<String>>> {
+    fn get_dump_args(&self) -> binder::BoxFuture<'static, Result<Vec<String>, StatusCode>> {
         let binder = self.binder.clone();
         P::spawn(
             move || binder.transact(TestTransactionCode::GetDumpArgs as TransactionCode, 0, |_| Ok(())),
@@ -253,7 +255,7 @@
         )
     }
 
-    fn get_selinux_context(&self) -> binder::BoxFuture<'static, binder::Result<String>> {
+    fn get_selinux_context(&self) -> binder::BoxFuture<'static, Result<String, StatusCode>> {
         let binder = self.binder.clone();
         P::spawn(
             move || binder.transact(TestTransactionCode::GetSelinuxContext as TransactionCode, 0, |_| Ok(())),
@@ -261,7 +263,7 @@
         )
     }
 
-    fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, binder::Result<bool>> {
+    fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, Result<bool, StatusCode>> {
         let binder = self.binder.clone();
         P::spawn(
             move || binder.transact(TestTransactionCode::GetIsHandlingTransaction as TransactionCode, 0, |_| Ok(())),
@@ -271,40 +273,40 @@
 }
 
 impl ITest for Binder<BnTest> {
-    fn test(&self) -> binder::Result<String> {
+    fn test(&self) -> Result<String, StatusCode> {
         self.0.test()
     }
 
-    fn get_dump_args(&self) -> binder::Result<Vec<String>> {
+    fn get_dump_args(&self) -> Result<Vec<String>, StatusCode> {
         self.0.get_dump_args()
     }
 
-    fn get_selinux_context(&self) -> binder::Result<String> {
+    fn get_selinux_context(&self) -> Result<String, StatusCode> {
         self.0.get_selinux_context()
     }
 
-    fn get_is_handling_transaction(&self) -> binder::Result<bool> {
+    fn get_is_handling_transaction(&self) -> Result<bool, StatusCode> {
         self.0.get_is_handling_transaction()
     }
 }
 
 impl<P: binder::BinderAsyncPool> IATest<P> for Binder<BnTest> {
-    fn test(&self) -> binder::BoxFuture<'static, binder::Result<String>> {
+    fn test(&self) -> binder::BoxFuture<'static, Result<String, StatusCode>> {
         let res = self.0.test();
         Box::pin(async move { res })
     }
 
-    fn get_dump_args(&self) -> binder::BoxFuture<'static, binder::Result<Vec<String>>> {
+    fn get_dump_args(&self) -> binder::BoxFuture<'static, Result<Vec<String>, StatusCode>> {
         let res = self.0.get_dump_args();
         Box::pin(async move { res })
     }
 
-    fn get_selinux_context(&self) -> binder::BoxFuture<'static, binder::Result<String>> {
+    fn get_selinux_context(&self) -> binder::BoxFuture<'static, Result<String, StatusCode>> {
         let res = self.0.get_selinux_context();
         Box::pin(async move { res })
     }
 
-    fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, binder::Result<bool>> {
+    fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, Result<bool, StatusCode>> {
         let res = self.0.get_is_handling_transaction();
         Box::pin(async move { res })
     }
@@ -325,7 +327,7 @@
     _code: TransactionCode,
     _data: &BorrowedParcel<'_>,
     _reply: &mut BorrowedParcel<'_>,
-) -> binder::Result<()> {
+) -> Result<(), StatusCode> {
     Ok(())
 }
 
@@ -363,9 +365,12 @@
     use std::time::Duration;
 
     use binder::{
-        Binder, BinderFeatures, DeathRecipient, FromIBinder, IBinder, IBinderInternal, Interface,
-        SpIBinder, StatusCode, Strong,
+        BinderFeatures, DeathRecipient, FromIBinder, IBinder, Interface, SpIBinder, StatusCode,
+        Strong,
     };
+    // Import from impl API for testing only, should not be necessary as long as
+    // you are using AIDL.
+    use binder::binder_impl::{Binder, IBinderInternal, TransactionCode};
 
     use binder_tokio::Tokio;
 
@@ -743,8 +748,7 @@
             let _process = ScopedServiceProcess::new(service_name);
 
             let test_client: Strong<dyn ITest> =
-                binder::get_interface(service_name)
-                .expect("Did not get test binder service");
+                binder::get_interface(service_name).expect("Did not get test binder service");
             let mut remote = test_client.as_binder();
             assert!(remote.is_binder_alive());
             remote.ping_binder().expect("Could not ping remote service");
@@ -925,7 +929,7 @@
         let service2 = service2.as_binder();
 
         let parcel = service1.prepare_transact().unwrap();
-        let res = service2.submit_transact(super::TestTransactionCode::Test as binder::TransactionCode, parcel, 0);
+        let res = service2.submit_transact(super::TestTransactionCode::Test as TransactionCode, parcel, 0);
 
         match res {
             Ok(_) => panic!("submit_transact should fail"),
diff --git a/libs/binder/rust/tests/serialization.rs b/libs/binder/rust/tests/serialization.rs
index 1fc761e..b62da7b 100644
--- a/libs/binder/rust/tests/serialization.rs
+++ b/libs/binder/rust/tests/serialization.rs
@@ -18,11 +18,12 @@
 //! access.
 
 use binder::declare_binder_interface;
-use binder::parcel::ParcelFileDescriptor;
 use binder::{
-    Binder, BinderFeatures, BorrowedParcel, ExceptionCode, Interface, Result, SpIBinder, Status,
-    StatusCode, TransactionCode,
+    BinderFeatures, ExceptionCode, Interface, ParcelFileDescriptor, SpIBinder, Status, StatusCode,
 };
+// Import from impl API for testing only, should not be necessary as long as you
+// are using AIDL.
+use binder::binder_impl::{BorrowedParcel, Binder, TransactionCode};
 
 use std::ffi::{c_void, CStr, CString};
 use std::sync::Once;
@@ -113,7 +114,7 @@
     code: TransactionCode,
     parcel: &BorrowedParcel<'_>,
     reply: &mut BorrowedParcel<'_>,
-) -> Result<()> {
+) -> Result<(), StatusCode> {
     match code {
         bindings::Transaction_TEST_BOOL => {
             assert_eq!(parcel.read::<bool>()?, true);
diff --git a/libs/binder/tests/rpc_fuzzer/corpus/transact_on_binder b/libs/binder/tests/rpc_fuzzer/corpus/transact_on_binder
new file mode 100644
index 0000000..ae081e6
--- /dev/null
+++ b/libs/binder/tests/rpc_fuzzer/corpus/transact_on_binder
Binary files differ
diff --git a/libs/cputimeinstate/Android.bp b/libs/cputimeinstate/Android.bp
index 1fd2c62..73f9d4d 100644
--- a/libs/cputimeinstate/Android.bp
+++ b/libs/cputimeinstate/Android.bp
@@ -14,6 +14,7 @@
         "libbase",
         "libbpf_bcc",
         "libbpf_android",
+        "libbpf_minimal",
         "liblog",
         "libnetdutils"
     ],
@@ -33,6 +34,7 @@
         "libbase",
         "libbpf_bcc",
         "libbpf_android",
+        "libbpf_minimal",
         "libtimeinstate",
         "libnetdutils",
     ],
diff --git a/services/audiomanager/Android.bp b/services/audiomanager/Android.bp
index e6fb2c3..d11631b 100644
--- a/services/audiomanager/Android.bp
+++ b/services/audiomanager/Android.bp
@@ -7,7 +7,7 @@
     default_applicable_licenses: ["frameworks_native_license"],
 }
 
-cc_library_shared {
+cc_library {
     name: "libaudiomanager",
 
     srcs: [
diff --git a/services/batteryservice/include/batteryservice/BatteryService.h b/services/batteryservice/include/batteryservice/BatteryService.h
index 1e8eb1e..178bc29 100644
--- a/services/batteryservice/include/batteryservice/BatteryService.h
+++ b/services/batteryservice/include/batteryservice/BatteryService.h
@@ -40,6 +40,7 @@
     bool chargerAcOnline;
     bool chargerUsbOnline;
     bool chargerWirelessOnline;
+    bool chargerDockOnline;
     int maxChargingCurrent;
     int maxChargingVoltage;
     int batteryStatus;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 73b7b63..71cd52d 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -133,42 +133,37 @@
     return true;
 }
 
-float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& layer,
-                                                    const RefreshRate& refreshRate,
-                                                    bool isSeamlessSwitch) const {
-    if (!isVoteAllowed(layer, refreshRate)) {
-        return 0;
-    }
-
-    // Slightly prefer seamless switches.
-    constexpr float kSeamedSwitchPenalty = 0.95f;
-    const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty;
-
-    // If the layer wants Max, give higher score to the higher refresh rate
-    if (layer.vote == LayerVoteType::Max) {
-        const auto ratio =
-                refreshRate.fps.getValue() / mAppRequestRefreshRates.back()->fps.getValue();
-        // use ratio^2 to get a lower score the more we get further from peak
-        return ratio * ratio;
-    }
+float RefreshRateConfigs::calculateNonExactMatchingLayerScoreLocked(
+        const LayerRequirement& layer, const RefreshRate& refreshRate) const {
+    constexpr float kScoreForFractionalPairs = .8f;
 
     const auto displayPeriod = refreshRate.getVsyncPeriod();
     const auto layerPeriod = layer.desiredRefreshRate.getPeriodNsecs();
     if (layer.vote == LayerVoteType::ExplicitDefault) {
         // Find the actual rate the layer will render, assuming
-        // that layerPeriod is the minimal time to render a frame
+        // that layerPeriod is the minimal period to render a frame.
+        // For example if layerPeriod is 20ms and displayPeriod is 16ms,
+        // then the actualLayerPeriod will be 32ms, because it is the
+        // smallest multiple of the display period which is >= layerPeriod.
         auto actualLayerPeriod = displayPeriod;
         int multiplier = 1;
         while (layerPeriod > actualLayerPeriod + MARGIN_FOR_PERIOD_CALCULATION) {
             multiplier++;
             actualLayerPeriod = displayPeriod * multiplier;
         }
+
+        // Because of the threshold we used above it's possible that score is slightly
+        // above 1.
         return std::min(1.0f,
                         static_cast<float>(layerPeriod) / static_cast<float>(actualLayerPeriod));
     }
 
     if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
         layer.vote == LayerVoteType::Heuristic) {
+        if (isFractionalPairOrMultiple(refreshRate.getFps(), layer.desiredRefreshRate)) {
+            return kScoreForFractionalPairs;
+        }
+
         // Calculate how many display vsyncs we need to present a single frame for this
         // layer
         const auto [displayFramesQuotient, displayFramesRemainder] =
@@ -176,7 +171,7 @@
         static constexpr size_t MAX_FRAMES_TO_FIT = 10; // Stop calculating when score < 0.1
         if (displayFramesRemainder == 0) {
             // Layer desired refresh rate matches the display rate.
-            return 1.0f * seamlessness;
+            return 1.0f;
         }
 
         if (displayFramesQuotient == 0) {
@@ -194,7 +189,29 @@
             iter++;
         }
 
-        return (1.0f / iter) * seamlessness;
+        return (1.0f / iter);
+    }
+
+    return 0;
+}
+
+float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& layer,
+                                                    const RefreshRate& refreshRate,
+                                                    bool isSeamlessSwitch) const {
+    if (!isVoteAllowed(layer, refreshRate)) {
+        return 0;
+    }
+
+    // Slightly prefer seamless switches.
+    constexpr float kSeamedSwitchPenalty = 0.95f;
+    const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty;
+
+    // If the layer wants Max, give higher score to the higher refresh rate
+    if (layer.vote == LayerVoteType::Max) {
+        const auto ratio = refreshRate.getFps().getValue() /
+                mAppRequestRefreshRates.back()->getFps().getValue();
+        // use ratio^2 to get a lower score the more we get further from peak
+        return ratio * ratio;
     }
 
     if (layer.vote == LayerVoteType::ExplicitExact) {
@@ -209,7 +226,18 @@
         return divider == 1;
     }
 
-    return 0;
+    // If the layer frame rate is a divider of the refresh rate it should score
+    // the highest score.
+    if (getFrameRateDivider(refreshRate.getFps(), layer.desiredRefreshRate) > 0) {
+        return 1.0f * seamlessness;
+    }
+
+    // The layer frame rate is not a divider of the refresh rate,
+    // there is a small penalty attached to the score to favor the frame rates
+    // the exactly matches the display refresh rate or a multiple.
+    constexpr float kNonExactMatchingPenalty = 0.95f;
+    return calculateNonExactMatchingLayerScoreLocked(layer, refreshRate) * seamlessness *
+            kNonExactMatchingPenalty;
 }
 
 struct RefreshRateScore {
@@ -421,7 +449,7 @@
 
             const auto layerScore =
                     calculateLayerScoreLocked(layer, *scores[i].refreshRate, isSeamlessSwitch);
-            ALOGV("%s gives %s score of %.2f", formatLayerInfo(layer, weight).c_str(),
+            ALOGV("%s gives %s score of %.4f", formatLayerInfo(layer, weight).c_str(),
                   scores[i].refreshRate->getName().c_str(), layerScore);
             scores[i].score += weight * layerScore;
         }
@@ -582,7 +610,7 @@
 
 template <typename Iter>
 const RefreshRate* RefreshRateConfigs::getBestRefreshRate(Iter begin, Iter end) const {
-    constexpr auto EPSILON = 0.001f;
+    constexpr auto kEpsilon = 0.0001f;
     const RefreshRate* bestRefreshRate = begin->refreshRate;
     float max = begin->score;
     for (auto i = begin; i != end; ++i) {
@@ -591,7 +619,7 @@
 
         ATRACE_INT(refreshRate->getName().c_str(), round<int>(score * 100));
 
-        if (score > max * (1 + EPSILON)) {
+        if (score > max * (1 + kEpsilon)) {
             max = score;
             bestRefreshRate = refreshRate;
         }
@@ -924,6 +952,17 @@
     return static_cast<int>(numPeriodsRounded);
 }
 
+bool RefreshRateConfigs::isFractionalPairOrMultiple(Fps smaller, Fps bigger) {
+    if (smaller.getValue() > bigger.getValue()) {
+        return isFractionalPairOrMultiple(bigger, smaller);
+    }
+
+    const auto multiplier = std::round(bigger.getValue() / smaller.getValue());
+    constexpr float kCoef = 1000.f / 1001.f;
+    return bigger.equalsWithMargin(Fps(smaller.getValue() * multiplier / kCoef)) ||
+            bigger.equalsWithMargin(Fps(smaller.getValue() * multiplier * kCoef));
+}
+
 void RefreshRateConfigs::dump(std::string& result) const {
     std::lock_guard lock(mLock);
     base::StringAppendF(&result, "DesiredDisplayModeSpecs (DisplayManager): %s\n\n",
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index dfd1395..913e5ee 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -342,6 +342,10 @@
     // layer refresh rate.
     static int getFrameRateDivider(Fps displayFrameRate, Fps layerFrameRate);
 
+    // Returns if the provided frame rates have a ratio t*1000/1001 or t*1001/1000
+    // for an integer t.
+    static bool isFractionalPairOrMultiple(Fps, Fps);
+
     using UidToFrameRateOverride = std::map<uid_t, Fps>;
     // Returns the frame rate override for each uid.
     //
@@ -405,6 +409,9 @@
     float calculateLayerScoreLocked(const LayerRequirement&, const RefreshRate&,
                                     bool isSeamlessSwitch) const REQUIRES(mLock);
 
+    float calculateNonExactMatchingLayerScoreLocked(const LayerRequirement&,
+                                                    const RefreshRate&) const REQUIRES(mLock);
+
     // The list of refresh rates, indexed by display modes ID. This may change after this
     // object is initialized.
     AllRefreshRatesMapType mRefreshRates GUARDED_BY(mLock);
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 3b2bd81..112fabd 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -99,9 +99,15 @@
     static inline const DisplayModeId HWC_CONFIG_ID_30 = DisplayModeId(4);
     static inline const DisplayModeId HWC_CONFIG_ID_25 = DisplayModeId(5);
     static inline const DisplayModeId HWC_CONFIG_ID_50 = DisplayModeId(6);
+    static inline const DisplayModeId HWC_CONFIG_ID_24 = DisplayModeId(7);
+    static inline const DisplayModeId HWC_CONFIG_ID_24_FRAC = DisplayModeId(8);
+    static inline const DisplayModeId HWC_CONFIG_ID_30_FRAC = DisplayModeId(9);
+    static inline const DisplayModeId HWC_CONFIG_ID_60_FRAC = DisplayModeId(10);
 
     // Test configs
     DisplayModePtr mConfig60 = createDisplayMode(HWC_CONFIG_ID_60, 0, Fps(60.0f).getPeriodNsecs());
+    DisplayModePtr mConfig60Frac =
+            createDisplayMode(HWC_CONFIG_ID_60_FRAC, 0, Fps(59.94f).getPeriodNsecs());
     DisplayModePtr mConfig90 = createDisplayMode(HWC_CONFIG_ID_90, 0, Fps(90.0f).getPeriodNsecs());
     DisplayModePtr mConfig90DifferentGroup =
             createDisplayMode(HWC_CONFIG_ID_90, 1, Fps(90.0f).getPeriodNsecs());
@@ -117,9 +123,15 @@
     DisplayModePtr mConfig30 = createDisplayMode(HWC_CONFIG_ID_30, 0, Fps(30.0f).getPeriodNsecs());
     DisplayModePtr mConfig30DifferentGroup =
             createDisplayMode(HWC_CONFIG_ID_30, 1, Fps(30.0f).getPeriodNsecs());
+    DisplayModePtr mConfig30Frac =
+            createDisplayMode(HWC_CONFIG_ID_30_FRAC, 0, Fps(29.97f).getPeriodNsecs());
+    DisplayModePtr mConfig25 = createDisplayMode(HWC_CONFIG_ID_25, 0, Fps(25.0f).getPeriodNsecs());
     DisplayModePtr mConfig25DifferentGroup =
             createDisplayMode(HWC_CONFIG_ID_25, 1, Fps(25.0f).getPeriodNsecs());
     DisplayModePtr mConfig50 = createDisplayMode(HWC_CONFIG_ID_50, 0, Fps(50.0f).getPeriodNsecs());
+    DisplayModePtr mConfig24 = createDisplayMode(HWC_CONFIG_ID_24, 0, Fps(24.0f).getPeriodNsecs());
+    DisplayModePtr mConfig24Frac =
+            createDisplayMode(HWC_CONFIG_ID_24_FRAC, 0, Fps(23.976f).getPeriodNsecs());
 
     // Test device configurations
     // The positions of the configs in the arrays below MUST match their IDs. For example,
@@ -146,6 +158,11 @@
                                        mConfig50};
     DisplayModes m60_120Device = {mConfig60, mConfig120};
 
+    // This is a typical TV configuration.
+    DisplayModes m24_25_30_50_60WithFracDevice = {mConfig24, mConfig24Frac, mConfig25,
+                                                  mConfig30, mConfig30Frac, mConfig50,
+                                                  mConfig60, mConfig60Frac};
+
     // Expected RefreshRate objects
     RefreshRate mExpected60Config = {HWC_CONFIG_ID_60, mConfig60, Fps(60),
                                      RefreshRate::ConstructorTag(0)};
@@ -166,7 +183,6 @@
     RefreshRate mExpected120Config = {HWC_CONFIG_ID_120, mConfig120, Fps(120),
                                       RefreshRate::ConstructorTag(0)};
 
-private:
     DisplayModePtr createDisplayMode(DisplayModeId modeId, int32_t group, int64_t vsyncPeriod,
                                      ui::Size resolution = ui::Size());
 };
@@ -1237,7 +1253,109 @@
         const auto& refreshRate =
                 refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
         EXPECT_TRUE(refreshRate.getFps().equalsWithMargin(Fps(test.second)))
-                << "Expecting " << test.first << "fps => " << test.second << "Hz";
+                << "Expecting " << test.first << "fps => " << test.second << "Hz"
+                << " but it was " << refreshRate.getFps();
+    }
+}
+
+TEST_F(RefreshRateConfigsTest,
+       getBestRefreshRate_ExplicitExactOrMultiple_WithFractionalRefreshRates) {
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& lr = layers[0];
+
+    // Test that 23.976 will choose 24 if 23.976 is not supported
+    {
+        android::DisplayModes modes = {mConfig24,     mConfig25, mConfig30,
+                                       mConfig30Frac, mConfig60, mConfig60Frac};
+        auto refreshRateConfigs =
+                std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+        lr.vote = LayerVoteType::ExplicitExactOrMultiple;
+        lr.desiredRefreshRate = Fps(23.976f);
+        lr.name = "ExplicitExactOrMultiple 23.976 fps";
+        EXPECT_EQ(HWC_CONFIG_ID_24,
+                  refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+                          .getModeId());
+    }
+
+    // Test that 24 will choose 23.976 if 24 is not supported
+    {
+        android::DisplayModes modes = {mConfig24Frac, mConfig25, mConfig30,
+                                       mConfig30Frac, mConfig60, mConfig60Frac};
+        auto refreshRateConfigs =
+                std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60);
+        lr.desiredRefreshRate = Fps(24.f);
+        lr.name = "ExplicitExactOrMultiple 24 fps";
+        EXPECT_EQ(HWC_CONFIG_ID_24_FRAC,
+                  refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+                          .getModeId());
+    }
+
+    // Test that 29.97 will prefer 59.94 over 60 and 30
+    {
+        android::DisplayModes modes = {mConfig24, mConfig24Frac, mConfig25,
+                                       mConfig30, mConfig60,     mConfig60Frac};
+        auto refreshRateConfigs =
+                std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60);
+        lr.desiredRefreshRate = Fps(29.97f);
+        lr.name = "ExplicitExactOrMultiple 29.97f fps";
+        EXPECT_EQ(HWC_CONFIG_ID_60_FRAC,
+                  refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+                          .getModeId());
+    }
+}
+
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExact_WithFractionalRefreshRates) {
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& lr = layers[0];
+
+    // Test that voting for supported refresh rate will select this refresh rate
+    {
+        auto refreshRateConfigs =
+                std::make_unique<RefreshRateConfigs>(m24_25_30_50_60WithFracDevice,
+                                                     /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+        for (auto desiredRefreshRate : {23.976f, 24.f, 25.f, 29.97f, 30.f, 50.f, 59.94f, 60.f}) {
+            lr.vote = LayerVoteType::ExplicitExact;
+            lr.desiredRefreshRate = Fps(desiredRefreshRate);
+            std::stringstream ss;
+            ss << "ExplicitExact " << desiredRefreshRate << " fps";
+            lr.name = ss.str();
+
+            auto selecteRefreshRate =
+                    refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
+
+            EXPECT_TRUE(selecteRefreshRate.getFps().equalsWithMargin(lr.desiredRefreshRate))
+                    << "Expecting " << lr.desiredRefreshRate << " but it was "
+                    << selecteRefreshRate.getFps();
+        }
+    }
+
+    // Test that 23.976 will choose 24 if 23.976 is not supported
+    {
+        android::DisplayModes modes = {mConfig24,     mConfig25, mConfig30,
+                                       mConfig30Frac, mConfig60, mConfig60Frac};
+        auto refreshRateConfigs =
+                std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60);
+        lr.vote = LayerVoteType::ExplicitExact;
+        lr.desiredRefreshRate = Fps(23.976f);
+        lr.name = "ExplicitExact 23.976 fps";
+        EXPECT_EQ(HWC_CONFIG_ID_24,
+                  refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+                          .getModeId());
+    }
+
+    // Test that 24 will choose 23.976 if 24 is not supported
+    {
+        android::DisplayModes modes = {mConfig24Frac, mConfig25, mConfig30,
+                                       mConfig30Frac, mConfig60, mConfig60Frac};
+        auto refreshRateConfigs =
+                std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60);
+        lr.desiredRefreshRate = Fps(24.f);
+        lr.name = "ExplicitExact 24 fps";
+        EXPECT_EQ(HWC_CONFIG_ID_24_FRAC,
+                  refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+                          .getModeId());
     }
 }
 
@@ -2035,6 +2153,100 @@
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
 }
 
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_FractionalRefreshRates_ExactAndDefault) {
+    RefreshRateConfigs::Config config = {.enableFrameRateOverride = true};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m24_25_30_50_60WithFracDevice,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60, config);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 0.5f},
+                                                LayerRequirement{.weight = 0.5f}};
+    auto& explicitDefaultLayer = layers[0];
+    auto& explicitExactOrMultipleLayer = layers[1];
+
+    explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple;
+    explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple";
+    explicitExactOrMultipleLayer.desiredRefreshRate = Fps(60);
+
+    explicitDefaultLayer.vote = LayerVoteType::ExplicitDefault;
+    explicitDefaultLayer.name = "ExplicitDefault";
+    explicitDefaultLayer.desiredRefreshRate = Fps(59.94f);
+
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+}
+
+// b/190578904
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_deviceWithCloseRefreshRates) {
+    constexpr int kMinRefreshRate = 10;
+    constexpr int kMaxRefreshRate = 240;
+
+    DisplayModes displayModes;
+    for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) {
+        constexpr int32_t kGroup = 0;
+        const auto refreshRate = Fps(static_cast<float>(fps));
+        displayModes.push_back(
+                createDisplayMode(DisplayModeId(fps), kGroup, refreshRate.getPeriodNsecs()));
+    }
+
+    const RefreshRateConfigs::GlobalSignals globalSignals = {.touch = false, .idle = false};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(displayModes,
+                                                 /*currentConfigId=*/displayModes[0]->getId());
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    const auto testRefreshRate = [&](Fps fps, LayerVoteType vote) {
+        layers[0].desiredRefreshRate = fps;
+        layers[0].vote = vote;
+        EXPECT_EQ(fps.getIntValue(),
+                  refreshRateConfigs->getBestRefreshRate(layers, globalSignals)
+                          .getFps()
+                          .getIntValue())
+                << "Failed for " << RefreshRateConfigs::layerVoteTypeString(vote);
+    };
+
+    for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) {
+        const auto refreshRate = Fps(static_cast<float>(fps));
+        testRefreshRate(refreshRate, LayerVoteType::Heuristic);
+        testRefreshRate(refreshRate, LayerVoteType::ExplicitDefault);
+        testRefreshRate(refreshRate, LayerVoteType::ExplicitExactOrMultiple);
+        testRefreshRate(refreshRate, LayerVoteType::ExplicitExact);
+    }
+}
+
+// b/190578904
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_conflictingVotes) {
+    const DisplayModes displayModes = {
+            createDisplayMode(DisplayModeId(0), 0, Fps(43.0f).getPeriodNsecs()),
+            createDisplayMode(DisplayModeId(1), 0, Fps(53.0f).getPeriodNsecs()),
+            createDisplayMode(DisplayModeId(2), 0, Fps(55.0f).getPeriodNsecs()),
+            createDisplayMode(DisplayModeId(3), 0, Fps(60.0f).getPeriodNsecs()),
+    };
+
+    const RefreshRateConfigs::GlobalSignals globalSignals = {.touch = false, .idle = false};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(displayModes,
+                                                 /*currentConfigId=*/displayModes[0]->getId());
+
+    const auto layers = std::vector<LayerRequirement>{
+            LayerRequirement{
+                    .vote = LayerVoteType::ExplicitDefault,
+                    .desiredRefreshRate = Fps(43.0f),
+                    .seamlessness = Seamlessness::SeamedAndSeamless,
+                    .weight = 0.41f,
+            },
+            LayerRequirement{
+                    .vote = LayerVoteType::ExplicitExactOrMultiple,
+                    .desiredRefreshRate = Fps(53.0f),
+                    .seamlessness = Seamlessness::SeamedAndSeamless,
+                    .weight = 0.41f,
+            },
+    };
+
+    EXPECT_EQ(53,
+              refreshRateConfigs->getBestRefreshRate(layers, globalSignals).getFps().getIntValue());
+}
+
 TEST_F(RefreshRateConfigsTest, testComparisonOperator) {
     EXPECT_TRUE(mExpected60Config < mExpected90Config);
     EXPECT_FALSE(mExpected60Config < mExpected60Config);
@@ -2130,6 +2342,33 @@
     EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(Fps(60.f), Fps(59.94f)));
 }
 
+TEST_F(RefreshRateConfigsTest, isFractionalPairOrMultiple) {
+    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(23.976f), Fps(24.f)));
+    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(24.f), Fps(23.976f)));
+
+    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(29.97f), Fps(30.f)));
+    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(30.f), Fps(29.97f)));
+
+    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(59.94f), Fps(60.f)));
+    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(60.f), Fps(59.94f)));
+
+    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(29.97f), Fps(60.f)));
+    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(60.f), Fps(29.97f)));
+
+    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(59.94f), Fps(30.f)));
+    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(30.f), Fps(59.94f)));
+
+    const std::vector<float> refreshRates = {23.976f, 24.f, 25.f, 29.97f, 30.f, 50.f, 59.94f, 60.f};
+    for (auto refreshRate : refreshRates) {
+        EXPECT_FALSE(
+                RefreshRateConfigs::isFractionalPairOrMultiple(Fps(refreshRate), Fps(refreshRate)));
+    }
+
+    EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(24.f), Fps(25.f)));
+    EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(23.978f), Fps(25.f)));
+    EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(29.97f), Fps(59.94f)));
+}
+
 TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_noLayers) {
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/