blob: a902e967392e01e8d814f21794c5008618b6a0b2 [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::{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<SpIBinder> = 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<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>()?);
// SAFETY: Just reading an extern constant.
assert_eq!(parcel.read::<Vec<bool>>()?, unsafe { bindings::TESTDATA_BOOL });
assert_eq!(parcel.read::<Option<Vec<bool>>>()?, None);
reply.write(&true)?;
reply.write(&false)?;
// SAFETY: Just reading an extern constant.
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);
// SAFETY: Just reading an extern constant.
assert_eq!(parcel.read::<Vec<i8>>()?, unsafe { bindings::TESTDATA_I8 });
// SAFETY: Just reading an extern constant.
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)?;
// 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<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);
// SAFETY: Just reading an extern constant.
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)?;
// SAFETY: Just reading an extern constant.
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);
// SAFETY: Just reading an extern constant.
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)?;
// SAFETY: Just reading an extern constant.
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);
// SAFETY: Just reading an extern constant.
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)?;
// SAFETY: Just reading an extern constant.
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);
// SAFETY: Just reading an extern constant.
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)?;
// SAFETY: Just reading an extern constant.
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());
// SAFETY: Just reading an extern constant.
assert_eq!(floats[1..], unsafe { bindings::TESTDATA_FLOAT }[1..]);
assert_eq!(parcel.read::<Option<Vec<f32>>>()?, None);
reply.write(&0f32)?;
// SAFETY: Just reading an extern constant.
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());
// SAFETY: Just reading an extern constant.
assert_eq!(doubles[1..], unsafe { bindings::TESTDATA_DOUBLE }[1..]);
assert_eq!(parcel.read::<Option<Vec<f64>>>()?, None);
reply.write(&0f64)?;
// SAFETY: Just reading an extern constant.
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()?;
// 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<Vec<Option<String>>> = parcel.read()?;
assert_eq!(s, None);
// SAFETY: Just reading an extern constant.
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 = 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<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(())
}