blob: 47dcdc2bc3e7190ea686a00bffe0d351f1394a94 [file] [log] [blame]
/*
* 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 crate lets you use the Tokio `spawn_blocking` pool with AIDL in async
//! Rust code.
//!
//! This crate works by defining a type [`Tokio`], which you can use as the
//! generic parameter in the async version of the trait generated by the AIDL
//! compiler.
//! ```text
//! use binder_tokio::Tokio;
//!
//! binder::get_interface::<dyn SomeAsyncInterface<Tokio>>("...").
//! ```
//!
//! [`Tokio`]: crate::Tokio
use binder::public_api::{BinderAsyncPool, BoxFuture, Strong};
use binder::{FromIBinder, StatusCode, BinderAsyncRuntime};
use std::future::Future;
/// 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)
}).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::FAILED_TRANSACTION),
Err(_) => Err(StatusCode::UNKNOWN_ERROR),
}
}
/// 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)
}).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::FAILED_TRANSACTION),
Err(_) => Err(StatusCode::UNKNOWN_ERROR),
}
}
/// 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>,
{
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()),
}
})
}
}
}
/// 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)
}
}