Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2a3c250d authored by Stephen Crane's avatar Stephen Crane
Browse files

Initial Binder Rust crate

This crate exposes an idiomatic Binder interface for Rust clients and
services. This interface is primarily designed for use by the
forthcoming Rust backend for the AIDL compiler. The crate links against
the stable NDK version of the libbinder interface and therefore can
be used by APEX packages.

Test: atest rustBinderTest libbinder_rs-internal_test
Bug: 161559357
Change-Id: I2e9b800186f24217edeb1c3778f13902a3cca2fd
parent 6ec9ae54
Loading
Loading
Loading
Loading
+74 −0
Original line number Diff line number Diff line
rust_library {
    name: "libbinder_rs",
    crate_name: "binder",
    srcs: ["src/lib.rs"],
    shared_libs: [
        "libbinder_ndk",
        "libutils",
    ],
    rustlibs: [
        "liblibc",
        "libbinder_ndk_bindgen",
    ],
    host_supported: true,
}

rust_bindgen {
    name: "libbinder_ndk_bindgen",
    crate_name: "binder_ndk_bindgen",
    wrapper_src: "BinderBindings.h",
    source_stem: "ndk_bindings",
    cflags: [
        "-x c++",
    ],
    bindgen_flags: [
        // Unfortunately the only way to specify the rust_non_exhaustive enum
        // style for a type is to make it the default
        "--default-enum-style", "rust_non_exhaustive",
        // and then specify constified enums for the enums we don't want
        // rustified
        "--constified-enum", "android::c_interface::consts::.*",

        "--whitelist-type", "android::c_interface::.*",
        "--whitelist-type", "AStatus",
        "--whitelist-type", "AIBinder_Class",
        "--whitelist-type", "AIBinder",
        "--whitelist-type", "AIBinder_Weak",
        "--whitelist-type", "AIBinder_DeathRecipient",
        "--whitelist-type", "AParcel",
        "--whitelist-type", "binder_status_t",
        "--whitelist-function", ".*",
    ],
    shared_libs: [
        "libbinder_ndk",
    ],
    host_supported: true,

    // Currently necessary for host builds
    // TODO(b/31559095): bionic on host should define this
    target: {
        host: {
            cflags: [
                "-D__INTRODUCED_IN(n)=",
                "-D__assert(a,b,c)=",
                // We want all the APIs to be available on the host.
                "-D__ANDROID_API__=10000",
            ],
        },
    },
}

rust_test {
    name: "libbinder_rs-internal_test",
    crate_name: "binder",
    srcs: ["src/lib.rs"],
    test_suites: ["general-tests"],
    auto_gen_config: true,
    shared_libs: [
        "libbinder_ndk",
    ],
    rustlibs: [
        "liblibc",
        "libbinder_ndk_bindgen",
    ],
}
+86 −0
Original line number Diff line number Diff line
/*
 * 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.
 */

#include <android/binder_ibinder.h>
#include <android/binder_manager.h>
#include <android/binder_parcel.h>
#include <android/binder_process.h>
#include <android/binder_shell.h>
#include <android/binder_status.h>

