rsbinder 0.10.0

Pure-Rust Binder IPC for Linux, Android, and macOS — kernel binder plus a libbinder-compatible RPC (binder-over-socket) transport.
Documentation
// Copyright 2022 Jeff Kim <hiking90@gmail.com>
// SPDX-License-Identifier: Apache-2.0

/*
 * 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.
 */

//! This module lets you use the Tokio `spawn_blocking` pool with AIDL in async
//! Rust code (enabled by the default `tokio` feature).
//!
//! It works by defining a type [`Tokio`], which you use as the generic
//! parameter `P` of the `IFooAsync<P>` trait generated by the AIDL compiler.
//! Obtain an async proxy either with the crate-root [`get_interface`] helper,
//! or by upgrading a sync handle with [`Strong::into_async`]:
//!
//! ```text
//! use rsbinder::Tokio;
//!
//! // Look up a service and cast straight to its async interface.
//! let svc = rsbinder::get_interface::<dyn SomeAsyncInterface<Tokio>>("name").await?;
//!
//! // Or upgrade an existing sync Strong handle:
//! let svc_async = sync_handle.into_async::<Tokio>();
//! ```
//!
//! [`Tokio`]: crate::Tokio
//! [`get_interface`]: crate::get_interface
//! [`Strong::into_async`]: crate::Strong::into_async

use crate::{hub, BinderAsyncPool, BinderAsyncRuntime, BoxFuture, FromIBinder, StatusCode, Strong};
use std::future::Future;

/// Retrieve an existing service for a particular interface — a single
/// `getService` wire call on Android 11+ (see [`crate::hub::get_interface`]).
/// For an event-driven wait use [`crate::hub::wait_for_interface`]; for a
/// non-blocking lookup use [`crate::hub::check_interface`].
#[allow(deprecated)] // wraps the deprecated single-shot hub::get_interface
pub async fn get_interface<T: FromIBinder + ?Sized + 'static>(
    name: &str,
) -> Result<Strong<T>, StatusCode> {
    if crate::is_handling_transaction() {
        // See comment in the BinderAsyncPool impl.
        return hub::get_interface::<T>(name);
    }

    let name = name.to_string();
    let res = tokio::task::spawn_blocking(move || hub::get_interface::<T>(&name)).await;

    // The `is_panic` branch is not actually reachable in Android as we compile
    // with `panic = abort`.
    match res {
        Ok(Ok(service)) => Ok(service),
        Ok(Err(err)) => Err(err),
        Err(e) if e.is_panic() => std::panic::resume_unwind(e.into_panic()),
        Err(e) if e.is_cancelled() => Err(StatusCode::FailedTransaction),
        Err(_) => Err(StatusCode::Unknown),
    }
}

// /// 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 rsbinder::is_handling_transaction() {
//         // See comment in the BinderAsyncPool impl.
//         return rsbinder::wait_for_interface::<T>(name);
//     }

//     let name = name.to_string();
//     let res = tokio::task::spawn_blocking(move || rsbinder::wait_for_interface::<T>(&name)).await;

//     // The `is_panic` branch is not actually reachable in Android as we compile
//     // with `panic = abort`.
//     match res {
//         Ok(Ok(service)) => Ok(service),
//         Ok(Err(err)) => Err(err),
//         Err(e) if e.is_panic() => std::panic::resume_unwind(e.into_panic()),
//         Err(e) if e.is_cancelled() => Err(StatusCode::FailedTransaction),
//         Err(_) => Err(StatusCode::Unknown),
//     }
// }

/// Use the Tokio `spawn_blocking` pool with AIDL.
pub enum Tokio {}

impl BinderAsyncPool for Tokio {
    fn spawn<'a, F1, F2, Fut, A, B, E>(spawn_me: F1, after_spawn: F2) -> BoxFuture<'a, Result<B, E>>
    where
        F1: FnOnce() -> A,
        F2: FnOnce(A) -> Fut,
        Fut: Future<Output = Result<B, E>>,
        F1: Send + 'static,
        F2: Send + 'a,
        Fut: Send + 'a,
        A: Send + 'static,
        B: Send + 'a,
        E: From<crate::StatusCode>,
    {
        // `is_handling_transaction()` forces the `THREAD_STATE`
        // thread-local, whose ctor eagerly pulls `ProcessState::as_self()`
        // — that panics in a pure RPC process (this same `Tokio` pool
        // also drives the generated async `Bp*` over the RPC stack,
        // which never brings up kernel binder). With no kernel
        // `ProcessState` there is by definition no in-flight *kernel*
        // binder transaction, so the answer is `false` and we take the
        // `spawn_blocking` arm (RPC nested-callback re-entrancy is
        // handled separately by the session's `DRIVING` marker, not
        // here). Short-circuit keeps every kernel scenario — where
        // `ProcessState` is always initialized — bit-for-bit unchanged.
        if crate::ProcessState::is_initialized() && crate::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::FailedTransaction.into()),
                    Err(_) => Err(StatusCode::Unknown.into()),
                }
            })
        }
    }
}

/// Wrapper around Tokio runtime types for providing a runtime to a binder server.
pub struct TokioRuntime<R>(pub R);

impl BinderAsyncRuntime for TokioRuntime<tokio::runtime::Runtime> {
    fn block_on<F: Future>(&self, future: F) -> F::Output {
        self.0.block_on(future)
    }
}

impl BinderAsyncRuntime for TokioRuntime<std::sync::Arc<tokio::runtime::Runtime>> {
    fn block_on<F: Future>(&self, future: F) -> F::Output {
        self.0.block_on(future)
    }
}

impl BinderAsyncRuntime for TokioRuntime<tokio::runtime::Handle> {
    fn block_on<F: Future>(&self, future: F) -> F::Output {
        self.0.block_on(future)
    }
}