blob: f6bdf5c9edc736332ca4ad16e1f1f2a046cd3c81 [file] [log] [blame]
/*
* 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::{BorrowedParcel, Binder, 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<SpIBinder> = 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<BnReadParcelTest> {}
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::<bool>()?);
assert!(!parcel.read::<bool>()?);
assert_eq!(parcel.read::<Vec<bool>>()?, unsafe {
bindings::TESTDATA_BOOL
});
assert_eq!(parcel.read::<Option<Vec<bool>>>()?, None);
reply.write(&true)?;
reply.write(&false)?;
reply.write(&unsafe { bindings::TESTDATA_BOOL }[..])?;
reply.write(&(None as Option<Vec<bool>>))?;
}
bindings::Transaction_TEST_BYTE => {
assert_eq!(parcel.read::<i8>()?, 0);
assert_eq!(parcel.read::<i8>()?, 1);
assert_eq!(parcel.read::<i8>()?, i8::max_value());
assert_eq!(parcel.read::<Vec<i8>>()?, unsafe { bindings::TESTDATA_I8 });
assert_eq!(parcel.read::<Vec<u8>>()?, unsafe { bindings::TESTDATA_U8 });
assert_eq!(parcel.read::<Option<Vec<i8>>>()?, 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<Vec<i8>>))?;
}
bindings::Transaction_TEST_U16 => {
assert_eq!(parcel.read::<u16>()?, 0);
assert_eq!(parcel.read::<u16>()?, 1);
assert_eq!(parcel.read::<u16>()?, u16::max_value());
assert_eq!(parcel.read::<Vec<u16>>()?, unsafe {
bindings::TESTDATA_CHARS
});
assert_eq!(parcel.read::<Option<Vec<u16>>>()?, None);
reply.write(&0u16)?;
reply.write(&1u16)?;
reply.write(&u16::max_value())?;
reply.write(&unsafe { bindings::TESTDATA_CHARS }[..])?;
reply.write(&(None as Option<Vec<u16>>))?;
}
bindings::Transaction_TEST_I32 => {
assert_eq!(parcel.read::<i32>()?, 0);
assert_eq!(parcel.read::<i32>()?, 1);
assert_eq!(parcel.read::<i32>()?, i32::max_value());
assert_eq!(parcel.read::<Vec<i32>>()?, unsafe {
bindings::TESTDATA_I32
});
assert_eq!(parcel.read::<Option<Vec<i32>>>()?, None);
reply.write(&0i32)?;
reply.write(&1i32)?;
reply.write(&i32::max_value())?;
reply.write(&unsafe { bindings::TESTDATA_I32 }[..])?;
reply.write(&(None as Option<Vec<i32>>))?;
}
bindings::Transaction_TEST_I64 => {
assert_eq!(parcel.read::<i64>()?, 0);
assert_eq!(parcel.read::<i64>()?, 1);
assert_eq!(parcel.read::<i64>()?, i64::max_value());
assert_eq!(parcel.read::<Vec<i64>>()?, unsafe {
bindings::TESTDATA_I64
});
assert_eq!(parcel.read::<Option<Vec<i64>>>()?, None);
reply.write(&0i64)?;
reply.write(&1i64)?;
reply.write(&i64::max_value())?;
reply.write(&unsafe { bindings::TESTDATA_I64 }[..])?;
reply.write(&(None as Option<Vec<i64>>))?;
}
bindings::Transaction_TEST_U64 => {
assert_eq!(parcel.read::<u64>()?, 0);
assert_eq!(parcel.read::<u64>()?, 1);
assert_eq!(parcel.read::<u64>()?, u64::max_value());
assert_eq!(parcel.read::<Vec<u64>>()?, unsafe {
bindings::TESTDATA_U64
});
assert_eq!(parcel.read::<Option<Vec<u64>>>()?, None);
reply.write(&0u64)?;
reply.write(&1u64)?;
reply.write(&u64::max_value())?;
reply.write(&unsafe { bindings::TESTDATA_U64 }[..])?;
reply.write(&(None as Option<Vec<u64>>))?;
}
bindings::Transaction_TEST_F32 => {
assert_eq!(parcel.read::<f32>()?, 0f32);
let floats = parcel.read::<Vec<f32>>()?;
assert!(floats[0].is_nan());
assert_eq!(floats[1..], unsafe { bindings::TESTDATA_FLOAT }[1..]);
assert_eq!(parcel.read::<Option<Vec<f32>>>()?, None);
reply.write(&0f32)?;
reply.write(&unsafe { bindings::TESTDATA_FLOAT }[..])?;
reply.write(&(None as Option<Vec<f32>>))?;
}
bindings::Transaction_TEST_F64 => {
assert_eq!(parcel.read::<f64>()?, 0f64);
let doubles = parcel.read::<Vec<f64>>()?;
assert!(doubles[0].is_nan());
assert_eq!(doubles[1..], unsafe { bindings::TESTDATA_DOUBLE }[1..]);
assert_eq!(parcel.read::<Option<Vec<f64>>>()?, None);
reply.write(&0f64)?;
reply.write(&unsafe { bindings::TESTDATA_DOUBLE }[..])?;
reply.write(&(None as Option<Vec<f64>>))?;
}
bindings::Transaction_TEST_STRING => {
let s: Option<String> = parcel.read()?;
assert_eq!(s.as_deref(), Some("testing"));
let s: Option<String> = parcel.read()?;
assert_eq!(s, None);
let s: Option<Vec<Option<String>>> = 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<Vec<Option<String>>> = parcel.read()?;
assert_eq!(s, None);
let strings: Vec<Option<String>> = 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<String>))?;
reply.write(&strings)?;
reply.write(&(None as Option<Vec<String>>))?;
}
bindings::Transaction_TEST_FILE_DESCRIPTOR => {
let file1 = parcel.read::<ParcelFileDescriptor>()?;
let file2 = parcel.read::<ParcelFileDescriptor>()?;
let files = parcel.read::<Vec<Option<ParcelFileDescriptor>>>()?;
reply.write(&file1)?;
reply.write(&file2)?;
reply.write(&files)?;
}
bindings::Transaction_TEST_IBINDER => {
assert!(parcel.read::<Option<SpIBinder>>()?.is_some());
assert!(parcel.read::<Option<SpIBinder>>()?.is_none());
let ibinders = parcel.read::<Option<Vec<Option<SpIBinder>>>>()?.unwrap();
assert_eq!(ibinders.len(), 2);
assert!(ibinders[0].is_some());
assert!(ibinders[1].is_none());
assert!(parcel.read::<Option<Vec<Option<SpIBinder>>>>()?.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<Vec<Option<&SpIBinder>>>))?;
}
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::<i32>(), Err(StatusCode::NOT_ENOUGH_DATA));
Ok(())
}