namespace android {

namespace c_interface {

// Expose error codes from anonymous enum in binder_status.h
enum StatusCode {
    OK = STATUS_OK,
    UNKNOWN_ERROR = STATUS_UNKNOWN_ERROR,
    NO_MEMORY = STATUS_NO_MEMORY,
    INVALID_OPERATION = STATUS_INVALID_OPERATION,
    BAD_VALUE = STATUS_BAD_VALUE,
    BAD_TYPE = STATUS_BAD_TYPE,
    NAME_NOT_FOUND = STATUS_NAME_NOT_FOUND,
    PERMISSION_DENIED = STATUS_PERMISSION_DENIED,
    NO_INIT = STATUS_NO_INIT,
    ALREADY_EXISTS = STATUS_ALREADY_EXISTS,
    DEAD_OBJECT = STATUS_DEAD_OBJECT,
    FAILED_TRANSACTION = STATUS_FAILED_TRANSACTION,
    BAD_INDEX = STATUS_BAD_INDEX,
    NOT_ENOUGH_DATA = STATUS_NOT_ENOUGH_DATA,
    WOULD_BLOCK = STATUS_WOULD_BLOCK,
    TIMED_OUT = STATUS_TIMED_OUT,
    UNKNOWN_TRANSACTION = STATUS_UNKNOWN_TRANSACTION,
    FDS_NOT_ALLOWED = STATUS_FDS_NOT_ALLOWED,
    UNEXPECTED_NULL = STATUS_UNEXPECTED_NULL,
};

// Expose exception codes from anonymous enum in binder_status.h
enum ExceptionCode {
    NONE = EX_NONE,
    SECURITY = EX_SECURITY,
    BAD_PARCELABLE = EX_BAD_PARCELABLE,
    ILLEGAL_ARGUMENT = EX_ILLEGAL_ARGUMENT,
    NULL_POINTER = EX_NULL_POINTER,
    ILLEGAL_STATE = EX_ILLEGAL_STATE,
    NETWORK_MAIN_THREAD = EX_NETWORK_MAIN_THREAD,
    UNSUPPORTED_OPERATION = EX_UNSUPPORTED_OPERATION,
    SERVICE_SPECIFIC = EX_SERVICE_SPECIFIC,
    PARCELABLE = EX_PARCELABLE,

    /**
     * This is special, and indicates to native binder proxies that the
     * transaction has failed at a low level.
     */
    TRANSACTION_FAILED = EX_TRANSACTION_FAILED,
};

namespace consts {

enum {
    FIRST_CALL_TRANSACTION = FIRST_CALL_TRANSACTION,
    LAST_CALL_TRANSACTION = LAST_CALL_TRANSACTION,
};

enum {
    FLAG_ONEWAY = FLAG_ONEWAY,
};

} // namespace consts

} // namespace c_interface

} // namespace android
+10 −0
Original line number Diff line number Diff line
{
  "presubmit": [
    {
      "name": "libbinder_rs-internal_test"
    },
    {
      "name": "rustBinderTest"
    }
  ]
}
+585 −0

File added.

Preview size limit exceeded, changes collapsed.

+373 −0
Original line number Diff line number Diff line
/*
 * 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.
 */

use crate::binder::AsNative;
use crate::sys;

use std::error;
use std::ffi::CStr;
use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
use std::result;

pub use sys::binder_status_t as status_t;

/// Low-level status codes from Android `libutils`.
// All error codes are negative integer values. Derived from the anonymous enum
// in utils/Errors.h
pub use sys::android_c_interface_StatusCode as StatusCode;

/// A specialized [`Result`](result::Result) for binder operations.
pub type Result<T> = result::Result<T, StatusCode>;

/// Convert a low-level status code into an empty result.
///
/// An OK status is converted into an `Ok` result, any other status is converted
/// into an `Err` result holding the status code.
pub fn status_result(status: status_t) -> Result<()> {
    match parse_status_code(status) {
        StatusCode::OK => Ok(()),
        e => Err(e),
    }
}

// impl Display for StatusCode {
//     fn fmt(&self, f: &mut Formatter) -> FmtResult {
//         write!(f, "StatusCode::{:?}", self)
//     }
// }

// impl error::Error for StatusCode {}

