android-binder 0.2.1

Safe bindings to Android Binder, restricted to the NDK
/*
 * 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.

extern crate alloc;

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_2021, $right:expr_2021 $(,)?) => {
        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_2021) => {
        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.
#[unsafe(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 start = parcel.get_data_position();

            let file1 = parcel.read::<ParcelFileDescriptor>()?;
            let file2 = parcel.read::<ParcelFileDescriptor>()?;
            // We expect a vector of two non-null ParcelFileDescriptors.
            // Since we're not using Vec<Option<T>> if there are null values
            // read should return an error.
            let files = parcel.read::<Vec<ParcelFileDescriptor>>()?;

            // Rewind to the start and ensure we can also read each descriptor
            // as an option. This ensures that we exercise both deserialization
            // code paths. Reading these again is safe because the underlying
            // parcel fd is duped for each read.
            //
            // SAFETY: start is less than the current size of the parcel data buffer, because we
            // haven't made it any shorter since we got the position.
            unsafe {
                assert!(parcel.set_data_position(start).is_ok());
            }

            let _ = parcel.read::<Option<ParcelFileDescriptor>>()?;
            let _ = parcel.read::<Option<ParcelFileDescriptor>>()?;
            let _ = 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(())
}