/* * 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::parcel::ParcelFileDescriptor; use binder::{ Binder, BinderFeatures, ExceptionCode, Interface, Parcel, Result, SpIBinder, Status, StatusCode, TransactionCode, }; use std::ffi::{c_void, CStr, CString}; use std::sync::Once; #[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_ONCE: Once = Once::new(); static mut SERVICE: Option = None; /// 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 { unsafe { SERVICE_ONCE.call_once(|| { SERVICE = Some(BnReadParcelTest::new_binder((), BinderFeatures::default()).as_binder()); }); SERVICE.as_ref().unwrap().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: &Parcel, reply: &mut Parcel, ) -> Result<()> { match code { bindings::Transaction_TEST_BOOL => { assert_eq!(parcel.read::()?, true); assert_eq!(parcel.read::()?, false); assert_eq!(parcel.read::>()?, unsafe { bindings::TESTDATA_BOOL }); assert_eq!(parcel.read::>>()?, None); reply.write(&true)?; reply.write(&false)?; 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_value()); assert_eq!(parcel.read::>()?, unsafe { bindings::TESTDATA_I8 }); assert_eq!(parcel.read::>()?, unsafe { bindings::TESTDATA_U8 }); assert_eq!(parcel.read::>>()?, None); reply.write(&0i8)?; reply.write(&1i8)?; reply.write(&i8::max_value())?; reply.write(&unsafe { bindings::TESTDATA_I8 }[..])?; 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_value()); assert_eq!(parcel.read::>()?, unsafe { bindings::TESTDATA_CHARS }); assert_eq!(parcel.read::>>()?, None); reply.write(&0u16)?; reply.write(&1u16)?; reply.write(&u16::max_value())?; 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_value()); assert_eq!(parcel.read::>()?, unsafe { bindings::TESTDATA_I32 }); assert_eq!(parcel.read::>>()?, None); reply.write(&0i32)?; reply.write(&1i32)?; reply.write(&i32::max_value())?; 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_value()); assert_eq!(parcel.read::>()?, unsafe { bindings::TESTDATA_I64 }); assert_eq!(parcel.read::>>()?, None); reply.write(&0i64)?; reply.write(&1i64)?; reply.write(&i64::max_value())?; 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_value()); assert_eq!(parcel.read::>()?, unsafe { bindings::TESTDATA_U64 }); assert_eq!(parcel.read::>>()?, None); reply.write(&0u64)?; reply.write(&1u64)?; reply.write(&u64::max_value())?; 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()); assert_eq!(floats[1..], unsafe { bindings::TESTDATA_FLOAT }[1..]); assert_eq!(parcel.read::>>()?, None); reply.write(&0f32)?; 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()); assert_eq!(doubles[1..], unsafe { bindings::TESTDATA_DOUBLE }[1..]); assert_eq!(parcel.read::>>()?, None); reply.write(&0f64)?; 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()?; for (s, expected) in s .unwrap() .iter() .zip(unsafe { bindings::TESTDATA_STRS }.iter()) { let expected = 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); 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 = unsafe { SERVICE .as_ref() .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(()) }