fn parse_status_code(code: i32) -> StatusCode {
    match code {
        e if e == StatusCode::OK as i32 => StatusCode::OK,
        e if e == StatusCode::NO_MEMORY as i32 => StatusCode::NO_MEMORY,
        e if e == StatusCode::INVALID_OPERATION as i32 => StatusCode::INVALID_OPERATION,
        e if e == StatusCode::BAD_VALUE as i32 => StatusCode::BAD_VALUE,
        e if e == StatusCode::BAD_TYPE as i32 => StatusCode::BAD_TYPE,
        e if e == StatusCode::NAME_NOT_FOUND as i32 => StatusCode::NAME_NOT_FOUND,
        e if e == StatusCode::PERMISSION_DENIED as i32 => StatusCode::PERMISSION_DENIED,
        e if e == StatusCode::NO_INIT as i32 => StatusCode::NO_INIT,
        e if e == StatusCode::ALREADY_EXISTS as i32 => StatusCode::ALREADY_EXISTS,
        e if e == StatusCode::DEAD_OBJECT as i32 => StatusCode::DEAD_OBJECT,
        e if e == StatusCode::FAILED_TRANSACTION as i32 => StatusCode::FAILED_TRANSACTION,
        e if e == StatusCode::BAD_INDEX as i32 => StatusCode::BAD_INDEX,
        e if e == StatusCode::NOT_ENOUGH_DATA as i32 => StatusCode::NOT_ENOUGH_DATA,
        e if e == StatusCode::WOULD_BLOCK as i32 => StatusCode::WOULD_BLOCK,
        e if e == StatusCode::TIMED_OUT as i32 => StatusCode::TIMED_OUT,
        e if e == StatusCode::UNKNOWN_TRANSACTION as i32 => StatusCode::UNKNOWN_TRANSACTION,
        e if e == StatusCode::FDS_NOT_ALLOWED as i32 => StatusCode::FDS_NOT_ALLOWED,
        e if e == StatusCode::UNEXPECTED_NULL as i32 => StatusCode::UNEXPECTED_NULL,
        _ => StatusCode::UNKNOWN_ERROR,
    }
}

pub use sys::android_c_interface_ExceptionCode as ExceptionCode;

fn parse_exception_code(code: i32) -> ExceptionCode {
    match code {
        e if e == ExceptionCode::NONE as i32 => ExceptionCode::NONE,
        e if e == ExceptionCode::SECURITY as i32 => ExceptionCode::SECURITY,
        e if e == ExceptionCode::BAD_PARCELABLE as i32 => ExceptionCode::BAD_PARCELABLE,
        e if e == ExceptionCode::ILLEGAL_ARGUMENT as i32 => ExceptionCode::ILLEGAL_ARGUMENT,
        e if e == ExceptionCode::NULL_POINTER as i32 => ExceptionCode::NULL_POINTER,
        e if e == ExceptionCode::ILLEGAL_STATE as i32 => ExceptionCode::ILLEGAL_STATE,
        e if e == ExceptionCode::NETWORK_MAIN_THREAD as i32 => {
            ExceptionCode::NETWORK_MAIN_THREAD
        }
        e if e == ExceptionCode::UNSUPPORTED_OPERATION as i32 => {
            ExceptionCode::UNSUPPORTED_OPERATION
        }
        e if e == ExceptionCode::SERVICE_SPECIFIC as i32 => ExceptionCode::SERVICE_SPECIFIC,
        _ => ExceptionCode::TRANSACTION_FAILED,
    }
}

// Safety: `Status` always contains a owning pointer to a valid `AStatus`. The
// lifetime of the contained pointer is the same as the `Status` object.
/// High-level binder status object that encapsulates a standard way to keep
/// track of and chain binder errors along with service specific errors.
///
/// Used in AIDL transactions to represent failed transactions.
pub struct Status(*mut sys::AStatus);

impl Status {
    /// Create a status object representing a successful transaction.
    pub fn ok() -> Self {
        let ptr = unsafe {
            // Safety: `AStatus_newOk` always returns a new, heap allocated
            // pointer to an `ASTatus` object, so we know this pointer will be
            // valid.
            //
            // Rust takes ownership of the returned pointer.
            sys::AStatus_newOk()
        };
        Self(ptr)
    }

