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