1 /*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 //! This crate lets you use the Tokio `spawn_blocking` pool with AIDL in async
18 //! Rust code.
19 //!
20 //! This crate works by defining a type [`Tokio`], which you can use as the
21 //! generic parameter in the async version of the trait generated by the AIDL
22 //! compiler.
23 //! ```text
24 //! use binder_tokio::Tokio;
25 //!
26 //! binder::get_interface::<dyn SomeAsyncInterface<Tokio>>("...").
27 //! ```
28 //!
29 //! [`Tokio`]: crate::Tokio
30
31 use binder::{BinderAsyncPool, BoxFuture, FromIBinder, StatusCode, Strong};
32 use binder::binder_impl::BinderAsyncRuntime;
33 use std::future::Future;
34
35 /// Retrieve an existing service for a particular interface, sleeping for a few
36 /// seconds if it doesn't yet exist.
get_interface<T: FromIBinder + ?Sized + 'static>(name: &str) -> Result<Strong<T>, StatusCode>37 pub async fn get_interface<T: FromIBinder + ?Sized + 'static>(name: &str) -> Result<Strong<T>, StatusCode> {
38 if binder::is_handling_transaction() {
39 // See comment in the BinderAsyncPool impl.
40 return binder::get_interface::<T>(name);
41 }
42
43 let name = name.to_string();
44 let res = tokio::task::spawn_blocking(move || {
45 binder::get_interface::<T>(&name)
46 }).await;
47
48 // The `is_panic` branch is not actually reachable in Android as we compile
49 // with `panic = abort`.
50 match res {
51 Ok(Ok(service)) => Ok(service),
52 Ok(Err(err)) => Err(err),
53 Err(e) if e.is_panic() => std::panic::resume_unwind(e.into_panic()),
54 Err(e) if e.is_cancelled() => Err(StatusCode::FAILED_TRANSACTION),
55 Err(_) => Err(StatusCode::UNKNOWN_ERROR),
56 }
57 }
58
59 /// Retrieve an existing service for a particular interface, or start it if it
60 /// is configured as a dynamic service and isn't yet started.
wait_for_interface<T: FromIBinder + ?Sized + 'static>(name: &str) -> Result<Strong<T>, StatusCode>61 pub async fn wait_for_interface<T: FromIBinder + ?Sized + 'static>(name: &str) -> Result<Strong<T>, StatusCode> {
62 if binder::is_handling_transaction() {
63 // See comment in the BinderAsyncPool impl.
64 return binder::wait_for_interface::<T>(name);
65 }
66
67 let name = name.to_string();
68 let res = tokio::task::spawn_blocking(move || {
69 binder::wait_for_interface::<T>(&name)
70 }).await;
71
72 // The `is_panic` branch is not actually reachable in Android as we compile
73 // with `panic = abort`.
74 match res {
75 Ok(Ok(service)) => Ok(service),
76 Ok(Err(err)) => Err(err),
77 Err(e) if e.is_panic() => std::panic::resume_unwind(e.into_panic()),
78 Err(e) if e.is_cancelled() => Err(StatusCode::FAILED_TRANSACTION),
79 Err(_) => Err(StatusCode::UNKNOWN_ERROR),
80 }
81 }
82
83 /// Use the Tokio `spawn_blocking` pool with AIDL.
84 pub enum Tokio {}
85
86 impl BinderAsyncPool for Tokio {
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>,87 fn spawn<'a, F1, F2, Fut, A, B, E>(spawn_me: F1, after_spawn: F2) -> BoxFuture<'a, Result<B, E>>
88 where
89 F1: FnOnce() -> A,
90 F2: FnOnce(A) -> Fut,
91 Fut: Future<Output = Result<B, E>>,
92 F1: Send + 'static,
93 F2: Send + 'a,
94 Fut: Send + 'a,
95 A: Send + 'static,
96 B: Send + 'a,
97 E: From<crate::StatusCode>,
98 {
99 if binder::is_handling_transaction() {
100 // We are currently on the thread pool for a binder server, so we should execute the
101 // transaction on the current thread so that the binder kernel driver is able to apply
102 // its deadlock prevention strategy to the sub-call.
103 //
104 // This shouldn't cause issues with blocking the thread as only one task will run in a
105 // call to `block_on`, so there aren't other tasks to block.
106 let result = spawn_me();
107 Box::pin(after_spawn(result))
108 } else {
109 let handle = tokio::task::spawn_blocking(spawn_me);
110 Box::pin(async move {
111 // The `is_panic` branch is not actually reachable in Android as we compile
112 // with `panic = abort`.
113 match handle.await {
114 Ok(res) => after_spawn(res).await,
115 Err(e) if e.is_panic() => std::panic::resume_unwind(e.into_panic()),
116 Err(e) if e.is_cancelled() => Err(StatusCode::FAILED_TRANSACTION.into()),
117 Err(_) => Err(StatusCode::UNKNOWN_ERROR.into()),
118 }
119 })
120 }
121 }
122 }
123
124 /// Wrapper around Tokio runtime types for providing a runtime to a binder server.
125 pub struct TokioRuntime<R>(pub R);
126
127 impl BinderAsyncRuntime for TokioRuntime<tokio::runtime::Runtime> {
block_on<F: Future>(&self, future: F) -> F::Output128 fn block_on<F: Future>(&self, future: F) -> F::Output {
129 self.0.block_on(future)
130 }
131 }
132
133 impl BinderAsyncRuntime for TokioRuntime<std::sync::Arc<tokio::runtime::Runtime>> {
block_on<F: Future>(&self, future: F) -> F::Output134 fn block_on<F: Future>(&self, future: F) -> F::Output {
135 self.0.block_on(future)
136 }
137 }
138
139 impl BinderAsyncRuntime for TokioRuntime<tokio::runtime::Handle> {
block_on<F: Future>(&self, future: F) -> F::Output140 fn block_on<F: Future>(&self, future: F) -> F::Output {
141 self.0.block_on(future)
142 }
143 }
144