    /// Create a status object from a service specific error
    pub fn new_service_specific_error(err: i32, message: Option<&CStr>) -> Status {
        let ptr = if let Some(message) = message {
            unsafe {
                // Safety: Any i32 is a valid service specific error for the
                // error code parameter. We construct a valid, null-terminated
                // `CString` from the message, which must be a valid C-style
                // string to pass as the message. This function always returns a
                // new, heap allocated pointer to an `AStatus` object, so we
                // know the returned pointer will be valid.
                //
                // Rust takes ownership of the returned pointer.
                sys::AStatus_fromServiceSpecificErrorWithMessage(err, message.as_ptr())
            }
        } else {
            unsafe {
                // Safety: Any i32 is a valid service specific error for the
                // error code parameter. This function always returns a new,
                // heap allocated pointer to an `AStatus` object, so we know the
                // returned pointer will be valid.
                //
                // Rust takes ownership of the returned pointer.
                sys::AStatus_fromServiceSpecificError(err)
            }
        };
        Self(ptr)
    }

    /// Create a status object from an exception code
    pub fn new_exception(exception: ExceptionCode, message: Option<&CStr>) -> Status {
        if let Some(message) = message {
            let ptr = unsafe {
                sys::AStatus_fromExceptionCodeWithMessage(exception as i32, message.as_ptr())
            };
            Self(ptr)
        } else {
            exception.into()
        }
    }

    /// Create a status object from a raw `AStatus` pointer.
    ///
    /// # Safety
    ///
    /// This constructor is safe iff `ptr` is a valid pointer to an `AStatus`.
    pub(crate) unsafe fn from_ptr(ptr: *mut sys::AStatus) -> Self {
        Self(ptr)
    }

    /// Returns `true` if this status represents a successful transaction.
    pub fn is_ok(&self) -> bool {
        unsafe {
            // Safety: `Status` always contains a valid `AStatus` pointer, so we
            // are always passing a valid pointer to `AStatus_isOk` here.
            sys::AStatus_isOk(self.as_native())
        }
    }

    /// Returns a description of the status.
    pub fn get_description(&self) -> String {
        let description_ptr = unsafe {
            // Safety: `Status` always contains a valid `AStatus` pointer, so we
            // are always passing a valid pointer to `AStatus_getDescription`
            // here.
            //
            // `AStatus_getDescription` always returns a valid pointer to a null
            // terminated C string. Rust is responsible for freeing this pointer
            // via `AStatus_deleteDescription`.
            sys::AStatus_getDescription(self.as_native())
        };
        let description = unsafe {
            // Safety: `AStatus_getDescription` always returns a valid C string,
            // which can be safely converted to a `CStr`.
            CStr::from_ptr(description_ptr)
        };
        let description = description.to_string_lossy().to_string();
        unsafe {
            // Safety: `description_ptr` was returned from
            // `AStatus_getDescription` above, and must be freed via
            // `AStatus_deleteDescription`. We must not access the pointer after
            // this call, so we copy it into an owned string above and return
            // that string.
            sys::AStatus_deleteDescription(description_ptr);
        }
        description
    }

    /// Returns the exception code of the status.
    pub fn exception_code(&self) -> ExceptionCode {
        let code = unsafe {
            // Safety: `Status` always contains a valid `AStatus` pointer, so we
            // are always passing a valid pointer to `AStatus_getExceptionCode`
            // here.
            sys::AStatus_getExceptionCode(self.as_native())
        };
        parse_exception_code(code)
    }

    /// Return a status code representing a transaction failure, or
    /// `StatusCode::OK` if there was no transaction failure.
    ///
    /// If this method returns `OK`, the status may still represent a different
    /// exception or a service specific error. To find out if this transaction
    /// as a whole is okay, use [`is_ok`](Self::is_ok) instead.
    pub fn transaction_error(&self) -> StatusCode {
        let code = unsafe {
            // Safety: `Status` always contains a valid `AStatus` pointer, so we
            // are always passing a valid pointer to `AStatus_getStatus` here.
            sys::AStatus_getStatus(self.as_native())
        };
        parse_status_code(code)
    }

