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::ptr;
24 use std::result;
25
26 pub 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
31 pub use sys::android_c_interface_StatusCode as StatusCode;
32
33 /// A specialized [`Result`](result::Result) for binder operations.
34 pub 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.
status_result(status: status_t) -> Result<()>40 pub fn status_result(status: status_t) -> Result<()> {
41 match parse_status_code(status) {
42 StatusCode::OK => Ok(()),
43 e => Err(e),
44 }
45 }
46
parse_status_code(code: i32) -> StatusCode47 fn 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
71 pub use sys::android_c_interface_ExceptionCode as ExceptionCode;
72
parse_exception_code(code: i32) -> ExceptionCode73 fn 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.
96 pub 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.
102 unsafe 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.
106 unsafe impl Send for Status {}
107
to_cstring<T: AsRef<str>>(message: T) -> Option<CString>108 fn to_cstring<T: AsRef<str>>(message: T) -> Option<CString> {
109 CString::new(message.as_ref()).ok()
110 }
111
112 impl Status {
113 /// Create a status object representing a successful transaction.
ok() -> Self114 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
new_service_specific_error(err: i32, message: Option<&CStr>) -> Status125 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.
new_service_specific_error_str<T: AsRef<str>>(err: i32, message: Option<T>) -> Status149 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
new_exception(exception: ExceptionCode, message: Option<&CStr>) -> Status154 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.
new_exception_str<T: AsRef<str>>( exception: ExceptionCode, message: Option<T>, ) -> Status168 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`.
from_ptr(ptr: *mut sys::AStatus) -> Self180 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.
is_ok(&self) -> bool185 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.
get_description(&self) -> String192 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.
exception_code(&self) -> ExceptionCode217 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.
transaction_error(&self) -> StatusCode231 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.
service_specific_error(&self) -> i32246 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`.
and_then<T, F>(self, op: F) -> result::Result<T, Status> where F: FnOnce() -> result::Result<T, Status>,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
264 impl error::Error for Status {}
265
266 impl Display for Status {
fmt(&self, f: &mut Formatter) -> FmtResult267 fn fmt(&self, f: &mut Formatter) -> FmtResult {
268 f.write_str(&self.get_description())
269 }
270 }
271
272 impl Debug for Status {
fmt(&self, f: &mut Formatter) -> FmtResult273 fn fmt(&self, f: &mut Formatter) -> FmtResult {
274 f.write_str(&self.get_description())
275 }
276 }
277
278 impl PartialEq for Status {
eq(&self, other: &Status) -> bool279 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
298 impl Eq for Status {}
299
300 impl From<StatusCode> for Status {
from(status: StatusCode) -> Status301 fn from(status: StatusCode) -> Status {
302 (status as status_t).into()
303 }
304 }
305
306 impl From<status_t> for Status {
from(status: status_t) -> Status307 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
316 impl From<ExceptionCode> for Status {
from(code: ExceptionCode) -> Status317 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
328 impl From<Status> for result::Result<(), Status> {
from(status: Status) -> 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
338 impl From<Status> for status_t {
from(status: Status) -> status_t339 fn from(status: Status) -> status_t {
340 status.transaction_error() as status_t
341 }
342 }
343
344 impl Drop for Status {
drop(&mut self)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.
361 unsafe impl AsNative<sys::AStatus> for Status {
as_native(&self) -> *const sys::AStatus362 fn as_native(&self) -> *const sys::AStatus {
363 self.0.as_ptr()
364 }
365
as_native_mut(&mut self) -> *mut sys::AStatus366 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 /// ```
407 pub 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.
or_binder_exception(self, exception: ExceptionCode) -> result::Result<T, Status>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.
or_binder_exception_with<M: AsRef<str>, O: FnOnce(E) -> M>( self, exception: ExceptionCode, op: O, ) -> result::Result<T, Status>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.
or_service_specific_exception(self, error_code: i32) -> result::Result<T, Status>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.
or_service_specific_exception_with<M: AsRef<str>, O: FnOnce(E) -> M>( self, error_code: i32, op: O, ) -> result::Result<T, Status>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
435 impl<T, E: std::fmt::Debug> IntoBinderResult<T, E> for result::Result<T, E> {
or_binder_exception(self, exception: ExceptionCode) -> result::Result<T, Status>436 fn or_binder_exception(self, exception: ExceptionCode) -> result::Result<T, Status> {
437 self.or_binder_exception_with(exception, |e| format!("{:?}", e))
438 }
439
or_binder_exception_with<M: AsRef<str>, O: FnOnce(E) -> M>( self, exception: ExceptionCode, op: O, ) -> result::Result<T, Status>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
or_service_specific_exception(self, error_code: i32) -> result::Result<T, Status>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
or_service_specific_exception_with<M: AsRef<str>, O: FnOnce(E) -> M>( self, error_code: i32, op: O, ) -> result::Result<T, Status>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)]
462 mod tests {
463 use super::*;
464
465 #[test]
make_service_specific_error()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]
make_exception()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]
make_exception_null()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]
convert_to_service_specific_exception()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]
convert_to_service_specific_exception_with()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]
convert_to_binder_exception()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]
convert_to_binder_exception_with()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 }
560