rsbinder 0.11.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_async`] 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_async::<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_async`]: crate::get_interface_async
//! [`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 — one
/// `getService` wire call, which does not block (see
/// [`crate::hub::try_get_interface`]). For an event-driven wait use
/// [`crate::hub::wait_for_interface`]; for a lookup that will not start an
/// unregistered lazy service, [`crate::hub::check_interface`].
///
/// The `_async` suffix is what tells the two apart at the crate root: the
/// synchronous lookups live in [`crate::hub`], and only this one has to be
/// awaited. It matches [`crate::connect_async`], the facade entry point.
pub async fn get_interface_async<T: FromIBinder + ?Sized + 'static>(
    name: &str,
) -> Result<Strong<T>, StatusCode> {
    fn lookup<T: FromIBinder + ?Sized + 'static>(name: &str) -> Result<Strong<T>, StatusCode> {
        hub::try_get_interface::<T>(name)?.ok_or(StatusCode::NameNotFound)
    }

    if crate::is_handling_transaction() {
        // See comment in the BinderAsyncPool impl.
        return lookup::<T>(name);
    }

    let name = name.to_string();
    let res = tokio::task::spawn_blocking(move || lookup::<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>,
    {
        // RPC too: a nested call must stay on this thread or it loses the thread-local `DRIVING` pin and deadlocks.
        if 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)
    }
}