/* * Copyright (C) 2020 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. */ //! Included as a module in the binder crate internal tests for internal API //! access. use binder::declare_binder_interface; use binder::{ BinderFeatures, ExceptionCode, Interface, ParcelFileDescriptor, SpIBinder, Status, StatusCode, }; // Import from impl API for testing only, should not be necessary as long as you // are using AIDL. use binder::binder_impl::{Binder, BorrowedParcel, TransactionCode}; use std::ffi::{c_void, CStr, CString}; use std::sync::OnceLock; #[allow( non_camel_case_types, non_snake_case, non_upper_case_globals, unused, improper_ctypes, missing_docs, clippy::all )] mod bindings { include!(concat!(env!("OUT_DIR"), "/bindings.rs")); } macro_rules! assert_eq { ($left:expr, $right:expr $(,)?) => { match (&$left, &$right) { (left, right) => { if *left != *right { eprintln!( "assertion failed: `{:?}` == `{:?}`, {}:{}:{}", &*left, &*right, file!(), line!(), column!() ); return Err(StatusCode::FAILED_TRANSACTION); } } } }; } macro_rules! assert { ($expr:expr) => { if !$expr { eprintln!("assertion failed: `{:?}`, {}:{}:{}", $expr, file!(), line!(), column!()); return Err(StatusCode::FAILED_TRANSACTION); } }; } static SERVICE: OnceLock = OnceLock::new(); /// Start binder service and return a raw AIBinder pointer to it. /// /// Safe to call multiple times, only creates the service once. #[no_mangle] pub extern "C" fn rust_service() -> *mut c_void { let service = SERVICE .get_or_init(|| BnReadParcelTest::new_binder((), BinderFeatures::default()).as_binder()); // SAFETY: The SpIBinder will remain alive as long as the program is running because it is in // the static SERVICE, so the pointer is valid forever. unsafe { service.as_raw().cast() } } /// Empty interface just to use the declare_binder_interface macro pub trait ReadParcelTest: Interface {} declare_binder_interface! { ReadParcelTest["read_parcel_test"] { native: BnReadParcelTest(on_transact), proxy: BpReadParcelTest, } } impl ReadParcelTest for Binder {} impl ReadParcelTest for BpReadParcelTest {} impl ReadParcelTest for () {} #[allow(clippy::float_cmp)] fn on_transact( _service: &dyn ReadParcelTest, code: TransactionCode, parcel: &BorrowedParcel<'_>, reply: &mut BorrowedParcel<'_>, ) -> Result<(), StatusCode> { match code { bindings::Transaction_TEST_BOOL => { assert!(parcel.read::()?); assert!(!parcel.read::()?); // SAFETY: Just reading an extern constant. assert_eq!(parcel.read::>()?, unsafe { bindings::TESTDATA_BOOL }); assert_eq!(parcel.read::>>()?, None); reply.write(&true)?; reply.write(&false)?; // SAFETY: Just reading an extern constant. reply.write(&unsafe { bindings::TESTDATA_BOOL }[..])?; reply.write(&(None as Option>))?; } bindings::Transaction_TEST_BYTE => { assert_eq!(parcel.read::()?, 0); assert_eq!(parcel.read::()?, 1); assert_eq!(parcel.read::()?, i8::MAX); // SAFETY: Just reading an extern constant. assert_eq!(parcel.read::>()?, unsafe { bindings::TESTDATA_I8 }); // SAFETY: Just reading an extern constant. assert_eq!(parcel.read::>()?, unsafe { bindings::TESTDATA_U8 }); assert_eq!(parcel.read::>>()?, None); reply.write(&0i8)?; reply.write(&1i8)?; reply.write(&i8::MAX)?; // SAFETY: Just reading an extern constant. reply.write(&unsafe { bindings::TESTDATA_I8 }[..])?; // SAFETY: Just reading an extern constant. reply.write(&unsafe { bindings::TESTDATA_U8 }[..])?; reply.write(&(None as Option>))?; } bindings::Transaction_TEST_U16 => { assert_eq!(parcel.read::()?, 0); assert_eq!(parcel.read::()?, 1); assert_eq!(parcel.read::()?, u16::MAX); // SAFETY: Just reading an extern constant. assert_eq!(parcel.read::>()?, unsafe { bindings::TESTDATA_CHARS }); assert_eq!(parcel.read::>>()?, None); reply.write(&0u16)?; reply.write(&1u16)?; reply.write(&u16::MAX)?; // SAFETY: Just reading an extern constant. reply.write(&unsafe { bindings::TESTDATA_CHARS }[..])?; reply.write(&(None as Option>))?; } bindings::Transaction_TEST_I32 => { assert_eq!(parcel.read::()?, 0); assert_eq!(parcel.read::()?, 1); assert_eq!(parcel.read::()?, i32::MAX); // SAFETY: Just reading an extern constant. assert_eq!(parcel.read::>()?, unsafe { bindings::TESTDATA_I32 }); assert_eq!(parcel.read::>>()?, None); reply.write(&0i32)?; reply.write(&1i32)?; reply.write(&i32::MAX)?; // SAFETY: Just reading an extern constant. reply.write(&unsafe { bindings::TESTDATA_I32 }[..])?; reply.write(&(None as Option>))?; } bindings::Transaction_TEST_I64 => { assert_eq!(parcel.read::()?, 0); assert_eq!(parcel.read::()?, 1); assert_eq!(parcel.read::()?, i64::MAX); // SAFETY: Just reading an extern constant. assert_eq!(parcel.read::>()?, unsafe { bindings::TESTDATA_I64 }); assert_eq!(parcel.read::>>()?, None); reply.write(&0i64)?; reply.write(&1i64)?; reply.write(&i64::MAX)?; // SAFETY: Just reading an extern constant. reply.write(&unsafe { bindings::TESTDATA_I64 }[..])?; reply.write(&(None as Option>))?; } bindings::Transaction_TEST_U64 => { assert_eq!(parcel.read::()?, 0); assert_eq!(parcel.read::()?, 1); assert_eq!(parcel.read::()?, u64::MAX); // SAFETY: Just reading an extern constant. assert_eq!(parcel.read::>()?, unsafe { bindings::TESTDATA_U64 }); assert_eq!(parcel.read::>>()?, None); reply.write(&0u64)?; reply.write(&1u64)?; reply.write(&u64::MAX)?; // SAFETY: Just reading an extern constant. reply.write(&unsafe { bindings::TESTDATA_U64 }[..])?; reply.write(&(None as Option>))?; } bindings::Transaction_TEST_F32 => { assert_eq!(parcel.read::()?, 0f32); let floats = parcel.read::>()?; assert!(floats[0].is_nan()); // SAFETY: Just reading an extern constant. assert_eq!(floats[1..], unsafe { bindings::TESTDATA_FLOAT }[1..]); assert_eq!(parcel.read::>>()?, None); reply.write(&0f32)?; // SAFETY: Just reading an extern constant. reply.write(&unsafe { bindings::TESTDATA_FLOAT }[..])?; reply.write(&(None as Option>))?; } bindings::Transaction_TEST_F64 => { assert_eq!(parcel.read::()?, 0f64); let doubles = parcel.read::>()?; assert!(doubles[0].is_nan()); // SAFETY: Just reading an extern constant. assert_eq!(doubles[1..], unsafe { bindings::TESTDATA_DOUBLE }[1..]); assert_eq!(parcel.read::>>()?, None); reply.write(&0f64)?; // SAFETY: Just reading an extern constant. reply.write(&unsafe { bindings::TESTDATA_DOUBLE }[..])?; reply.write(&(None as Option>))?; } bindings::Transaction_TEST_STRING => { let s: Option = parcel.read()?; assert_eq!(s.as_deref(), Some("testing")); let s: Option = parcel.read()?; assert_eq!(s, None); let s: Option>> = parcel.read()?; // SAFETY: Just reading an extern constant. for (s, expected) in s.unwrap().iter().zip(unsafe { bindings::TESTDATA_STRS }.iter()) { let expected = // SAFETY: Just reading an extern constant. unsafe { expected.as_ref().and_then(|e| CStr::from_ptr(e).to_str().ok()) }; assert_eq!(s.as_deref(), expected); } let s: Option>> = parcel.read()?; assert_eq!(s, None); // SAFETY: Just reading an extern constant. let strings: Vec> = unsafe { bindings::TESTDATA_STRS .iter() .map(|s| { s.as_ref().map(|s| { CStr::from_ptr(s).to_str().expect("String was not UTF-8").to_owned() }) }) .collect() }; reply.write("testing")?; reply.write(&(None as Option))?; reply.write(&strings)?; reply.write(&(None as Option>))?; } bindings::Transaction_TEST_FILE_DESCRIPTOR => { let file1 = parcel.read::()?; let file2 = parcel.read::()?; let files = parcel.read::>>()?; reply.write(&file1)?; reply.write(&file2)?; reply.write(&files)?; } bindings::Transaction_TEST_IBINDER => { assert!(parcel.read::>()?.is_some()); assert!(parcel.read::>()?.is_none()); let ibinders = parcel.read::>>>()?.unwrap(); assert_eq!(ibinders.len(), 2); assert!(ibinders[0].is_some()); assert!(ibinders[1].is_none()); assert!(parcel.read::>>>()?.is_none()); let service = SERVICE.get().expect("Global binder service not initialized").clone(); reply.write(&service)?; reply.write(&(None as Option<&SpIBinder>))?; reply.write(&[Some(&service), None][..])?; reply.write(&(None as Option>>))?; } bindings::Transaction_TEST_STATUS => { let status: Status = parcel.read()?; assert!(status.is_ok()); let status: Status = parcel.read()?; assert_eq!(status.exception_code(), ExceptionCode::NULL_POINTER); assert_eq!(status.get_description(), "Status(-4, EX_NULL_POINTER): 'a status message'"); let status: Status = parcel.read()?; assert_eq!(status.service_specific_error(), 42); assert_eq!( status.get_description(), "Status(-8, EX_SERVICE_SPECIFIC): '42: a service-specific error'" ); reply.write(&Status::ok())?; reply.write(&Status::new_exception( ExceptionCode::NULL_POINTER, Some(&CString::new("a status message").unwrap()), ))?; reply.write(&Status::new_service_specific_error( 42, Some(&CString::new("a service-specific error").unwrap()), ))?; } bindings::Transaction_TEST_FAIL => { assert!(false); } _ => return Err(StatusCode::UNKNOWN_TRANSACTION), } assert_eq!(parcel.read::(), Err(StatusCode::NOT_ENOUGH_DATA)); Ok(()) }