summaryrefslogtreecommitdiff
path: root/libs/binder/rust/src/error.rs
blob: 2598ebc8041fef68166ff0215e88e0a0b247a6ef (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use crate::binder::AsNative;
use crate::sys;

use std::error;
use std::ffi::CStr;
use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
use std::result;

pub use sys::binder_status_t as status_t;

/// Low-level status codes from Android `libutils`.
// All error codes are negative integer values. Derived from the anonymous enum
// in utils/Errors.h
pub use sys::android_c_interface_StatusCode as StatusCode;

/// A specialized [`Result`](result::Result) for binder operations.
pub type Result<T> = result::Result<T, StatusCode>;

/// Convert a low-level status code into an empty result.
///
/// An OK status is converted into an `Ok` result, any other status is converted
/// into an `Err` result holding the status code.
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,
    }
}

// Safety: `Status` always contains a owning pointer to a valid `AStatus`. The
// lifetime of the contained pointer is the same as the `Status` object.
/// High-level binder status object that encapsulates a standard way to keep
/// track of and chain binder errors along with service specific errors.
///
/// Used in AIDL transactions to represent failed transactions.
pub struct Status(*mut sys::AStatus);

// Safety: The `AStatus` that the `Status` points to must have an entirely thread-safe API for the
// duration of the `Status` object's lifetime. We ensure this by not allowing mutation of a `Status`
// in Rust, and the NDK API says we're the owner of our `AStatus` objects so outside code should not
// be mutating them underneath us.
unsafe impl Sync for Status {}

// Safety: `Status` always contains an owning pointer to a global, immutable, interned `AStatus`.
// A thread-local `AStatus` would not be valid.
unsafe impl Send for Status {}

impl Status {
    /// Create a status object representing a successful transaction.
    pub fn ok() -> Self {
        let ptr = unsafe {
            // Safety: `AStatus_newOk` always returns a new, heap allocated
            // pointer to an `ASTatus` object, so we know this pointer will be
            // valid.
            //
            // Rust takes ownership of the returned pointer.
            sys::AStatus_newOk()
        };
        Self(ptr)
    }

    /// Create a status object from a service specific error
    pub fn new_service_specific_error(err: i32, message: Option<&CStr>) -> Status {
        let ptr = if let Some(message) = message {
            unsafe {
                // Safety: Any i32 is a valid service specific error for the
                // error code parameter. We construct a valid, null-terminated
                // `CString` from the message, which must be a valid C-style
                // string to pass as the message. This function always returns a
                // new, heap allocated pointer to an `AStatus` object, so we
                // know the returned pointer will be valid.
                //
                // Rust takes ownership of the returned pointer.
                sys::AStatus_fromServiceSpecificErrorWithMessage(err, message.as_ptr())
            }
        } else {
            unsafe {
                // Safety: Any i32 is a valid service specific error for the
                // error code parameter. This function always returns a new,
                // heap allocated pointer to an `AStatus` object, so we know the
                // returned pointer will be valid.
                //
                // Rust takes ownership of the returned pointer.
                sys::AStatus_fromServiceSpecificError(err)
            }
        };
        Self(ptr)
    }

