binder_tokio: only use threadpool for new transactions

The binder kernel driver will detect whether a binder transaction is
started from a thread that is already handling a transaction, and if-so,
use some special logic to avoid deadlocks. This CL makes the async Rust
wrapper check whether we are handling a transaction before offloading
transactions to the `spawn_blocking` thread pool. This avoids breaking
the deadlock prevention in the kernel.

Test: m
Change-Id: If2707c464a4043c80bd893314113b65ef4633baf
diff --git a/libs/binder/rust/binder_tokio/lib.rs b/libs/binder/rust/binder_tokio/lib.rs
index 64833b6..91047be 100644
--- a/libs/binder/rust/binder_tokio/lib.rs
+++ b/libs/binder/rust/binder_tokio/lib.rs
@@ -35,6 +35,11 @@
 /// Retrieve an existing service for a particular interface, sleeping for a few
 /// seconds if it doesn't yet exist.
 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);
+    }
+
     let name = name.to_string();
     let res = tokio::task::spawn_blocking(move || {
         binder::public_api::get_interface::<T>(&name)
@@ -54,6 +59,11 @@
 /// Retrieve an existing service for a particular interface, or start it if it
 /// is configured as a dynamic service and isn't yet started.
 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);
+    }
+
     let name = name.to_string();
     let res = tokio::task::spawn_blocking(move || {
         binder::public_api::wait_for_interface::<T>(&name)
@@ -86,18 +96,27 @@
         B: Send + 'a,
         E: From<crate::StatusCode>,
     {
-        let handle = tokio::task::spawn_blocking(spawn_me);
-        Box::pin(async move {
-            // The `is_panic` branch is not actually reachable in Android as we compile
-            // with `panic = abort`.
-            match handle.await {
-                Ok(res) => after_spawn(res).await,
-                Err(e) if e.is_panic() => std::panic::resume_unwind(e.into_panic()),
-                Err(e) if e.is_cancelled() => Err(StatusCode::FAILED_TRANSACTION.into()),
-                Err(_) => Err(StatusCode::UNKNOWN_ERROR.into()),
-            }
-        })
+        if binder::is_handling_transaction() {
+            // We are currently on the thread pool for a binder server, so we should execute the
+            // transaction on the current thread so that the binder kernel driver is able to apply
+            // its deadlock prevention strategy to the sub-call.
+            //
+            // This shouldn't cause issues with blocking the thread as only one task will run in a
+            // call to `block_on`, so there aren't other tasks to block.
+            let result = spawn_me();
+            Box::pin(after_spawn(result))
+        } else {
+            let handle = tokio::task::spawn_blocking(spawn_me);
+            Box::pin(async move {
+                // The `is_panic` branch is not actually reachable in Android as we compile
+                // with `panic = abort`.
+                match handle.await {
+                    Ok(res) => after_spawn(res).await,
+                    Err(e) if e.is_panic() => std::panic::resume_unwind(e.into_panic()),
+                    Err(e) if e.is_cancelled() => Err(StatusCode::FAILED_TRANSACTION.into()),
+                    Err(_) => Err(StatusCode::UNKNOWN_ERROR.into()),
+                }
+            })
+        }
     }
 }
-
-
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index cce55c0..b94dfa1 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -114,7 +114,7 @@
 };
 pub use crate::binder_async::{BoxFuture, BinderAsyncPool};
 pub use error::{status_t, ExceptionCode, Result, Status, StatusCode};
-pub use native::{add_service, force_lazy_services_persist, register_lazy_service, Binder};
+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};
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index e183ea3..b7c7ae4 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -517,3 +517,12 @@
 }
 
 impl Interface for () {}
+
+/// Determine whether the current thread is currently executing an incoming
+/// transaction.
+pub fn is_handling_transaction() -> bool {
+    unsafe {
+        // Safety: This method is always safe to call.
+        sys::AIBinder_isHandlingTransaction()
+    }
+}