binder/
error.rs

1/*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17use crate::binder::AsNative;
18use crate::sys;
19
20use std::error;
21use std::ffi::{CStr, CString};
22use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
23use std::ptr;
24use std::result;
25
26pub use sys::binder_status_t as status_t;
27
28/// Low-level status codes from Android `libutils`.
29// All error codes are negative integer values. Derived from the anonymous enum
30// in utils/Errors.h
31pub use sys::android_c_interface_StatusCode as StatusCode;
32
33/// A specialized [`Result`](result::Result) for binder operations.
34pub type Result<T> = result::Result<T, StatusCode>;
35
36/// Convert a low-level status code into an empty result.
37///
38/// An OK status is converted into an `Ok` result, any other status is converted
39/// into an `Err` result holding the status code.
40pub fn status_result(status: status_t) -> Result<()> {
41    match parse_status_code(status) {
42        StatusCode::OK => Ok(()),
43        e => Err(e),
44    }
45}
46
47fn parse_status_code(code: i32) -> StatusCode {
48    match code {
49        e if e == StatusCode::OK as i32 => StatusCode::OK,
50        e if e == StatusCode::NO_MEMORY as i32 => StatusCode::NO_MEMORY,
51        e if e == StatusCode::INVALID_OPERATION as i32 => StatusCode::INVALID_OPERATION,
52        e if e == StatusCode::BAD_VALUE as i32 => StatusCode::BAD_VALUE,
53        e if e == StatusCode::BAD_TYPE as i32 => StatusCode::BAD_TYPE,
54        e if e == StatusCode::NAME_NOT_FOUND as i32 => StatusCode::NAME_NOT_FOUND,
55        e if e == StatusCode::PERMISSION_DENIED as i32 => StatusCode::PERMISSION_DENIED,
56        e if e == StatusCode::NO_INIT as i32 => StatusCode::NO_INIT,
57        e if e == StatusCode::ALREADY_EXISTS as i32 => StatusCode::ALREADY_EXISTS,
58        e if e == StatusCode::DEAD_OBJECT as i32 => StatusCode::DEAD_OBJECT,
59        e if e == StatusCode::FAILED_TRANSACTION as i32 => StatusCode::FAILED_TRANSACTION,
60        e if e == StatusCode::BAD_INDEX as i32 => StatusCode::BAD_INDEX,
61        e if e == StatusCode::NOT_ENOUGH_DATA as i32 => StatusCode::NOT_ENOUGH_DATA,
62        e if e == StatusCode::WOULD_BLOCK as i32 => StatusCode::WOULD_BLOCK,
63        e if e == StatusCode::TIMED_OUT as i32 => StatusCode::TIMED_OUT,
64        e if e == StatusCode::UNKNOWN_TRANSACTION as i32 => StatusCode::UNKNOWN_TRANSACTION,
65        e if e == StatusCode::FDS_NOT_ALLOWED as i32 => StatusCode::FDS_NOT_ALLOWED,
66        e if e == StatusCode::UNEXPECTED_NULL as i32 => StatusCode::UNEXPECTED_NULL,
67        _ => StatusCode::UNKNOWN_ERROR,
68    }
69}
70
71pub use sys::android_c_interface_ExceptionCode as ExceptionCode;
72
73fn parse_exception_code(code: i32) -> ExceptionCode {
74    match code {
75        e if e == ExceptionCode::NONE as i32 => ExceptionCode::NONE,
76        e if e == ExceptionCode::SECURITY as i32 => ExceptionCode::SECURITY,
77        e if e == ExceptionCode::BAD_PARCELABLE as i32 => ExceptionCode::BAD_PARCELABLE,
78        e if e == ExceptionCode::ILLEGAL_ARGUMENT as i32 => ExceptionCode::ILLEGAL_ARGUMENT,
79        e if e == ExceptionCode::NULL_POINTER as i32 => ExceptionCode::NULL_POINTER,
80        e if e == ExceptionCode::ILLEGAL_STATE as i32 => ExceptionCode::ILLEGAL_STATE,
81        e if e == ExceptionCode::NETWORK_MAIN_THREAD as i32 => ExceptionCode::NETWORK_MAIN_THREAD,
82        e if e == ExceptionCode::UNSUPPORTED_OPERATION as i32 => {
83            ExceptionCode::UNSUPPORTED_OPERATION
84        }
85        e if e == ExceptionCode::SERVICE_SPECIFIC as i32 => ExceptionCode::SERVICE_SPECIFIC,
86        _ => ExceptionCode::TRANSACTION_FAILED,
87    }
88}
89
90// Safety: `Status` always contains a owning pointer to a valid `AStatus`. The
91// lifetime of the contained pointer is the same as the `Status` object.
92/// High-level binder status object that encapsulates a standard way to keep
93/// track of and chain binder errors along with service specific errors.
94///
95/// Used in AIDL transactions to represent failed transactions.
96pub struct Status(ptr::NonNull<sys::AStatus>);
97
98// Safety: The `AStatus` that the `Status` points to must have an entirely thread-safe API for the
99// duration of the `Status` object's lifetime. We ensure this by not allowing mutation of a `Status`
100// in Rust, and the NDK API says we're the owner of our `AStatus` objects so outside code should not
101// be mutating them underneath us.
102unsafe impl Sync for Status {}
103
104// Safety: `Status` always contains an owning pointer to a global, immutable, interned `AStatus`.
105// A thread-local `AStatus` would not be valid.
106unsafe impl Send for Status {}
107
108fn to_cstring<T: AsRef<str>>(message: T) -> Option<CString> {
109    CString::new(message.as_ref()).ok()
110}
111
112impl Status {
113    /// Create a status object representing a successful transaction.
114    pub fn ok() -> Self {
115        // Safety: `AStatus_newOk` always returns a new, heap allocated
116        // pointer to an `ASTatus` object, so we know this pointer will be
117        // valid.
118        //
119        // Rust takes ownership of the returned pointer.
120        let ptr = unsafe { sys::AStatus_newOk() };
121        Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer"))
122    }
123
124    /// Create a status object from a service specific error
125    pub fn new_service_specific_error(err: i32, message: Option<&CStr>) -> Status {
126        let ptr = if let Some(message) = message {
127            // Safety: Any i32 is a valid service specific error for the
128            // error code parameter. We construct a valid, null-terminated
129            // `CString` from the message, which must be a valid C-style
130            // string to pass as the message. This function always returns a
131            // new, heap allocated pointer to an `AStatus` object, so we
132            // know the returned pointer will be valid.
133            //
134            // Rust takes ownership of the returned pointer.
135            unsafe { sys::AStatus_fromServiceSpecificErrorWithMessage(err, message.as_ptr()) }
136        } else {
137            // Safety: Any i32 is a valid service specific error for the
138            // error code parameter. This function always returns a new,
139            // heap allocated pointer to an `AStatus` object, so we know the
140            // returned pointer will be valid.
141            //
142            // Rust takes ownership of the returned pointer.
143            unsafe { sys::AStatus_fromServiceSpecificError(err) }
144        };
145        Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer"))
146    }
147
148    /// Creates a status object from a service specific error.
149    pub fn new_service_specific_error_str<T: AsRef<str>>(err: i32, message: Option<T>) -> Status {
150        Self::new_service_specific_error(err, message.and_then(to_cstring).as_deref())
151    }
152
153    /// Create a status object from an exception code
154    pub fn new_exception(exception: ExceptionCode, message: Option<&CStr>) -> Status {
155        if let Some(message) = message {
156            // Safety: the C string pointer is valid and not retained by the
157            // function.
158            let ptr = unsafe {
159                sys::AStatus_fromExceptionCodeWithMessage(exception as i32, message.as_ptr())
160            };
161            Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer"))
162        } else {
163            exception.into()
164        }
165    }
166
167    /// Creates a status object from an exception code and message.
168    pub fn new_exception_str<T: AsRef<str>>(
169        exception: ExceptionCode,
170        message: Option<T>,
171    ) -> Status {
172        Self::new_exception(exception, message.and_then(to_cstring).as_deref())
173    }
174
175    /// Create a status object from a raw `AStatus` pointer.
176    ///
177    /// # Safety
178    ///
179    /// This constructor is safe iff `ptr` is a valid pointer to an `AStatus`.
180    pub(crate) unsafe fn from_ptr(ptr: *mut sys::AStatus) -> Self {
181        Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer"))
182    }
183
184    /// Returns `true` if this status represents a successful transaction.
185    pub fn is_ok(&self) -> bool {
186        // Safety: `Status` always contains a valid `AStatus` pointer, so we
187        // are always passing a valid pointer to `AStatus_isOk` here.
188        unsafe { sys::AStatus_isOk(self.as_native()) }
189    }
190
191    /// Returns a description of the status.
192    pub fn get_description(&self) -> String {
193        // Safety: `Status` always contains a valid `AStatus` pointer, so we
194        // are always passing a valid pointer to `AStatus_getDescription`
195        // here.
196        //
197        // `AStatus_getDescription` always returns a valid pointer to a null
198        // terminated C string. Rust is responsible for freeing this pointer
199        // via `AStatus_deleteDescription`.
200        let description_ptr = unsafe { sys::AStatus_getDescription(self.as_native()) };
201        // Safety: `AStatus_getDescription` always returns a valid C string,
202        // which can be safely converted to a `CStr`.
203        let description = unsafe { CStr::from_ptr(description_ptr) };
204        let description = description.to_string_lossy().to_string();
205        // Safety: `description_ptr` was returned from
206        // `AStatus_getDescription` above, and must be freed via
207        // `AStatus_deleteDescription`. We must not access the pointer after
208        // this call, so we copy it into an owned string above and return
209        // that string.
210        unsafe {
211            sys::AStatus_deleteDescription(description_ptr);
212        }
213        description
214    }
215
216    /// Returns the exception code of the status.
217    pub fn exception_code(&self) -> ExceptionCode {
218        // Safety: `Status` always contains a valid `AStatus` pointer, so we
219        // are always passing a valid pointer to `AStatus_getExceptionCode`
220        // here.
221        let code = unsafe { sys::AStatus_getExceptionCode(self.as_native()) };
222        parse_exception_code(code)
223    }
224
225    /// Return a status code representing a transaction failure, or
226    /// `StatusCode::OK` if there was no transaction failure.
227    ///
228    /// If this method returns `OK`, the status may still represent a different
229    /// exception or a service specific error. To find out if this transaction
230    /// as a whole is okay, use [`is_ok`](Self::is_ok) instead.
231    pub fn transaction_error(&self) -> StatusCode {
232        // Safety: `Status` always contains a valid `AStatus` pointer, so we
233        // are always passing a valid pointer to `AStatus_getStatus` here.
234        let code = unsafe { sys::AStatus_getStatus(self.as_native()) };
235        parse_status_code(code)
236    }
237
238    /// Return a service specific error if this status represents one.
239    ///
240    /// This function will only ever return a non-zero result if
241    /// [`exception_code`](Self::exception_code) returns
242    /// `ExceptionCode::SERVICE_SPECIFIC`. If this function returns 0, the
243    /// status object may still represent a different exception or status. To
244    /// find out if this transaction as a whole is okay, use
245    /// [`is_ok`](Self::is_ok) instead.
246    pub fn service_specific_error(&self) -> i32 {
247        // Safety: `Status` always contains a valid `AStatus` pointer, so we
248        // are always passing a valid pointer to
249        // `AStatus_getServiceSpecificError` here.
250        unsafe { sys::AStatus_getServiceSpecificError(self.as_native()) }
251    }
252
253    /// Calls `op` if the status was ok, otherwise returns an `Err` value of
254    /// `self`.
255    pub fn and_then<T, F>(self, op: F) -> result::Result<T, Status>
256    where
257        F: FnOnce() -> result::Result<T, Status>,
258    {
259        <result::Result<(), Status>>::from(self)?;
260        op()
261    }
262}
263
264impl error::Error for Status {}
265
266impl Display for Status {
267    fn fmt(&self, f: &mut Formatter) -> FmtResult {
268        f.write_str(&self.get_description())
269    }
270}
271
272impl Debug for Status {
273    fn fmt(&self, f: &mut Formatter) -> FmtResult {
274        f.write_str(&self.get_description())
275    }
276}
277
278impl PartialEq for Status {
279    fn eq(&self, other: &Status) -> bool {
280        let self_code = self.exception_code();
281        let other_code = other.exception_code();
282
283        match (self_code, other_code) {
284            (ExceptionCode::NONE, ExceptionCode::NONE) => true,
285            (ExceptionCode::TRANSACTION_FAILED, ExceptionCode::TRANSACTION_FAILED) => {
286                self.transaction_error() == other.transaction_error()
287                    && self.get_description() == other.get_description()
288            }
289            (ExceptionCode::SERVICE_SPECIFIC, ExceptionCode::SERVICE_SPECIFIC) => {
290                self.service_specific_error() == other.service_specific_error()
291                    && self.get_description() == other.get_description()
292            }
293            (e1, e2) => e1 == e2 && self.get_description() == other.get_description(),
294        }
295    }
296}
297
298impl Eq for Status {}
299
300impl From<StatusCode> for Status {
301    fn from(status: StatusCode) -> Status {
302        (status as status_t).into()
303    }
304}
305
306impl From<status_t> for Status {
307    fn from(status: status_t) -> Status {
308        // Safety: `AStatus_fromStatus` expects any `status_t` integer, so
309        // this is a safe FFI call. Unknown values will be coerced into
310        // UNKNOWN_ERROR.
311        let ptr = unsafe { sys::AStatus_fromStatus(status) };
312        Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer"))
313    }
314}
315
316impl From<ExceptionCode> for Status {
317    fn from(code: ExceptionCode) -> Status {
318        // Safety: `AStatus_fromExceptionCode` expects any
319        // `binder_exception_t` (i32) integer, so this is a safe FFI call.
320        // Unknown values will be coerced into EX_TRANSACTION_FAILED.
321        let ptr = unsafe { sys::AStatus_fromExceptionCode(code as i32) };
322        Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer"))
323    }
324}
325
326// TODO: impl Try for Status when try_trait is stabilized
327// https://github.com/rust-lang/rust/issues/42327
328impl From<Status> for result::Result<(), Status> {
329    fn from(status: Status) -> result::Result<(), Status> {
330        if status.is_ok() {
331            Ok(())
332        } else {
333            Err(status)
334        }
335    }
336}
337
338impl From<Status> for status_t {
339    fn from(status: Status) -> status_t {
340        status.transaction_error() as status_t
341    }
342}
343
344impl Drop for Status {
345    fn drop(&mut self) {
346        // Safety: `Status` manages the lifetime of its inner `AStatus`
347        // pointee, so we need to delete it here. We know that the pointer
348        // will be valid here since `Status` always contains a valid pointer
349        // while it is alive.
350        unsafe {
351            sys::AStatus_delete(self.0.as_mut());
352        }
353    }
354}
355
356/// Safety: `Status` always contains a valid pointer to an `AStatus` object, so
357/// we can trivially convert it to a correctly-typed raw pointer.
358///
359/// Care must be taken that the returned pointer is only dereferenced while the
360/// `Status` object is still alive.
361unsafe impl AsNative<sys::AStatus> for Status {
362    fn as_native(&self) -> *const sys::AStatus {
363        self.0.as_ptr()
364    }
365
366    fn as_native_mut(&mut self) -> *mut sys::AStatus {
367        // Safety: The pointer will be valid here since `Status` always contains
368        // a valid and initialized pointer while it is alive.
369        unsafe { self.0.as_mut() }
370    }
371}
372
373/// A conversion from `std::result::Result<T, E>` to `binder::Result<T>`. If this type is `Ok(T)`,
374/// it's returned as is. If this type is `Err(E)`, `E` is converted into `Status` which can be
375/// either a general binder exception, or a service-specific exception.
376///
377/// # Examples
378///
379/// ```
380/// // std::io::Error is formatted as the exception's message
381/// fn file_exists(name: &str) -> binder::Result<bool> {
382///     std::fs::metadata(name)
383///         .or_service_specific_exception(NOT_FOUND)?
384/// }
385///
386/// // A custom function is used to create the exception's message
387/// fn file_exists(name: &str) -> binder::Result<bool> {
388///     std::fs::metadata(name)
389///         .or_service_specific_exception_with(NOT_FOUND,
390///             |e| format!("file {} not found: {:?}", name, e))?
391/// }
392///
393/// // anyhow::Error is formatted as the exception's message
394/// use anyhow::{Context, Result};
395/// fn file_exists(name: &str) -> binder::Result<bool> {
396///     std::fs::metadata(name)
397///         .context("file {} not found")
398///         .or_service_specific_exception(NOT_FOUND)?
399/// }
400///
401/// // General binder exceptions can be created similarly
402/// fn file_exists(name: &str) -> binder::Result<bool> {
403///     std::fs::metadata(name)
404///         .or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT)?
405/// }
406/// ```
407pub trait IntoBinderResult<T, E> {
408    /// Converts the embedded error into a general binder exception of code `exception`. The
409    /// message of the exception is set by formatting the error for debugging.
410    fn or_binder_exception(self, exception: ExceptionCode) -> result::Result<T, Status>;
411
412    /// Converts the embedded error into a general binder exception of code `exception`. The
413    /// message of the exception is set by lazily evaluating the `op` function.
414    fn or_binder_exception_with<M: AsRef<str>, O: FnOnce(E) -> M>(
415        self,
416        exception: ExceptionCode,
417        op: O,
418    ) -> result::Result<T, Status>;
419
420    /// Converts the embedded error into a service-specific binder exception. `error_code` is used
421    /// to distinguish different service-specific binder exceptions. The message of the exception
422    /// is set by formatting the error for debugging.
423    fn or_service_specific_exception(self, error_code: i32) -> result::Result<T, Status>;
424
425    /// Converts the embedded error into a service-specific binder exception. `error_code` is used
426    /// to distinguish different service-specific binder exceptions. The message of the exception
427    /// is set by lazily evaluating the `op` function.
428    fn or_service_specific_exception_with<M: AsRef<str>, O: FnOnce(E) -> M>(
429        self,
430        error_code: i32,
431        op: O,
432    ) -> result::Result<T, Status>;
433}
434
435impl<T, E: std::fmt::Debug> IntoBinderResult<T, E> for result::Result<T, E> {
436    fn or_binder_exception(self, exception: ExceptionCode) -> result::Result<T, Status> {
437        self.or_binder_exception_with(exception, |e| format!("{:?}", e))
438    }
439
440    fn or_binder_exception_with<M: AsRef<str>, O: FnOnce(E) -> M>(
441        self,
442        exception: ExceptionCode,
443        op: O,
444    ) -> result::Result<T, Status> {
445        self.map_err(|e| Status::new_exception_str(exception, Some(op(e))))
446    }
447
448    fn or_service_specific_exception(self, error_code: i32) -> result::Result<T, Status> {
449        self.or_service_specific_exception_with(error_code, |e| format!("{:?}", e))
450    }
451
452    fn or_service_specific_exception_with<M: AsRef<str>, O: FnOnce(E) -> M>(
453        self,
454        error_code: i32,
455        op: O,
456    ) -> result::Result<T, Status> {
457        self.map_err(|e| Status::new_service_specific_error_str(error_code, Some(op(e))))
458    }
459}
460
461#[cfg(test)]
462mod tests {
463    use super::*;
464
465    #[test]
466    fn make_service_specific_error() {
467        let status = Status::new_service_specific_error_str(-42, Some("message"));
468
469        assert!(!status.is_ok());
470        assert_eq!(status.exception_code(), ExceptionCode::SERVICE_SPECIFIC);
471        assert_eq!(status.service_specific_error(), -42);
472        assert_eq!(
473            status.get_description(),
474            "Status(-8, EX_SERVICE_SPECIFIC): '-42: message'".to_string()
475        );
476    }
477
478    #[test]
479    fn make_exception() {
480        let status = Status::new_exception_str(ExceptionCode::ILLEGAL_STATE, Some("message"));
481
482        assert!(!status.is_ok());
483        assert_eq!(status.exception_code(), ExceptionCode::ILLEGAL_STATE);
484        assert_eq!(status.service_specific_error(), 0);
485        assert_eq!(status.get_description(), "Status(-5, EX_ILLEGAL_STATE): 'message'".to_string());
486    }
487
488    #[test]
489    fn make_exception_null() {
490        let status = Status::new_exception_str(ExceptionCode::ILLEGAL_STATE, Some("one\0two"));
491
492        assert!(!status.is_ok());
493        assert_eq!(status.exception_code(), ExceptionCode::ILLEGAL_STATE);
494        assert_eq!(status.service_specific_error(), 0);
495        assert_eq!(status.get_description(), "Status(-5, EX_ILLEGAL_STATE): ''".to_string());
496    }
497
498    #[test]
499    fn convert_to_service_specific_exception() {
500        let res: std::result::Result<(), Status> =
501            Err("message").or_service_specific_exception(-42);
502
503        assert!(res.is_err());
504        let status = res.unwrap_err();
505        assert_eq!(status.exception_code(), ExceptionCode::SERVICE_SPECIFIC);
506        assert_eq!(status.service_specific_error(), -42);
507        assert_eq!(
508            status.get_description(),
509            "Status(-8, EX_SERVICE_SPECIFIC): '-42: \"message\"'".to_string()
510        );
511    }
512
513    #[test]
514    fn convert_to_service_specific_exception_with() {
515        let res: std::result::Result<(), Status> = Err("message")
516            .or_service_specific_exception_with(-42, |e| format!("outer message: {:?}", e));
517
518        assert!(res.is_err());
519        let status = res.unwrap_err();
520        assert_eq!(status.exception_code(), ExceptionCode::SERVICE_SPECIFIC);
521        assert_eq!(status.service_specific_error(), -42);
522        assert_eq!(
523            status.get_description(),
524            "Status(-8, EX_SERVICE_SPECIFIC): '-42: outer message: \"message\"'".to_string()
525        );
526    }
527
528    #[test]
529    fn convert_to_binder_exception() {
530        let res: std::result::Result<(), Status> =
531            Err("message").or_binder_exception(ExceptionCode::ILLEGAL_STATE);
532
533        assert!(res.is_err());
534        let status = res.unwrap_err();
535        assert_eq!(status.exception_code(), ExceptionCode::ILLEGAL_STATE);
536        assert_eq!(status.service_specific_error(), 0);
537        assert_eq!(
538            status.get_description(),
539            "Status(-5, EX_ILLEGAL_STATE): '\"message\"'".to_string()
540        );
541    }
542
543    #[test]
544    fn convert_to_binder_exception_with() {
545        let res: std::result::Result<(), Status> = Err("message")
546            .or_binder_exception_with(ExceptionCode::ILLEGAL_STATE, |e| {
547                format!("outer message: {:?}", e)
548            });
549
550        assert!(res.is_err());
551        let status = res.unwrap_err();
552        assert_eq!(status.exception_code(), ExceptionCode::ILLEGAL_STATE);
553        assert_eq!(status.service_specific_error(), 0);
554        assert_eq!(
555            status.get_description(),
556            "Status(-5, EX_ILLEGAL_STATE): 'outer message: \"message\"'".to_string()
557        );
558    }
559}