    /// Create a status object from an exception code
    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)
        } else {
            exception.into()
        }
    }

    /// Create a status object from a raw `AStatus` pointer.
    ///
    /// # Safety
    ///
    /// This constructor is safe iff `ptr` is a valid pointer to an `AStatus`.
    pub(crate) unsafe fn from_ptr(ptr: *mut sys::AStatus) -> Self {
        Self(ptr)
    }

    /// Returns `true` if this status represents a successful transaction.
    pub fn is_ok(&self) -> bool {
        unsafe {
            // Safety: `Status` always contains a valid `AStatus` pointer, so we
            // are always passing a valid pointer to `AStatus_isOk` here.
            sys::AStatus_isOk(self.as_native())
        }
    }

    /// Returns a description of the status.
    pub fn get_description(&self) -> String {
        let description_ptr = unsafe {
            // Safety: `Status` always contains a valid `AStatus` pointer, so we
            // are always passing a valid pointer to `AStatus_getDescription`
            // here.
            //
            // `AStatus_getDescription` always returns a valid pointer to a null
            // terminated C string. Rust is responsible for freeing this pointer
            // via `AStatus_deleteDescription`.
            sys::AStatus_getDescription(self.as_native())
        };
        let description = unsafe {
            // Safety: `AStatus_getDescription` always returns a valid C string,
            // which can be safely converted to a `CStr`.
            CStr::from_ptr(description_ptr)
        };
        let description = description.to_string_lossy().to_string();
        unsafe {
            // Safety: `description_ptr` was returned from
            // `AStatus_getDescription` above, and must be freed via
            // `AStatus_deleteDescription`. We must not access the pointer after
            // this call, so we copy it into an owned string above and return
            // that string.
            sys::AStatus_deleteDescription(description_ptr);
        }
        description
    }

    /// Returns the exception code of the status.
    pub fn exception_code(&self) -> ExceptionCode {
        let code = unsafe {
            // Safety: `Status` always contains a valid `AStatus` pointer, so we
            // are always passing a valid pointer to `AStatus_getExceptionCode`
            // here.
            sys::AStatus_getExceptionCode(self.as_native())
        };
        parse_exception_code(code)
    }

    /// Return a status code representing a transaction failure, or
    /// `StatusCode::OK` if there was no transaction failure.
    ///
    /// If this method returns `OK`, the status may still represent a different
    /// exception or a service specific error. To find out if this transaction
    /// as a whole is okay, use [`is_ok`](Self::is_ok) instead.
    pub fn transaction_error(&self) -> StatusCode {
        let code = unsafe {
            // Safety: `Status` always contains a valid `AStatus` pointer, so we
            // are always passing a valid pointer to `AStatus_getStatus` here.
            sys::AStatus_getStatus(self.as_native())
        };
        parse_status_code(code)
    }

    /// Return a service specific error if this status represents one.
    ///
    /// This function will only ever return a non-zero result if
    /// [`exception_code`](Self::exception_code) returns
    /// `ExceptionCode::SERVICE_SPECIFIC`. If this function returns 0, the
    /// status object may still represent a different exception or status. To
    /// find out if this transaction as a whole is okay, use
    /// [`is_ok`](Self::is_ok) instead.
    pub fn service_specific_error(&self) -> i32 {
        unsafe {
            // Safety: `Status` always contains a valid `AStatus` pointer, so we
            // are always passing a valid pointer to
            // `AStatus_getServiceSpecificError` here.
            sys::AStatus_getServiceSpecificError(self.as_native())
        }
    }

    /// Calls `op` if the status was ok, otherwise returns an `Err` value of
    /// `self`.
    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()
    }
}

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 {
            // Safety: `AStatus_fromStatus` expects any `status_t` integer, so
            // this is a safe FFI call. Unknown values will be coerced into
            // UNKNOWN_ERROR.
            sys::AStatus_fromStatus(status)
        };
        Self(ptr)
    }
}

impl From<ExceptionCode> for Status {
    fn from(code: ExceptionCode) -> Status {
        let ptr = unsafe {
            // Safety: `AStatus_fromExceptionCode` expects any
            // `binder_exception_t` (i32) integer, so this is a safe FFI call.
            // Unknown values will be coerced into EX_TRANSACTION_FAILED.
            sys::AStatus_fromExceptionCode(code as i32)
        };
        Self(ptr)
    }
}

// TODO: impl Try for Status when try_trait is stabilized
// https://github.com/rust-lang/rust/issues/42327
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 {
            // Safety: `Status` manages the lifetime of its inner `AStatus`
            // pointee, so we need to delete it here. We know that the pointer
            // will be valid here since `Status` always contains a valid pointer
            // while it is alive.
            sys::AStatus_delete(self.0);
        }
    }
}

/// # Safety
///
/// `Status` always contains a valid pointer to an `AStatus` object, so we can
/// trivially convert it to a correctly-typed raw pointer.
///
/// Care must be taken that the returned pointer is only dereferenced while the
/// `Status` object is still alive.
unsafe impl AsNative<sys::AStatus> for Status {
    fn as_native(&self) -> *const sys::AStatus {
        self.0
    }

    fn as_native_mut(&mut self) -> *mut sys::AStatus {
        self.0
    }
}