use crate::binder::AsNative;
use crate::sys;
use alloc::ffi::CString;
use alloc::string::{String, ToString};
use core::ffi::CStr;
use core::fmt::{Debug, Display, Formatter, Result as FmtResult};
use core::ptr;
use core::result;
#[cfg(feature = "std")]
use std::error;
pub use sys::binder_status_t as status_t;
pub use sys::android_c_interface_StatusCode as StatusCode;
pub type Result<T> = result::Result<T, StatusCode>;
pub fn status_result(status: status_t) -> Result<()> {
match parse_status_code(status) {
StatusCode::OK => Ok(()),
e => Err(e),
}
}
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,
}
}
pub struct Status(ptr::NonNull<sys::AStatus>);
unsafe impl Sync for Status {}
unsafe impl Send for Status {}
fn to_cstring<T: AsRef<str>>(message: T) -> Option<CString> {
CString::new(message.as_ref()).ok()
}
impl Status {
pub fn ok() -> Self {
let ptr = unsafe { sys::AStatus_newOk() };
Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer"))
}
pub fn new_service_specific_error(err: i32, message: Option<&CStr>) -> Status {
let ptr = if let Some(message) = message {
unsafe { sys::AStatus_fromServiceSpecificErrorWithMessage(err, message.as_ptr()) }
} else {
unsafe { sys::AStatus_fromServiceSpecificError(err) }
};
Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer"))
}
pub fn new_service_specific_error_str<T: AsRef<str>>(err: i32, message: Option<T>) -> Status {
Self::new_service_specific_error(err, message.and_then(to_cstring).as_deref())
}
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::NonNull::new(ptr).expect("Unexpected null AStatus pointer"))
} else {
exception.into()
}
}
pub fn new_exception_str<T: AsRef<str>>(
exception: ExceptionCode,
message: Option<T>,
) -> Status {
Self::new_exception(exception, message.and_then(to_cstring).as_deref())
}
pub(crate) unsafe fn from_ptr(ptr: *mut sys::AStatus) -> Self {
Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer"))
}
pub fn is_ok(&self) -> bool {
unsafe { sys::AStatus_isOk(self.as_native()) }
}
pub fn get_description(&self) -> String {
let description_ptr = unsafe { sys::AStatus_getDescription(self.as_native()) };
let description = unsafe { CStr::from_ptr(description_ptr) };
let description = description.to_string_lossy().to_string();
unsafe {
sys::AStatus_deleteDescription(description_ptr);
}
description
}
pub fn exception_code(&self) -> ExceptionCode {
let code = unsafe { sys::AStatus_getExceptionCode(self.as_native()) };
parse_exception_code(code)
}
pub fn transaction_error(&self) -> StatusCode {
let code = unsafe { sys::AStatus_getStatus(self.as_native()) };
parse_status_code(code)
}
pub fn service_specific_error(&self) -> i32 {
unsafe { sys::AStatus_getServiceSpecificError(self.as_native()) }
}
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()
}
}
#[cfg(feature = "std")]
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 { sys::AStatus_fromStatus(status) };
Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer"))
}
}
impl From<ExceptionCode> for Status {
fn from(code: ExceptionCode) -> Status {
let ptr = unsafe { sys::AStatus_fromExceptionCode(code as i32) };
Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer"))
}
}
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 {
sys::AStatus_delete(self.0.as_mut());
}
}
}
unsafe impl AsNative<sys::AStatus> for Status {
fn as_native(&self) -> *const sys::AStatus {
self.0.as_ptr()
}
fn as_native_mut(&mut self) -> *mut sys::AStatus {
unsafe { self.0.as_mut() }
}
}
pub trait IntoBinderResult<T, E> {
fn or_binder_exception(self, exception: ExceptionCode) -> result::Result<T, Status>;
fn or_binder_exception_with<M: AsRef<str>, O: FnOnce(E) -> M>(
self,
exception: ExceptionCode,
op: O,
) -> result::Result<T, Status>;
fn or_service_specific_exception(self, error_code: i32) -> result::Result<T, Status>;
fn or_service_specific_exception_with<M: AsRef<str>, O: FnOnce(E) -> M>(
self,
error_code: i32,
op: O,
) -> result::Result<T, Status>;
}
impl<T, E: core::fmt::Debug> IntoBinderResult<T, E> for result::Result<T, E> {
fn or_binder_exception(self, exception: ExceptionCode) -> result::Result<T, Status> {
self.or_binder_exception_with(exception, |e| alloc::format!("{:?}", e))
}
fn or_binder_exception_with<M: AsRef<str>, O: FnOnce(E) -> M>(
self,
exception: ExceptionCode,
op: O,
) -> result::Result<T, Status> {
self.map_err(|e| Status::new_exception_str(exception, Some(op(e))))
}
fn or_service_specific_exception(self, error_code: i32) -> result::Result<T, Status> {
self.or_service_specific_exception_with(error_code, |e| alloc::format!("{:?}", e))
}
fn or_service_specific_exception_with<M: AsRef<str>, O: FnOnce(E) -> M>(
self,
error_code: i32,
op: O,
) -> result::Result<T, Status> {
self.map_err(|e| Status::new_service_specific_error_str(error_code, Some(op(e))))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn make_service_specific_error() {
let status = Status::new_service_specific_error_str(-42, Some("message"));
assert!(!status.is_ok());
assert_eq!(status.exception_code(), ExceptionCode::SERVICE_SPECIFIC);
assert_eq!(status.service_specific_error(), -42);
assert_eq!(
status.get_description(),
"Status(-8, EX_SERVICE_SPECIFIC): '-42: message'".to_string()
);
}
#[test]
fn make_exception() {
let status = Status::new_exception_str(ExceptionCode::ILLEGAL_STATE, Some("message"));
assert!(!status.is_ok());
assert_eq!(status.exception_code(), ExceptionCode::ILLEGAL_STATE);
assert_eq!(status.service_specific_error(), 0);
assert_eq!(status.get_description(), "Status(-5, EX_ILLEGAL_STATE): 'message'".to_string());
}
#[test]
fn make_exception_null() {
let status = Status::new_exception_str(ExceptionCode::ILLEGAL_STATE, Some("one\0two"));
assert!(!status.is_ok());
assert_eq!(status.exception_code(), ExceptionCode::ILLEGAL_STATE);
assert_eq!(status.service_specific_error(), 0);
assert_eq!(status.get_description(), "Status(-5, EX_ILLEGAL_STATE): ''".to_string());
}
#[test]
fn convert_to_service_specific_exception() {
let res: std::result::Result<(), Status> =
Err("message").or_service_specific_exception(-42);
assert!(res.is_err());
let status = res.unwrap_err();
assert_eq!(status.exception_code(), ExceptionCode::SERVICE_SPECIFIC);
assert_eq!(status.service_specific_error(), -42);
assert_eq!(
status.get_description(),
"Status(-8, EX_SERVICE_SPECIFIC): '-42: \"message\"'".to_string()
);
}
#[test]
fn convert_to_service_specific_exception_with() {
let res: std::result::Result<(), Status> = Err("message")
.or_service_specific_exception_with(-42, |e| format!("outer message: {:?}", e));
assert!(res.is_err());
let status = res.unwrap_err();
assert_eq!(status.exception_code(), ExceptionCode::SERVICE_SPECIFIC);
assert_eq!(status.service_specific_error(), -42);
assert_eq!(
status.get_description(),
"Status(-8, EX_SERVICE_SPECIFIC): '-42: outer message: \"message\"'".to_string()
);
}
#[test]
fn convert_to_binder_exception() {
let res: std::result::Result<(), Status> =
Err("message").or_binder_exception(ExceptionCode::ILLEGAL_STATE);
assert!(res.is_err());
let status = res.unwrap_err();
assert_eq!(status.exception_code(), ExceptionCode::ILLEGAL_STATE);
assert_eq!(status.service_specific_error(), 0);
assert_eq!(
status.get_description(),
"Status(-5, EX_ILLEGAL_STATE): '\"message\"'".to_string()
);
}
#[test]
fn convert_to_binder_exception_with() {
let res: std::result::Result<(), Status> = Err("message")
.or_binder_exception_with(ExceptionCode::ILLEGAL_STATE, |e| {
format!("outer message: {:?}", e)
});
assert!(res.is_err());
let status = res.unwrap_err();
assert_eq!(status.exception_code(), ExceptionCode::ILLEGAL_STATE);
assert_eq!(status.service_specific_error(), 0);
assert_eq!(
status.get_description(),
"Status(-5, EX_ILLEGAL_STATE): 'outer message: \"message\"'".to_string()
);
}
}