    /// Return a service specific error if this status represents one.
    ///
    /// This function will only ever return a non-zero result if
    /// [`exception_code`](Self::exception_code) returns
    /// `ExceptionCode::SERVICE_SPECIFIC`. If this function returns 0, the
    /// status object may still represent a different exception or status. To
    /// find out if this transaction as a whole is okay, use
    /// [`is_ok`](Self::is_ok) instead.
    pub fn service_specific_error(&self) -> i32 {
        unsafe {
            // Safety: `Status` always contains a valid `AStatus` pointer, so we
            // are always passing a valid pointer to
            // `AStatus_getServiceSpecificError` here.
            sys::AStatus_getServiceSpecificError(self.as_native())
        }
    }

    /// Calls `op` if the status was ok, otherwise returns an `Err` value of
    /// `self`.
    pub fn and_then<T, F>(self, op: F) -> result::Result<T, Status>
    where
        F: FnOnce() -> result::Result<T, Status>,
    {
        <result::Result<(), Status>>::from(self)?;
        op()
    }
}

impl error::Error for Status {}

impl Display for Status {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        f.write_str(&self.get_description())
    }
}

impl Debug for Status {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        f.write_str(&self.get_description())
    }
}

impl PartialEq for Status {
    fn eq(&self, other: &Status) -> bool {
        let self_code = self.exception_code();
        let other_code = other.exception_code();

        match (self_code, other_code) {
            (ExceptionCode::NONE, ExceptionCode::NONE) => true,
            (ExceptionCode::TRANSACTION_FAILED, ExceptionCode::TRANSACTION_FAILED) => {
                self.transaction_error() == other.transaction_error()
                    && self.get_description() == other.get_description()
            }
            (ExceptionCode::SERVICE_SPECIFIC, ExceptionCode::SERVICE_SPECIFIC) => {
                self.service_specific_error() == other.service_specific_error()
                    && self.get_description() == other.get_description()
            }
            (e1, e2) => e1 == e2 && self.get_description() == other.get_description(),
        }
    }
}

impl Eq for Status {}

impl From<StatusCode> for Status {
    fn from(status: StatusCode) -> Status {
        (status as status_t).into()
    }
}

impl From<status_t> for Status {
    fn from(status: status_t) -> Status {
        let ptr = unsafe {
            // Safety: `AStatus_fromStatus` expects any `status_t` integer, so
            // this is a safe FFI call. Unknown values will be coerced into
            // UNKNOWN_ERROR.
            sys::AStatus_fromStatus(status)
        };
        Self(ptr)
    }
}

impl From<ExceptionCode> for Status {
    fn from(code: ExceptionCode) -> Status {
        let ptr = unsafe {
            // Safety: `AStatus_fromExceptionCode` expects any
            // `binder_exception_t` (i32) integer, so this is a safe FFI call.
            // Unknown values will be coerced into EX_TRANSACTION_FAILED.
            sys::AStatus_fromExceptionCode(code as i32)
        };
        Self(ptr)
    }
}

// TODO: impl Try for Status when try_trait is stabilized
// https://github.com/rust-lang/rust/issues/42327
impl From<Status> for result::Result<(), Status> {
    fn from(status: Status) -> result::Result<(), Status> {
        if status.is_ok() {
            Ok(())
        } else {
            Err(status)
        }
    }
}

impl From<Status> for status_t {
    fn from(status: Status) -> status_t {
        status.transaction_error() as status_t
    }
}

impl Drop for Status {
    fn drop(&mut self) {
        unsafe {
            // Safety: `Status` manages the lifetime of its inner `AStatus`
            // pointee, so we need to delete it here. We know that the pointer
            // will be valid here since `Status` always contains a valid pointer
            // while it is alive.
            sys::AStatus_delete(self.0);
        }
    }
}

/// # Safety
///
/// `Status` always contains a valid pointer to an `AStatus` object, so we can
/// trivially convert it to a correctly-typed raw pointer.
///
/// Care must be taken that the returned pointer is only dereferenced while the
/// `Status` object is still alive.
unsafe impl AsNative<sys::AStatus> for Status {
    fn as_native(&self) -> *const sys::AStatus {
        self.0
    }

    fn as_native_mut(&mut self) -> *mut sys::AStatus {
        self.0
    }
}
Loading