• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 use crate::binder::AsNative;
18 use crate::sys;
19 
20 use std::error;
21 use std::ffi::{CStr, CString};
22 use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
23 use std::result;
24 
25 pub use sys::binder_status_t as status_t;
26 
27 /// Low-level status codes from Android `libutils`.
28 // All error codes are negative integer values. Derived from the anonymous enum
29 // in utils/Errors.h
30 pub use sys::android_c_interface_StatusCode as StatusCode;
31 
32 /// A specialized [`Result`](result::Result) for binder operations.
33 pub type Result<T> = result::Result<T, StatusCode>;
34 
35 /// Convert a low-level status code into an empty result.
36 ///
37 /// An OK status is converted into an `Ok` result, any other status is converted
38 /// into an `Err` result holding the status code.
status_result(status: status_t) -> Result<()>39 pub fn status_result(status: status_t) -> Result<()> {
40     match parse_status_code(status) {
41         StatusCode::OK => Ok(()),
42         e => Err(e),
43     }
44 }
45 
parse_status_code(code: i32) -> StatusCode46 fn parse_status_code(code: i32) -> StatusCode {
47     match code {
48         e if e == StatusCode::OK as i32 => StatusCode::OK,
49         e if e == StatusCode::NO_MEMORY as i32 => StatusCode::NO_MEMORY,
50         e if e == StatusCode::INVALID_OPERATION as i32 => StatusCode::INVALID_OPERATION,
51         e if e == StatusCode::BAD_VALUE as i32 => StatusCode::BAD_VALUE,
52         e if e == StatusCode::BAD_TYPE as i32 => StatusCode::BAD_TYPE,
53         e if e == StatusCode::NAME_NOT_FOUND as i32 => StatusCode::NAME_NOT_FOUND,
54         e if e == StatusCode::PERMISSION_DENIED as i32 => StatusCode::PERMISSION_DENIED,
55         e if e == StatusCode::NO_INIT as i32 => StatusCode::NO_INIT,
56         e if e == StatusCode::ALREADY_EXISTS as i32 => StatusCode::ALREADY_EXISTS,
57         e if e == StatusCode::DEAD_OBJECT as i32 => StatusCode::DEAD_OBJECT,
58         e if e == StatusCode::FAILED_TRANSACTION as i32 => StatusCode::FAILED_TRANSACTION,
59         e if e == StatusCode::BAD_INDEX as i32 => StatusCode::BAD_INDEX,
60         e if e == StatusCode::NOT_ENOUGH_DATA as i32 => StatusCode::NOT_ENOUGH_DATA,
61         e if e == StatusCode::WOULD_BLOCK as i32 => StatusCode::WOULD_BLOCK,
62         e if e == StatusCode::TIMED_OUT as i32 => StatusCode::TIMED_OUT,
63         e if e == StatusCode::UNKNOWN_TRANSACTION as i32 => StatusCode::UNKNOWN_TRANSACTION,
64         e if e == StatusCode::FDS_NOT_ALLOWED as i32 => StatusCode::FDS_NOT_ALLOWED,
65         e if e == StatusCode::UNEXPECTED_NULL as i32 => StatusCode::UNEXPECTED_NULL,
66         _ => StatusCode::UNKNOWN_ERROR,
67     }
68 }
69 
70 pub use sys::android_c_interface_ExceptionCode as ExceptionCode;
71 
parse_exception_code(code: i32) -> ExceptionCode72 fn parse_exception_code(code: i32) -> ExceptionCode {
73     match code {
74         e if e == ExceptionCode::NONE as i32 => ExceptionCode::NONE,
75         e if e == ExceptionCode::SECURITY as i32 => ExceptionCode::SECURITY,
76         e if e == ExceptionCode::BAD_PARCELABLE as i32 => ExceptionCode::BAD_PARCELABLE,
77         e if e == ExceptionCode::ILLEGAL_ARGUMENT as i32 => ExceptionCode::ILLEGAL_ARGUMENT,
78         e if e == ExceptionCode::NULL_POINTER as i32 => ExceptionCode::NULL_POINTER,
79         e if e == ExceptionCode::ILLEGAL_STATE as i32 => ExceptionCode::ILLEGAL_STATE,
80         e if e == ExceptionCode::NETWORK_MAIN_THREAD as i32 => ExceptionCode::NETWORK_MAIN_THREAD,
81         e if e == ExceptionCode::UNSUPPORTED_OPERATION as i32 => {
82             ExceptionCode::UNSUPPORTED_OPERATION
83         }
84         e if e == ExceptionCode::SERVICE_SPECIFIC as i32 => ExceptionCode::SERVICE_SPECIFIC,
85         _ => ExceptionCode::TRANSACTION_FAILED,
86     }
87 }
88 
89 // Safety: `Status` always contains a owning pointer to a valid `AStatus`. The
90 // lifetime of the contained pointer is the same as the `Status` object.
91 /// High-level binder status object that encapsulates a standard way to keep
92 /// track of and chain binder errors along with service specific errors.
93 ///
94 /// Used in AIDL transactions to represent failed transactions.
95 pub struct Status(*mut sys::AStatus);
96 
97 // Safety: The `AStatus` that the `Status` points to must have an entirely thread-safe API for the
98 // duration of the `Status` object's lifetime. We ensure this by not allowing mutation of a `Status`
99 // in Rust, and the NDK API says we're the owner of our `AStatus` objects so outside code should not
100 // be mutating them underneath us.
101 unsafe impl Sync for Status {}
102 
103 // Safety: `Status` always contains an owning pointer to a global, immutable, interned `AStatus`.
104 // A thread-local `AStatus` would not be valid.
105 unsafe impl Send for Status {}
106 
to_cstring<T: AsRef<str>>(message: T) -> Option<CString>107 fn to_cstring<T: AsRef<str>>(message: T) -> Option<CString> {
108     CString::new(message.as_ref()).ok()
109 }
110 
111 impl Status {
112     /// Create a status object representing a successful transaction.
ok() -> Self113     pub fn ok() -> Self {
114         let ptr = unsafe {
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             sys::AStatus_newOk()
121         };
122         Self(ptr)
123     }
124 
125     /// Create a status object from a service specific error
new_service_specific_error(err: i32, message: Option<&CStr>) -> Status126     pub fn new_service_specific_error(err: i32, message: Option<&CStr>) -> Status {
127         let ptr = if let Some(message) = message {
128             unsafe {
129                 // Safety: Any i32 is a valid service specific error for the
130                 // error code parameter. We construct a valid, null-terminated
131                 // `CString` from the message, which must be a valid C-style
132                 // string to pass as the message. This function always returns a
133                 // new, heap allocated pointer to an `AStatus` object, so we
134                 // know the returned pointer will be valid.
135                 //
136                 // Rust takes ownership of the returned pointer.
137                 sys::AStatus_fromServiceSpecificErrorWithMessage(err, message.as_ptr())
138             }
139         } else {
140             unsafe {
141                 // Safety: Any i32 is a valid service specific error for the
142                 // error code parameter. This function always returns a new,
143                 // heap allocated pointer to an `AStatus` object, so we know the
144                 // returned pointer will be valid.
145                 //
146                 // Rust takes ownership of the returned pointer.
147                 sys::AStatus_fromServiceSpecificError(err)
148             }
149         };
150         Self(ptr)
151     }
152 
153     /// Creates a status object from a service specific error.
new_service_specific_error_str<T: AsRef<str>>(err: i32, message: Option<T>) -> Status154     pub fn new_service_specific_error_str<T: AsRef<str>>(err: i32, message: Option<T>) -> Status {
155         Self::new_service_specific_error(err, message.and_then(to_cstring).as_deref())
156     }
157 
158     /// Create a status object from an exception code
new_exception(exception: ExceptionCode, message: Option<&CStr>) -> Status159     pub fn new_exception(exception: ExceptionCode, message: Option<&CStr>) -> Status {
160         if let Some(message) = message {
161             let ptr = unsafe {
162                 sys::AStatus_fromExceptionCodeWithMessage(exception as i32, message.as_ptr())
163             };
164             Self(ptr)
165         } else {
166             exception.into()
167         }
168     }
169 
170     /// Creates a status object from an exception code and message.
new_exception_str<T: AsRef<str>>( exception: ExceptionCode, message: Option<T>, ) -> Status171     pub fn new_exception_str<T: AsRef<str>>(
172         exception: ExceptionCode,
173         message: Option<T>,
174     ) -> Status {
175         Self::new_exception(exception, message.and_then(to_cstring).as_deref())
176     }
177 
178     /// Create a status object from a raw `AStatus` pointer.
179     ///
180     /// # Safety
181     ///
182     /// This constructor is safe iff `ptr` is a valid pointer to an `AStatus`.
from_ptr(ptr: *mut sys::AStatus) -> Self183     pub(crate) unsafe fn from_ptr(ptr: *mut sys::AStatus) -> Self {
184         Self(ptr)
185     }
186 
187     /// Returns `true` if this status represents a successful transaction.
is_ok(&self) -> bool188     pub fn is_ok(&self) -> bool {
189         unsafe {
190             // Safety: `Status` always contains a valid `AStatus` pointer, so we
191             // are always passing a valid pointer to `AStatus_isOk` here.
192             sys::AStatus_isOk(self.as_native())
193         }
194     }
195 
196     /// Returns a description of the status.
get_description(&self) -> String197     pub fn get_description(&self) -> String {
198         let description_ptr = unsafe {
199             // Safety: `Status` always contains a valid `AStatus` pointer, so we
200             // are always passing a valid pointer to `AStatus_getDescription`
201             // here.
202             //
203             // `AStatus_getDescription` always returns a valid pointer to a null
204             // terminated C string. Rust is responsible for freeing this pointer
205             // via `AStatus_deleteDescription`.
206             sys::AStatus_getDescription(self.as_native())
207         };
208         let description = unsafe {
209             // Safety: `AStatus_getDescription` always returns a valid C string,
210             // which can be safely converted to a `CStr`.
211             CStr::from_ptr(description_ptr)
212         };
213         let description = description.to_string_lossy().to_string();
214         unsafe {
215             // Safety: `description_ptr` was returned from
216             // `AStatus_getDescription` above, and must be freed via
217             // `AStatus_deleteDescription`. We must not access the pointer after
218             // this call, so we copy it into an owned string above and return
219             // that string.
220             sys::AStatus_deleteDescription(description_ptr);
221         }
222         description
223     }
224 
225     /// Returns the exception code of the status.
exception_code(&self) -> ExceptionCode226     pub fn exception_code(&self) -> ExceptionCode {
227         let code = unsafe {
228             // Safety: `Status` always contains a valid `AStatus` pointer, so we
229             // are always passing a valid pointer to `AStatus_getExceptionCode`
230             // here.
231             sys::AStatus_getExceptionCode(self.as_native())
232         };
233         parse_exception_code(code)
234     }
235 
236     /// Return a status code representing a transaction failure, or
237     /// `StatusCode::OK` if there was no transaction failure.
238     ///
239     /// If this method returns `OK`, the status may still represent a different
240     /// exception or a service specific error. To find out if this transaction
241     /// as a whole is okay, use [`is_ok`](Self::is_ok) instead.
transaction_error(&self) -> StatusCode242     pub fn transaction_error(&self) -> StatusCode {
243         let code = unsafe {
244             // Safety: `Status` always contains a valid `AStatus` pointer, so we
245             // are always passing a valid pointer to `AStatus_getStatus` here.
246             sys::AStatus_getStatus(self.as_native())
247         };
248         parse_status_code(code)
249     }
250 
251     /// Return a service specific error if this status represents one.
252     ///
253     /// This function will only ever return a non-zero result if
254     /// [`exception_code`](Self::exception_code) returns
255     /// `ExceptionCode::SERVICE_SPECIFIC`. If this function returns 0, the
256     /// status object may still represent a different exception or status. To
257     /// find out if this transaction as a whole is okay, use
258     /// [`is_ok`](Self::is_ok) instead.
service_specific_error(&self) -> i32259     pub fn service_specific_error(&self) -> i32 {
260         unsafe {
261             // Safety: `Status` always contains a valid `AStatus` pointer, so we
262             // are always passing a valid pointer to
263             // `AStatus_getServiceSpecificError` here.
264             sys::AStatus_getServiceSpecificError(self.as_native())
265         }
266     }
267 
268     /// Calls `op` if the status was ok, otherwise returns an `Err` value of
269     /// `self`.
and_then<T, F>(self, op: F) -> result::Result<T, Status> where F: FnOnce() -> result::Result<T, Status>,270     pub fn and_then<T, F>(self, op: F) -> result::Result<T, Status>
271     where
272         F: FnOnce() -> result::Result<T, Status>,
273     {
274         <result::Result<(), Status>>::from(self)?;
275         op()
276     }
277 }
278 
279 impl error::Error for Status {}
280 
281 impl Display for Status {
fmt(&self, f: &mut Formatter) -> FmtResult282     fn fmt(&self, f: &mut Formatter) -> FmtResult {
283         f.write_str(&self.get_description())
284     }
285 }
286 
287 impl Debug for Status {
fmt(&self, f: &mut Formatter) -> FmtResult288     fn fmt(&self, f: &mut Formatter) -> FmtResult {
289         f.write_str(&self.get_description())
290     }
291 }
292 
293 impl PartialEq for Status {
eq(&self, other: &Status) -> bool294     fn eq(&self, other: &Status) -> bool {
295         let self_code = self.exception_code();
296         let other_code = other.exception_code();
297 
298         match (self_code, other_code) {
299             (ExceptionCode::NONE, ExceptionCode::NONE) => true,
300             (ExceptionCode::TRANSACTION_FAILED, ExceptionCode::TRANSACTION_FAILED) => {
301                 self.transaction_error() == other.transaction_error()
302                     && self.get_description() == other.get_description()
303             }
304             (ExceptionCode::SERVICE_SPECIFIC, ExceptionCode::SERVICE_SPECIFIC) => {
305                 self.service_specific_error() == other.service_specific_error()
306                     && self.get_description() == other.get_description()
307             }
308             (e1, e2) => e1 == e2 && self.get_description() == other.get_description(),
309         }
310     }
311 }
312 
313 impl Eq for Status {}
314 
315 impl From<StatusCode> for Status {
from(status: StatusCode) -> Status316     fn from(status: StatusCode) -> Status {
317         (status as status_t).into()
318     }
319 }
320 
321 impl From<status_t> for Status {
from(status: status_t) -> Status322     fn from(status: status_t) -> Status {
323         let ptr = unsafe {
324             // Safety: `AStatus_fromStatus` expects any `status_t` integer, so
325             // this is a safe FFI call. Unknown values will be coerced into
326             // UNKNOWN_ERROR.
327             sys::AStatus_fromStatus(status)
328         };
329         Self(ptr)
330     }
331 }
332 
333 impl From<ExceptionCode> for Status {
from(code: ExceptionCode) -> Status334     fn from(code: ExceptionCode) -> Status {
335         let ptr = unsafe {
336             // Safety: `AStatus_fromExceptionCode` expects any
337             // `binder_exception_t` (i32) integer, so this is a safe FFI call.
338             // Unknown values will be coerced into EX_TRANSACTION_FAILED.
339             sys::AStatus_fromExceptionCode(code as i32)
340         };
341         Self(ptr)
342     }
343 }
344 
345 // TODO: impl Try for Status when try_trait is stabilized
346 // https://github.com/rust-lang/rust/issues/42327
347 impl From<Status> for result::Result<(), Status> {
from(status: Status) -> result::Result<(), Status>348     fn from(status: Status) -> result::Result<(), Status> {
349         if status.is_ok() {
350             Ok(())
351         } else {
352             Err(status)
353         }
354     }
355 }
356 
357 impl From<Status> for status_t {
from(status: Status) -> status_t358     fn from(status: Status) -> status_t {
359         status.transaction_error() as status_t
360     }
361 }
362 
363 impl Drop for Status {
drop(&mut self)364     fn drop(&mut self) {
365         unsafe {
366             // Safety: `Status` manages the lifetime of its inner `AStatus`
367             // pointee, so we need to delete it here. We know that the pointer
368             // will be valid here since `Status` always contains a valid pointer
369             // while it is alive.
370             sys::AStatus_delete(self.0);
371         }
372     }
373 }
374 
375 /// # Safety
376 ///
377 /// `Status` always contains a valid pointer to an `AStatus` object, so we can
378 /// trivially convert it to a correctly-typed raw pointer.
379 ///
380 /// Care must be taken that the returned pointer is only dereferenced while the
381 /// `Status` object is still alive.
382 unsafe impl AsNative<sys::AStatus> for Status {
as_native(&self) -> *const sys::AStatus383     fn as_native(&self) -> *const sys::AStatus {
384         self.0
385     }
386 
as_native_mut(&mut self) -> *mut sys::AStatus387     fn as_native_mut(&mut self) -> *mut sys::AStatus {
388         self.0
389     }
390 }
391 
392 #[cfg(test)]
393 mod tests {
394     use super::*;
395 
396     #[test]
make_service_specific_error()397     fn make_service_specific_error() {
398         let status = Status::new_service_specific_error_str(-42, Some("message"));
399 
400         assert!(!status.is_ok());
401         assert_eq!(status.exception_code(), ExceptionCode::SERVICE_SPECIFIC);
402         assert_eq!(status.service_specific_error(), -42);
403         assert_eq!(
404             status.get_description(),
405             "Status(-8, EX_SERVICE_SPECIFIC): '-42: message'".to_string()
406         );
407     }
408 
409     #[test]
make_exception()410     fn make_exception() {
411         let status = Status::new_exception_str(ExceptionCode::ILLEGAL_STATE, Some("message"));
412 
413         assert!(!status.is_ok());
414         assert_eq!(status.exception_code(), ExceptionCode::ILLEGAL_STATE);
415         assert_eq!(status.service_specific_error(), 0);
416         assert_eq!(status.get_description(), "Status(-5, EX_ILLEGAL_STATE): 'message'".to_string());
417     }
418 
419     #[test]
make_exception_null()420     fn make_exception_null() {
421         let status = Status::new_exception_str(ExceptionCode::ILLEGAL_STATE, Some("one\0two"));
422 
423         assert!(!status.is_ok());
424         assert_eq!(status.exception_code(), ExceptionCode::ILLEGAL_STATE);
425         assert_eq!(status.service_specific_error(), 0);
426         assert_eq!(status.get_description(), "Status(-5, EX_ILLEGAL_STATE): ''".to_string());
427     }
428 }
429