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