blob: fe59416a993eaec54973786bf13492224537e5ac [file] [log] [blame]
Stephen Crane2a3c2502020-06-16 17:48:35 -07001/*
2 * Copyright (C) 2020 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//! Rust Binder crate integration tests
18
19use binder::declare_binder_interface;
20use binder::parcel::Parcel;
21use binder::{Binder, IBinder, Interface, SpIBinder, TransactionCode};
22
23/// Name of service runner.
24///
25/// Must match the binary name in Android.bp
26const RUST_SERVICE_BINARY: &str = "rustBinderTestService";
27
28/// Binary to run a test service.
29///
30/// This needs to be in a separate process from the tests, so we spawn this
31/// binary as a child, providing the service name as an argument.
32fn main() -> Result<(), &'static str> {
33 // Ensure that we can handle all transactions on the main thread.
34 binder::ProcessState::set_thread_pool_max_thread_count(0);
35 binder::ProcessState::start_thread_pool();
36
37 let mut args = std::env::args().skip(1);
38 if args.len() < 1 || args.len() > 2 {
39 print_usage();
40 return Err("");
41 }
42 let service_name = args.next().ok_or_else(|| {
43 print_usage();
44 "Missing SERVICE_NAME argument"
45 })?;
46 let extension_name = args.next();
47
48 {
49 let mut service = Binder::new(BnTest(Box::new(TestService {
50 s: service_name.clone(),
51 })));
52 if let Some(extension_name) = extension_name {
53 let extension = BnTest::new_binder(TestService { s: extension_name });
54 service
55 .set_extension(&mut extension.as_binder())
56 .expect("Could not add extension");
57 }
58 binder::add_service(&service_name, service.as_binder())
59 .expect("Could not register service");
60 }
61
62 binder::ProcessState::join_thread_pool();
63 Err("Unexpected exit after join_thread_pool")
64}
65
66fn print_usage() {
67 eprintln!(
68 "Usage: {} SERVICE_NAME [EXTENSION_NAME]",
69 RUST_SERVICE_BINARY
70 );
71 eprintln!(concat!(
72 "Spawn a Binder test service identified by SERVICE_NAME,",
73 " optionally with an extesion named EXTENSION_NAME",
74 ));
75}
76
77#[derive(Clone)]
78struct TestService {
79 s: String,
80}
81
82impl Interface for TestService {}
83
84impl ITest for TestService {
85 fn test(&self) -> binder::Result<String> {
86 Ok(self.s.clone())
87 }
88}
89
90/// Trivial testing binder interface
91pub trait ITest: Interface {
92 /// Returns a test string
93 fn test(&self) -> binder::Result<String>;
94}
95
96declare_binder_interface! {
97 ITest["android.os.ITest"] {
98 native: BnTest(on_transact),
99 proxy: BpTest {
100 x: i32 = 100
101 },
102 }
103}
104
105fn on_transact(
106 service: &dyn ITest,
107 _code: TransactionCode,
108 _data: &Parcel,
109 reply: &mut Parcel,
110) -> binder::Result<()> {
111 reply.write(&service.test()?)?;
112 Ok(())
113}
114
115impl ITest for BpTest {
116 fn test(&self) -> binder::Result<String> {
117 let reply = self
118 .binder
119 .transact(SpIBinder::FIRST_CALL_TRANSACTION, 0, |_| Ok(()))?;
120 reply.read()
121 }
122}
123
124impl ITest for Binder<BnTest> {
125 fn test(&self) -> binder::Result<String> {
126 self.0.test()
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use std::fs::File;
133 use std::process::{Child, Command};
134 use std::sync::atomic::{AtomicBool, Ordering};
135 use std::sync::Arc;
136 use std::thread;
137 use std::time::Duration;
138
139 use binder::{DeathRecipient, FromIBinder, IBinder, SpIBinder, StatusCode};
140
141 use super::{ITest, RUST_SERVICE_BINARY};
142
143 pub struct ScopedServiceProcess(Child);
144
145 impl ScopedServiceProcess {
146 pub fn new(identifier: &str) -> Self {
147 Self::new_internal(identifier, None)
148 }
149
150 pub fn new_with_extension(identifier: &str, extension: &str) -> Self {
151 Self::new_internal(identifier, Some(extension))
152 }
153
154 fn new_internal(identifier: &str, extension: Option<&str>) -> Self {
155 let mut binary_path =
156 std::env::current_exe().expect("Could not retrieve current executable path");
157 binary_path.pop();
158 binary_path.push(RUST_SERVICE_BINARY);
159 let mut command = Command::new(&binary_path);
160 command.arg(identifier);
161 if let Some(ext) = extension {
162 command.arg(ext);
163 }
164 let child = command.spawn().expect("Could not start service");
165 Self(child)
166 }
167 }
168
169 impl Drop for ScopedServiceProcess {
170 fn drop(&mut self) {
171 self.0.kill().expect("Could not kill child process");
172 self.0
173 .wait()
174 .expect("Could not wait for child process to die");
175 }
176 }
177
178 #[test]
179 fn check_services() {
180 let mut sm = binder::get_service("manager").expect("Did not get manager binder service");
181 assert!(sm.is_binder_alive());
182 assert!(sm.ping_binder().is_ok());
183
184 assert!(binder::get_service("this_service_does_not_exist").is_none());
185 assert_eq!(
186 binder::get_interface::<dyn ITest>("this_service_does_not_exist").err(),
187 Some(StatusCode::NAME_NOT_FOUND)
188 );
189
190 // The service manager service isn't an ITest, so this must fail.
191 assert_eq!(
192 binder::get_interface::<dyn ITest>("manager").err(),
193 Some(StatusCode::BAD_TYPE)
194 );
195 }
196
197 #[test]
198 fn trivial_client() {
199 let service_name = "trivial_client_test";
200 let _process = ScopedServiceProcess::new(service_name);
201 let test_client: Box<dyn ITest> =
202 binder::get_interface(service_name).expect("Did not get manager binder service");
203 assert_eq!(test_client.test().unwrap(), "trivial_client_test");
204 }
205
206 fn register_death_notification(binder: &mut SpIBinder) -> (Arc<AtomicBool>, DeathRecipient) {
207 let binder_died = Arc::new(AtomicBool::new(false));
208
209 let mut death_recipient = {
210 let flag = binder_died.clone();
211 DeathRecipient::new(move || {
212 flag.store(true, Ordering::Relaxed);
213 })
214 };
215
216 binder
217 .link_to_death(&mut death_recipient)
218 .expect("link_to_death failed");
219
220 (binder_died, death_recipient)
221 }
222
223 /// Killing a remote service should unregister the service and trigger
224 /// death notifications.
225 #[test]
226 fn test_death_notifications() {
227 binder::ProcessState::start_thread_pool();
228
229 let service_name = "test_death_notifications";
230 let service_process = ScopedServiceProcess::new(service_name);
231 let mut remote = binder::get_service(service_name).expect("Could not retrieve service");
232
233 let (binder_died, _recipient) = register_death_notification(&mut remote);
234
235 drop(service_process);
236 remote
237 .ping_binder()
238 .expect_err("Service should have died already");
239
240 // Pause to ensure any death notifications get delivered
241 thread::sleep(Duration::from_secs(1));
242
243 assert!(
244 binder_died.load(Ordering::Relaxed),
245 "Did not receive death notification"
246 );
247 }
248
249 /// Test unregistering death notifications.
250 #[test]
251 fn test_unregister_death_notifications() {
252 binder::ProcessState::start_thread_pool();
253
254 let service_name = "test_unregister_death_notifications";
255 let service_process = ScopedServiceProcess::new(service_name);
256 let mut remote = binder::get_service(service_name).expect("Could not retrieve service");
257
258 let (binder_died, mut recipient) = register_death_notification(&mut remote);
259
260 remote
261 .unlink_to_death(&mut recipient)
262 .expect("Could not unlink death notifications");
263
264 drop(service_process);
265 remote
266 .ping_binder()
267 .expect_err("Service should have died already");
268
269 // Pause to ensure any death notifications get delivered
270 thread::sleep(Duration::from_secs(1));
271
272 assert!(
273 !binder_died.load(Ordering::Relaxed),
274 "Received unexpected death notification after unlinking",
275 );
276 }
277
278 /// Dropping a remote handle should unregister any death notifications.
279 #[test]
280 fn test_death_notification_registration_lifetime() {
281 binder::ProcessState::start_thread_pool();
282
283 let service_name = "test_death_notification_registration_lifetime";
284 let service_process = ScopedServiceProcess::new(service_name);
285 let mut remote = binder::get_service(service_name).expect("Could not retrieve service");
286
287 let (binder_died, _recipient) = register_death_notification(&mut remote);
288
289 // This should automatically unregister our death notification.
290 drop(remote);
291
292 drop(service_process);
293
294 // Pause to ensure any death notifications get delivered
295 thread::sleep(Duration::from_secs(1));
296
297 // We dropped the remote handle, so we should not receive the death
298 // notification when the remote process dies here.
299 assert!(
300 !binder_died.load(Ordering::Relaxed),
301 "Received unexpected death notification after dropping remote handle"
302 );
303 }
304
305 /// Test IBinder interface methods not exercised elsewhere.
306 #[test]
307 fn test_misc_ibinder() {
308 let service_name = "rust_test_ibinder";
309
310 {
311 let _process = ScopedServiceProcess::new(service_name);
312
313 let mut remote = binder::get_service(service_name);
314 assert!(remote.is_binder_alive());
315 remote.ping_binder().expect("Could not ping remote service");
316
317 // We're not testing the output of dump here, as that's really a
318 // property of the C++ implementation. There is the risk that the
319 // method just does nothing, but we don't want to depend on any
320 // particular output from the underlying library.
321 let null_out = File::open("/dev/null").expect("Could not open /dev/null");
322 remote
323 .dump(&null_out, &[])
324 .expect("Could not dump remote service");
325 }
326
327 // get/set_extensions is tested in test_extensions()
328
329 // transact is tested everywhere else, and we can't make raw
330 // transactions outside the [FIRST_CALL_TRANSACTION,
331 // LAST_CALL_TRANSACTION] range from the NDK anyway.
332
333 // link_to_death is tested in test_*_death_notification* tests.
334 }
335
336 #[test]
337 fn test_extensions() {
338 let service_name = "rust_test_extensions";
339 let extension_name = "rust_test_extensions_ext";
340
341 {
342 let _process = ScopedServiceProcess::new(service_name);
343
344 let mut remote = binder::get_service(service_name);
345 assert!(remote.is_binder_alive());
346
347 let extension = remote
348 .get_extension()
349 .expect("Could not check for an extension");
350 assert!(extension.is_none());
351 }
352
353 {
354 let _process = ScopedServiceProcess::new_with_extension(service_name, extension_name);
355
356 let mut remote = binder::get_service(service_name);
357 assert!(remote.is_binder_alive());
358
359 let maybe_extension = remote
360 .get_extension()
361 .expect("Could not check for an extension");
362
363 let extension = maybe_extension.expect("Remote binder did not have an extension");
364
365 let extension: Box<dyn ITest> = FromIBinder::try_from(extension)
366 .expect("Extension could not be converted to the expected interface");
367
368 assert_eq!(extension.test().unwrap(), extension_name);
369 }
370 }
371}