1 /*
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
19 use binder::declare_binder_interface;
20 use binder::parcel::Parcel;
21 use binder::{
22 Binder, BinderFeatures, IBinderInternal, Interface, StatusCode, ThreadState, TransactionCode,
23 FIRST_CALL_TRANSACTION,
24 };
25 use std::convert::{TryFrom, TryInto};
26
27 /// Name of service runner.
28 ///
29 /// Must match the binary name in Android.bp
30 const RUST_SERVICE_BINARY: &str = "rustBinderTestService";
31
32 /// Binary to run a test service.
33 ///
34 /// This needs to be in a separate process from the tests, so we spawn this
35 /// binary as a child, providing the service name as an argument.
main() -> Result<(), &'static str>36 fn main() -> Result<(), &'static str> {
37 // Ensure that we can handle all transactions on the main thread.
38 binder::ProcessState::set_thread_pool_max_thread_count(0);
39 binder::ProcessState::start_thread_pool();
40
41 let mut args = std::env::args().skip(1);
42 if args.len() < 1 || args.len() > 2 {
43 print_usage();
44 return Err("");
45 }
46 let service_name = args.next().ok_or_else(|| {
47 print_usage();
48 "Missing SERVICE_NAME argument"
49 })?;
50 let extension_name = args.next();
51
52 {
53 let mut service = Binder::new(BnTest(Box::new(TestService {
54 s: service_name.clone(),
55 })));
56 service.set_requesting_sid(true);
57 if let Some(extension_name) = extension_name {
58 let extension =
59 BnTest::new_binder(TestService { s: extension_name }, BinderFeatures::default());
60 service
61 .set_extension(&mut extension.as_binder())
62 .expect("Could not add extension");
63 }
64 binder::add_service(&service_name, service.as_binder())
65 .expect("Could not register service");
66 }
67
68 binder::ProcessState::join_thread_pool();
69 Err("Unexpected exit after join_thread_pool")
70 }
71
print_usage()72 fn print_usage() {
73 eprintln!(
74 "Usage: {} SERVICE_NAME [EXTENSION_NAME]",
75 RUST_SERVICE_BINARY
76 );
77 eprintln!(concat!(
78 "Spawn a Binder test service identified by SERVICE_NAME,",
79 " optionally with an extesion named EXTENSION_NAME",
80 ));
81 }
82
83 #[derive(Clone)]
84 struct TestService {
85 s: String,
86 }
87
88 #[repr(u32)]
89 enum TestTransactionCode {
90 Test = FIRST_CALL_TRANSACTION,
91 GetSelinuxContext,
92 }
93
94 impl TryFrom<u32> for TestTransactionCode {
95 type Error = StatusCode;
96
try_from(c: u32) -> Result<Self, Self::Error>97 fn try_from(c: u32) -> Result<Self, Self::Error> {
98 match c {
99 _ if c == TestTransactionCode::Test as u32 => Ok(TestTransactionCode::Test),
100 _ if c == TestTransactionCode::GetSelinuxContext as u32 => {
101 Ok(TestTransactionCode::GetSelinuxContext)
102 }
103 _ => Err(StatusCode::UNKNOWN_TRANSACTION),
104 }
105 }
106 }
107
108 impl Interface for TestService {}
109
110 impl ITest for TestService {
test(&self) -> binder::Result<String>111 fn test(&self) -> binder::Result<String> {
112 Ok(self.s.clone())
113 }
114
get_selinux_context(&self) -> binder::Result<String>115 fn get_selinux_context(&self) -> binder::Result<String> {
116 let sid =
117 ThreadState::with_calling_sid(|sid| sid.map(|s| s.to_string_lossy().into_owned()));
118 sid.ok_or(StatusCode::UNEXPECTED_NULL)
119 }
120 }
121
122 /// Trivial testing binder interface
123 pub trait ITest: Interface {
124 /// Returns a test string
test(&self) -> binder::Result<String>125 fn test(&self) -> binder::Result<String>;
126
127 /// Returns the caller's SELinux context
get_selinux_context(&self) -> binder::Result<String>128 fn get_selinux_context(&self) -> binder::Result<String>;
129 }
130
131 declare_binder_interface! {
132 ITest["android.os.ITest"] {
133 native: BnTest(on_transact),
134 proxy: BpTest {
135 x: i32 = 100
136 },
137 }
138 }
139
on_transact( service: &dyn ITest, code: TransactionCode, _data: &Parcel, reply: &mut Parcel, ) -> binder::Result<()>140 fn on_transact(
141 service: &dyn ITest,
142 code: TransactionCode,
143 _data: &Parcel,
144 reply: &mut Parcel,
145 ) -> binder::Result<()> {
146 match code.try_into()? {
147 TestTransactionCode::Test => reply.write(&service.test()?),
148 TestTransactionCode::GetSelinuxContext => reply.write(&service.get_selinux_context()?),
149 }
150 }
151
152 impl ITest for BpTest {
test(&self) -> binder::Result<String>153 fn test(&self) -> binder::Result<String> {
154 let reply =
155 self.binder
156 .transact(TestTransactionCode::Test as TransactionCode, 0, |_| Ok(()))?;
157 reply.read()
158 }
159
get_selinux_context(&self) -> binder::Result<String>160 fn get_selinux_context(&self) -> binder::Result<String> {
161 let reply = self.binder.transact(
162 TestTransactionCode::GetSelinuxContext as TransactionCode,
163 0,
164 |_| Ok(()),
165 )?;
166 reply.read()
167 }
168 }
169
170 impl ITest for Binder<BnTest> {
test(&self) -> binder::Result<String>171 fn test(&self) -> binder::Result<String> {
172 self.0.test()
173 }
174
get_selinux_context(&self) -> binder::Result<String>175 fn get_selinux_context(&self) -> binder::Result<String> {
176 self.0.get_selinux_context()
177 }
178 }
179
180 /// Trivial testing binder interface
181 pub trait ITestSameDescriptor: Interface {}
182
183 declare_binder_interface! {
184 ITestSameDescriptor["android.os.ITest"] {
185 native: BnTestSameDescriptor(on_transact_same_descriptor),
186 proxy: BpTestSameDescriptor,
187 }
188 }
189
on_transact_same_descriptor( _service: &dyn ITestSameDescriptor, _code: TransactionCode, _data: &Parcel, _reply: &mut Parcel, ) -> binder::Result<()>190 fn on_transact_same_descriptor(
191 _service: &dyn ITestSameDescriptor,
192 _code: TransactionCode,
193 _data: &Parcel,
194 _reply: &mut Parcel,
195 ) -> binder::Result<()> {
196 Ok(())
197 }
198
199 impl ITestSameDescriptor for BpTestSameDescriptor {}
200
201 impl ITestSameDescriptor for Binder<BnTestSameDescriptor> {}
202
203 #[cfg(test)]
204 mod tests {
205 use selinux_bindgen as selinux_sys;
206 use std::ffi::CStr;
207 use std::fs::File;
208 use std::process::{Child, Command};
209 use std::ptr;
210 use std::sync::atomic::{AtomicBool, Ordering};
211 use std::sync::Arc;
212 use std::thread;
213 use std::time::Duration;
214
215 use binder::{
216 Binder, BinderFeatures, DeathRecipient, FromIBinder, IBinder, IBinderInternal, Interface,
217 SpIBinder, StatusCode, Strong,
218 };
219
220 use super::{BnTest, ITest, ITestSameDescriptor, TestService, RUST_SERVICE_BINARY};
221
222 pub struct ScopedServiceProcess(Child);
223
224 impl ScopedServiceProcess {
new(identifier: &str) -> Self225 pub fn new(identifier: &str) -> Self {
226 Self::new_internal(identifier, None)
227 }
228
new_with_extension(identifier: &str, extension: &str) -> Self229 pub fn new_with_extension(identifier: &str, extension: &str) -> Self {
230 Self::new_internal(identifier, Some(extension))
231 }
232
new_internal(identifier: &str, extension: Option<&str>) -> Self233 fn new_internal(identifier: &str, extension: Option<&str>) -> Self {
234 let mut binary_path =
235 std::env::current_exe().expect("Could not retrieve current executable path");
236 binary_path.pop();
237 binary_path.push(RUST_SERVICE_BINARY);
238 let mut command = Command::new(&binary_path);
239 command.arg(identifier);
240 if let Some(ext) = extension {
241 command.arg(ext);
242 }
243 let child = command.spawn().expect("Could not start service");
244 Self(child)
245 }
246 }
247
248 impl Drop for ScopedServiceProcess {
drop(&mut self)249 fn drop(&mut self) {
250 self.0.kill().expect("Could not kill child process");
251 self.0
252 .wait()
253 .expect("Could not wait for child process to die");
254 }
255 }
256
257 #[test]
check_services()258 fn check_services() {
259 let mut sm = binder::get_service("manager").expect("Did not get manager binder service");
260 assert!(sm.is_binder_alive());
261 assert!(sm.ping_binder().is_ok());
262
263 assert!(binder::get_service("this_service_does_not_exist").is_none());
264 assert_eq!(
265 binder::get_interface::<dyn ITest>("this_service_does_not_exist").err(),
266 Some(StatusCode::NAME_NOT_FOUND)
267 );
268
269 // The service manager service isn't an ITest, so this must fail.
270 assert_eq!(
271 binder::get_interface::<dyn ITest>("manager").err(),
272 Some(StatusCode::BAD_TYPE)
273 );
274 }
275
276 #[test]
trivial_client()277 fn trivial_client() {
278 let service_name = "trivial_client_test";
279 let _process = ScopedServiceProcess::new(service_name);
280 let test_client: Strong<dyn ITest> =
281 binder::get_interface(service_name).expect("Did not get manager binder service");
282 assert_eq!(test_client.test().unwrap(), "trivial_client_test");
283 }
284
285 #[test]
get_selinux_context()286 fn get_selinux_context() {
287 let service_name = "get_selinux_context";
288 let _process = ScopedServiceProcess::new(service_name);
289 let test_client: Strong<dyn ITest> =
290 binder::get_interface(service_name).expect("Did not get manager binder service");
291 let expected_context = unsafe {
292 let mut out_ptr = ptr::null_mut();
293 assert_eq!(selinux_sys::getcon(&mut out_ptr), 0);
294 assert!(!out_ptr.is_null());
295 CStr::from_ptr(out_ptr)
296 };
297 assert_eq!(
298 test_client.get_selinux_context().unwrap(),
299 expected_context
300 .to_str()
301 .expect("context was invalid UTF-8"),
302 );
303 }
304
register_death_notification(binder: &mut SpIBinder) -> (Arc<AtomicBool>, DeathRecipient)305 fn register_death_notification(binder: &mut SpIBinder) -> (Arc<AtomicBool>, DeathRecipient) {
306 let binder_died = Arc::new(AtomicBool::new(false));
307
308 let mut death_recipient = {
309 let flag = binder_died.clone();
310 DeathRecipient::new(move || {
311 flag.store(true, Ordering::Relaxed);
312 })
313 };
314
315 binder
316 .link_to_death(&mut death_recipient)
317 .expect("link_to_death failed");
318
319 (binder_died, death_recipient)
320 }
321
322 /// Killing a remote service should unregister the service and trigger
323 /// death notifications.
324 #[test]
test_death_notifications()325 fn test_death_notifications() {
326 binder::ProcessState::start_thread_pool();
327
328 let service_name = "test_death_notifications";
329 let service_process = ScopedServiceProcess::new(service_name);
330 let mut remote = binder::get_service(service_name).expect("Could not retrieve service");
331
332 let (binder_died, _recipient) = register_death_notification(&mut remote);
333
334 drop(service_process);
335 remote
336 .ping_binder()
337 .expect_err("Service should have died already");
338
339 // Pause to ensure any death notifications get delivered
340 thread::sleep(Duration::from_secs(1));
341
342 assert!(
343 binder_died.load(Ordering::Relaxed),
344 "Did not receive death notification"
345 );
346 }
347
348 /// Test unregistering death notifications.
349 #[test]
test_unregister_death_notifications()350 fn test_unregister_death_notifications() {
351 binder::ProcessState::start_thread_pool();
352
353 let service_name = "test_unregister_death_notifications";
354 let service_process = ScopedServiceProcess::new(service_name);
355 let mut remote = binder::get_service(service_name).expect("Could not retrieve service");
356
357 let (binder_died, mut recipient) = register_death_notification(&mut remote);
358
359 remote
360 .unlink_to_death(&mut recipient)
361 .expect("Could not unlink death notifications");
362
363 drop(service_process);
364 remote
365 .ping_binder()
366 .expect_err("Service should have died already");
367
368 // Pause to ensure any death notifications get delivered
369 thread::sleep(Duration::from_secs(1));
370
371 assert!(
372 !binder_died.load(Ordering::Relaxed),
373 "Received unexpected death notification after unlinking",
374 );
375 }
376
377 /// Dropping a remote handle should unregister any death notifications.
378 #[test]
test_death_notification_registration_lifetime()379 fn test_death_notification_registration_lifetime() {
380 binder::ProcessState::start_thread_pool();
381
382 let service_name = "test_death_notification_registration_lifetime";
383 let service_process = ScopedServiceProcess::new(service_name);
384 let mut remote = binder::get_service(service_name).expect("Could not retrieve service");
385
386 let (binder_died, _recipient) = register_death_notification(&mut remote);
387
388 // This should automatically unregister our death notification.
389 drop(remote);
390
391 drop(service_process);
392
393 // Pause to ensure any death notifications get delivered
394 thread::sleep(Duration::from_secs(1));
395
396 // We dropped the remote handle, so we should not receive the death
397 // notification when the remote process dies here.
398 assert!(
399 !binder_died.load(Ordering::Relaxed),
400 "Received unexpected death notification after dropping remote handle"
401 );
402 }
403
404 /// Test IBinder interface methods not exercised elsewhere.
405 #[test]
test_misc_ibinder()406 fn test_misc_ibinder() {
407 let service_name = "rust_test_ibinder";
408
409 {
410 let _process = ScopedServiceProcess::new(service_name);
411
412 let mut remote = binder::get_service(service_name);
413 assert!(remote.is_binder_alive());
414 remote.ping_binder().expect("Could not ping remote service");
415
416 // We're not testing the output of dump here, as that's really a
417 // property of the C++ implementation. There is the risk that the
418 // method just does nothing, but we don't want to depend on any
419 // particular output from the underlying library.
420 let null_out = File::open("/dev/null").expect("Could not open /dev/null");
421 remote
422 .dump(&null_out, &[])
423 .expect("Could not dump remote service");
424 }
425
426 // get/set_extensions is tested in test_extensions()
427
428 // transact is tested everywhere else, and we can't make raw
429 // transactions outside the [FIRST_CALL_TRANSACTION,
430 // LAST_CALL_TRANSACTION] range from the NDK anyway.
431
432 // link_to_death is tested in test_*_death_notification* tests.
433 }
434
435 #[test]
test_extensions()436 fn test_extensions() {
437 let service_name = "rust_test_extensions";
438 let extension_name = "rust_test_extensions_ext";
439
440 {
441 let _process = ScopedServiceProcess::new(service_name);
442
443 let mut remote = binder::get_service(service_name);
444 assert!(remote.is_binder_alive());
445
446 let extension = remote
447 .get_extension()
448 .expect("Could not check for an extension");
449 assert!(extension.is_none());
450 }
451
452 {
453 let _process = ScopedServiceProcess::new_with_extension(service_name, extension_name);
454
455 let mut remote = binder::get_service(service_name);
456 assert!(remote.is_binder_alive());
457
458 let maybe_extension = remote
459 .get_extension()
460 .expect("Could not check for an extension");
461
462 let extension = maybe_extension.expect("Remote binder did not have an extension");
463
464 let extension: Strong<dyn ITest> = FromIBinder::try_from(extension)
465 .expect("Extension could not be converted to the expected interface");
466
467 assert_eq!(extension.test().unwrap(), extension_name);
468 }
469 }
470
471 /// Test re-associating a local binder object with a different class.
472 ///
473 /// This is needed because different binder service (e.g. NDK vs Rust)
474 /// implementations are incompatible and must not be interchanged. A local
475 /// service with the same descriptor string but a different class pointer
476 /// may have been created by an NDK service and is therefore incompatible
477 /// with the Rust service implementation. It must be treated as remote and
478 /// all API calls parceled and sent through transactions.
479 ///
480 /// Further tests of this behavior with the C NDK and Rust API are in
481 /// rust_ndk_interop.rs
482 #[test]
associate_existing_class()483 fn associate_existing_class() {
484 let service = Binder::new(BnTest(Box::new(TestService {
485 s: "testing_service".to_string(),
486 })));
487
488 // This should succeed although we will have to treat the service as
489 // remote.
490 let _interface: Strong<dyn ITestSameDescriptor> =
491 FromIBinder::try_from(service.as_binder())
492 .expect("Could not re-interpret service as the ITestSameDescriptor interface");
493 }
494
495 /// Test that we can round-trip a rust service through a generic IBinder
496 #[test]
reassociate_rust_binder()497 fn reassociate_rust_binder() {
498 let service_name = "testing_service";
499 let service_ibinder = BnTest::new_binder(
500 TestService {
501 s: service_name.to_string(),
502 },
503 BinderFeatures::default(),
504 )
505 .as_binder();
506
507 let service: Strong<dyn ITest> = service_ibinder
508 .into_interface()
509 .expect("Could not reassociate the generic ibinder");
510
511 assert_eq!(service.test().unwrap(), service_name);
512 }
513
514 #[test]
weak_binder_upgrade()515 fn weak_binder_upgrade() {
516 let service_name = "testing_service";
517 let service = BnTest::new_binder(
518 TestService {
519 s: service_name.to_string(),
520 },
521 BinderFeatures::default(),
522 );
523
524 let weak = Strong::downgrade(&service);
525
526 let upgraded = weak.upgrade().expect("Could not upgrade weak binder");
527
528 assert_eq!(service, upgraded);
529 }
530
531 #[test]
weak_binder_upgrade_dead()532 fn weak_binder_upgrade_dead() {
533 let service_name = "testing_service";
534 let weak = {
535 let service = BnTest::new_binder(
536 TestService {
537 s: service_name.to_string(),
538 },
539 BinderFeatures::default(),
540 );
541
542 Strong::downgrade(&service)
543 };
544
545 assert_eq!(weak.upgrade(), Err(StatusCode::DEAD_OBJECT));
546 }
547
548 #[test]
weak_binder_clone()549 fn weak_binder_clone() {
550 let service_name = "testing_service";
551 let service = BnTest::new_binder(
552 TestService {
553 s: service_name.to_string(),
554 },
555 BinderFeatures::default(),
556 );
557
558 let weak = Strong::downgrade(&service);
559 let cloned = weak.clone();
560 assert_eq!(weak, cloned);
561
562 let upgraded = weak.upgrade().expect("Could not upgrade weak binder");
563 let clone_upgraded = cloned.upgrade().expect("Could not upgrade weak binder");
564
565 assert_eq!(service, upgraded);
566 assert_eq!(service, clone_upgraded);
567 }
568
569 #[test]
570 #[allow(clippy::eq_op)]
binder_ord()571 fn binder_ord() {
572 let service1 = BnTest::new_binder(
573 TestService {
574 s: "testing_service1".to_string(),
575 },
576 BinderFeatures::default(),
577 );
578 let service2 = BnTest::new_binder(
579 TestService {
580 s: "testing_service2".to_string(),
581 },
582 BinderFeatures::default(),
583 );
584
585 assert!(!(service1 < service1));
586 assert!(!(service1 > service1));
587 assert_eq!(service1 < service2, !(service2 < service1));
588 }
589 }
590