Skip to main content

rsbinder/
error.rs

1// Copyright 2022 Jeff Kim <hiking90@gmail.com>
2// SPDX-License-Identifier: Apache-2.0
3
4//! Error handling and status codes for binder operations.
5//!
6//! This module defines the result types and error codes used throughout
7//! the binder library for consistent error handling across IPC operations.
8
9use std::error::Error;
10use std::fmt;
11
12/// Result type alias for binder operations.
13pub type Result<T> = std::result::Result<T, StatusCode>;
14
15const UNKNOWN_ERROR: i32 = -2147483647 - 1;
16
17/// Status codes for binder operations.
18///
19/// Represents various error conditions that can occur during binder IPC operations,
20/// including system errors, protocol errors, and application-specific errors.
21#[derive(Default, Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
22#[non_exhaustive]
23pub enum StatusCode {
24    /// Operation completed successfully
25    #[default]
26    Ok,
27    /// Unknown error occurred
28    Unknown,
29    /// Out of memory
30    NoMemory,
31    /// Invalid operation for current state
32    InvalidOperation,
33    /// Invalid parameter value
34    BadValue,
35    /// Wrong data type
36    BadType,
37    /// Named resource not found
38    NameNotFound,
39    /// Permission denied
40    PermissionDenied,
41    /// Object not initialized
42    NoInit,
43    /// Resource already exists
44    AlreadyExists,
45    /// Remote object is dead
46    DeadObject,
47    /// Transaction failed
48    FailedTransaction,
49    /// Unknown transaction code
50    UnknownTransaction,
51    /// Invalid array index
52    BadIndex,
53    /// File descriptors not allowed
54    FdsNotAllowed,
55    /// Unexpected null pointer
56    UnexpectedNull,
57    /// Not enough data available
58    NotEnoughData,
59    /// Operation would block
60    WouldBlock,
61    /// Operation timed out
62    TimedOut,
63    /// Bad file descriptor
64    BadFd,
65    /// System errno value
66    Errno(i32),
67    /// Service-specific error code
68    ServiceSpecific(i32),
69}
70
71impl Error for StatusCode {}
72
73impl fmt::Display for StatusCode {
74    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
75        match self {
76            StatusCode::Ok => write!(f, "Ok"),
77            StatusCode::Unknown => write!(f, "Unknown"),
78            StatusCode::NoMemory => write!(f, "NoMemory"),
79            StatusCode::InvalidOperation => write!(f, "InvalidOperation"),
80            StatusCode::BadValue => write!(f, "BadValue"),
81            StatusCode::BadType => write!(f, "BadType"),
82            StatusCode::NameNotFound => write!(f, "NameNotFound"),
83            StatusCode::PermissionDenied => write!(f, "PermissionDenied"),
84            StatusCode::NoInit => write!(f, "NoInit"),
85            StatusCode::AlreadyExists => write!(f, "AlreadyExists"),
86            StatusCode::DeadObject => write!(f, "DeadObject"),
87            StatusCode::FailedTransaction => write!(f, "FailedTransaction"),
88            StatusCode::UnknownTransaction => write!(f, "UnknownTransaction"),
89            StatusCode::BadIndex => write!(f, "BadIndex"),
90            StatusCode::FdsNotAllowed => write!(f, "FdsNotAllowed"),
91            StatusCode::UnexpectedNull => write!(f, "UnexpectedNull"),
92            StatusCode::NotEnoughData => write!(f, "NotEnoughData"),
93            StatusCode::WouldBlock => write!(f, "WouldBlock"),
94            StatusCode::TimedOut => write!(f, "TimedOut"),
95            StatusCode::BadFd => write!(f, "BadFd"),
96            StatusCode::Errno(errno) => write!(f, "Errno({errno})"),
97            StatusCode::ServiceSpecific(v) => write!(f, "ServiceSpecific({v})"),
98        }
99    }
100}
101
102impl From<StatusCode> for i32 {
103    fn from(code: StatusCode) -> Self {
104        match code {
105            StatusCode::Ok => 0,
106            StatusCode::Unknown => UNKNOWN_ERROR as _,
107            StatusCode::NoMemory => -(rustix::io::Errno::NOMEM.raw_os_error()),
108            StatusCode::InvalidOperation => -(rustix::io::Errno::NOSYS.raw_os_error()),
109            StatusCode::BadValue => -(rustix::io::Errno::INVAL.raw_os_error()),
110            StatusCode::BadType => UNKNOWN_ERROR + 1,
111            StatusCode::NameNotFound => -(rustix::io::Errno::NOENT.raw_os_error()),
112            StatusCode::PermissionDenied => -(rustix::io::Errno::PERM.raw_os_error()),
113            StatusCode::NoInit => -(rustix::io::Errno::NODEV.raw_os_error()),
114            StatusCode::AlreadyExists => -(rustix::io::Errno::EXIST.raw_os_error()),
115            StatusCode::DeadObject => -(rustix::io::Errno::PIPE.raw_os_error()),
116            StatusCode::FailedTransaction => UNKNOWN_ERROR + 2,
117            StatusCode::UnknownTransaction => -(rustix::io::Errno::BADMSG.raw_os_error()),
118            StatusCode::BadIndex => -(rustix::io::Errno::OVERFLOW.raw_os_error()),
119            StatusCode::FdsNotAllowed => UNKNOWN_ERROR + 7,
120            StatusCode::UnexpectedNull => UNKNOWN_ERROR + 8,
121            StatusCode::NotEnoughData => -(rustix::io::Errno::NODATA.raw_os_error()),
122            StatusCode::WouldBlock => -(rustix::io::Errno::WOULDBLOCK.raw_os_error()),
123            StatusCode::TimedOut => -(rustix::io::Errno::TIMEDOUT.raw_os_error()),
124            StatusCode::BadFd => -(rustix::io::Errno::BADF.raw_os_error()),
125            StatusCode::ServiceSpecific(v) => v,
126            StatusCode::Errno(errno) => errno,
127        }
128    }
129}
130
131impl From<i32> for StatusCode {
132    fn from(code: i32) -> Self {
133        match code {
134            code if code == StatusCode::Ok.into() => StatusCode::Ok,
135            code if code == StatusCode::Unknown.into() => StatusCode::Unknown,
136            code if code == StatusCode::NoMemory.into() => StatusCode::NoMemory,
137            code if code == StatusCode::InvalidOperation.into() => StatusCode::InvalidOperation,
138            code if code == StatusCode::BadValue.into() => StatusCode::BadValue,
139            code if code == StatusCode::BadType.into() => StatusCode::BadType,
140            code if code == StatusCode::NameNotFound.into() => StatusCode::NameNotFound,
141            code if code == StatusCode::PermissionDenied.into() => StatusCode::PermissionDenied,
142            code if code == StatusCode::NoInit.into() => StatusCode::NoInit,
143            code if code == StatusCode::AlreadyExists.into() => StatusCode::AlreadyExists,
144            code if code == StatusCode::DeadObject.into() => StatusCode::DeadObject,
145            code if code == StatusCode::FailedTransaction.into() => StatusCode::FailedTransaction,
146            code if code == StatusCode::UnknownTransaction.into() => StatusCode::UnknownTransaction,
147            code if code == StatusCode::BadIndex.into() => StatusCode::BadIndex,
148            code if code == StatusCode::FdsNotAllowed.into() => StatusCode::FdsNotAllowed,
149            code if code == StatusCode::UnexpectedNull.into() => StatusCode::UnexpectedNull,
150            code if code == StatusCode::NotEnoughData.into() => StatusCode::NotEnoughData,
151            code if code == StatusCode::WouldBlock.into() => StatusCode::WouldBlock,
152            code if code == StatusCode::TimedOut.into() => StatusCode::TimedOut,
153            code if code == StatusCode::BadFd.into() => StatusCode::BadFd,
154            code if code < 0 => StatusCode::Errno(code),
155            _ => StatusCode::ServiceSpecific(code),
156        }
157    }
158}
159
160impl From<std::array::TryFromSliceError> for StatusCode {
161    fn from(_: std::array::TryFromSliceError) -> Self {
162        StatusCode::NotEnoughData
163    }
164}
165
166impl From<std::io::Error> for StatusCode {
167    fn from(err: std::io::Error) -> Self {
168        // Preserve the underlying errno instead of flattening every I/O
169        // failure to BadFd. OS-backed errors route through the errno
170        // mapping (BADF still yields BadFd); non-OS errors have no errno
171        // to map, so they surface as Unknown.
172        match err.raw_os_error() {
173            Some(raw) => StatusCode::from(rustix::io::Errno::from_raw_os_error(raw)),
174            None => StatusCode::Unknown,
175        }
176    }
177}
178
179impl From<rustix::io::Errno> for StatusCode {
180    fn from(errno: rustix::io::Errno) -> Self {
181        match errno {
182            rustix::io::Errno::NOMEM => StatusCode::NoMemory,
183            rustix::io::Errno::NOSYS => StatusCode::InvalidOperation,
184            rustix::io::Errno::INVAL => StatusCode::BadValue,
185            rustix::io::Errno::NOENT => StatusCode::NameNotFound,
186            rustix::io::Errno::PERM => StatusCode::PermissionDenied,
187            rustix::io::Errno::NODEV => StatusCode::NoInit,
188            rustix::io::Errno::EXIST => StatusCode::AlreadyExists,
189            rustix::io::Errno::PIPE => StatusCode::DeadObject,
190            rustix::io::Errno::BADMSG => StatusCode::UnknownTransaction,
191            rustix::io::Errno::OVERFLOW => StatusCode::BadIndex,
192            rustix::io::Errno::NODATA => StatusCode::NotEnoughData,
193            rustix::io::Errno::WOULDBLOCK => StatusCode::WouldBlock,
194            rustix::io::Errno::TIMEDOUT => StatusCode::TimedOut,
195            rustix::io::Errno::BADF => StatusCode::BadFd,
196            _ => StatusCode::Errno(-errno.raw_os_error()),
197        }
198    }
199}
200
201#[cfg(test)]
202mod tests {
203    use super::*;
204
205    #[test]
206    fn test_status_code() {
207        let code = StatusCode::Ok;
208        assert_eq!(code, StatusCode::from(0));
209        assert_eq!(code, StatusCode::from(Into::<i32>::into(StatusCode::Ok)));
210
211        let code = StatusCode::Unknown;
212        assert_eq!(code, StatusCode::from(UNKNOWN_ERROR));
213        assert_eq!(
214            code,
215            StatusCode::from(Into::<i32>::into(StatusCode::Unknown))
216        );
217
218        let code = StatusCode::NoMemory;
219        assert_eq!(
220            code,
221            StatusCode::from(-(rustix::io::Errno::NOMEM.raw_os_error()))
222        );
223        assert_eq!(
224            code,
225            StatusCode::from(Into::<i32>::into(StatusCode::NoMemory))
226        );
227
228        let code = StatusCode::InvalidOperation;
229        assert_eq!(
230            code,
231            StatusCode::from(-(rustix::io::Errno::NOSYS.raw_os_error()))
232        );
233        assert_eq!(
234            code,
235            StatusCode::from(Into::<i32>::into(StatusCode::InvalidOperation))
236        );
237
238        let code = StatusCode::BadValue;
239        assert_eq!(
240            code,
241            StatusCode::from(-(rustix::io::Errno::INVAL.raw_os_error()))
242        );
243        assert_eq!(
244            code,
245            StatusCode::from(Into::<i32>::into(StatusCode::BadValue))
246        );
247
248        let code = StatusCode::BadType;
249        assert_eq!(code, StatusCode::from(UNKNOWN_ERROR + 1));
250        assert_eq!(
251            code,
252            StatusCode::from(Into::<i32>::into(StatusCode::BadType))
253        );
254
255        let code = StatusCode::NameNotFound;
256        assert_eq!(
257            code,
258            StatusCode::from(-(rustix::io::Errno::NOENT.raw_os_error()))
259        );
260        assert_eq!(
261            code,
262            StatusCode::from(Into::<i32>::into(StatusCode::NameNotFound))
263        );
264
265        let code = StatusCode::PermissionDenied;
266        assert_eq!(
267            code,
268            StatusCode::from(-(rustix::io::Errno::PERM.raw_os_error()))
269        );
270        assert_eq!(
271            code,
272            StatusCode::from(Into::<i32>::into(StatusCode::PermissionDenied))
273        );
274
275        let code = StatusCode::NoInit;
276        assert_eq!(
277            code,
278            StatusCode::from(-(rustix::io::Errno::NODEV.raw_os_error()))
279        );
280        assert_eq!(
281            code,
282            StatusCode::from(Into::<i32>::into(StatusCode::NoInit))
283        );
284
285        let code = StatusCode::AlreadyExists;
286        assert_eq!(
287            code,
288            StatusCode::from(-(rustix::io::Errno::EXIST.raw_os_error()))
289        );
290        assert_eq!(
291            code,
292            StatusCode::from(Into::<i32>::into(StatusCode::AlreadyExists))
293        );
294
295        let code = StatusCode::DeadObject;
296        assert_eq!(
297            code,
298            StatusCode::from(-(rustix::io::Errno::PIPE.raw_os_error()))
299        );
300        assert_eq!(
301            code,
302            StatusCode::from(Into::<i32>::into(StatusCode::DeadObject))
303        );
304
305        let code = StatusCode::FailedTransaction;
306        assert_eq!(code, StatusCode::from(UNKNOWN_ERROR + 2));
307        assert_eq!(
308            code,
309            StatusCode::from(Into::<i32>::into(StatusCode::FailedTransaction))
310        );
311
312        let code = StatusCode::UnknownTransaction;
313        assert_eq!(
314            code,
315            StatusCode::from(-(rustix::io::Errno::BADMSG.raw_os_error()))
316        );
317        assert_eq!(
318            code,
319            StatusCode::from(Into::<i32>::into(StatusCode::UnknownTransaction))
320        );
321
322        let code = StatusCode::BadIndex;
323        assert_eq!(
324            code,
325            StatusCode::from(-(rustix::io::Errno::OVERFLOW.raw_os_error()))
326        );
327        assert_eq!(
328            code,
329            StatusCode::from(Into::<i32>::into(StatusCode::BadIndex))
330        );
331
332        let code = StatusCode::FdsNotAllowed;
333        assert_eq!(code, StatusCode::from(UNKNOWN_ERROR + 7));
334        assert_eq!(
335            code,
336            StatusCode::from(Into::<i32>::into(StatusCode::FdsNotAllowed))
337        );
338
339        let code = StatusCode::UnexpectedNull;
340        assert_eq!(code, StatusCode::from(UNKNOWN_ERROR + 8));
341        assert_eq!(
342            code,
343            StatusCode::from(Into::<i32>::into(StatusCode::UnexpectedNull))
344        );
345
346        let code = StatusCode::NotEnoughData;
347        assert_eq!(
348            code,
349            StatusCode::from(-(rustix::io::Errno::NODATA.raw_os_error()))
350        );
351        assert_eq!(
352            code,
353            StatusCode::from(Into::<i32>::into(StatusCode::NotEnoughData))
354        );
355
356        let code = StatusCode::WouldBlock;
357        assert_eq!(
358            code,
359            StatusCode::from(-(rustix::io::Errno::WOULDBLOCK.raw_os_error()))
360        );
361        assert_eq!(
362            code,
363            StatusCode::from(Into::<i32>::into(StatusCode::WouldBlock))
364        );
365
366        let code = StatusCode::TimedOut;
367        assert_eq!(
368            code,
369            StatusCode::from(-(rustix::io::Errno::TIMEDOUT.raw_os_error()))
370        );
371        assert_eq!(
372            code,
373            StatusCode::from(Into::<i32>::into(StatusCode::TimedOut))
374        );
375
376        let code = StatusCode::BadFd;
377        assert_eq!(
378            code,
379            StatusCode::from(-(rustix::io::Errno::BADF.raw_os_error()))
380        );
381        assert_eq!(code, StatusCode::from(Into::<i32>::into(StatusCode::BadFd)));
382
383        let code = StatusCode::ServiceSpecific(1);
384        assert_eq!(code, StatusCode::from(1));
385        assert_eq!(
386            code,
387            StatusCode::from(Into::<i32>::into(StatusCode::ServiceSpecific(1)))
388        );
389
390        let code: StatusCode = StatusCode::Errno(-64);
391        assert_eq!(code, StatusCode::from(-64));
392        assert_eq!(
393            code,
394            StatusCode::from(Into::<i32>::into(StatusCode::Errno(-64)))
395        );
396    }
397
398    #[test]
399    fn test_status_code_from_errno() {
400        let code = StatusCode::from(rustix::io::Errno::NOMEM);
401        assert_eq!(code, StatusCode::NoMemory);
402
403        let code = StatusCode::from(rustix::io::Errno::NOSYS);
404        assert_eq!(code, StatusCode::InvalidOperation);
405
406        let code = StatusCode::from(rustix::io::Errno::INVAL);
407        assert_eq!(code, StatusCode::BadValue);
408
409        let code = StatusCode::from(rustix::io::Errno::NOENT);
410        assert_eq!(code, StatusCode::NameNotFound);
411
412        let code = StatusCode::from(rustix::io::Errno::PERM);
413        assert_eq!(code, StatusCode::PermissionDenied);
414
415        let code = StatusCode::from(rustix::io::Errno::NODEV);
416        assert_eq!(code, StatusCode::NoInit);
417
418        let code = StatusCode::from(rustix::io::Errno::EXIST);
419        assert_eq!(code, StatusCode::AlreadyExists);
420
421        let code = StatusCode::from(rustix::io::Errno::PIPE);
422        assert_eq!(code, StatusCode::DeadObject);
423
424        let code = StatusCode::from(rustix::io::Errno::BADMSG);
425        assert_eq!(code, StatusCode::UnknownTransaction);
426
427        let code = StatusCode::from(rustix::io::Errno::OVERFLOW);
428        assert_eq!(code, StatusCode::BadIndex);
429
430        let code = StatusCode::from(rustix::io::Errno::NODATA);
431        assert_eq!(code, StatusCode::NotEnoughData);
432
433        let code = StatusCode::from(rustix::io::Errno::WOULDBLOCK);
434        assert_eq!(code, StatusCode::WouldBlock);
435
436        let code = StatusCode::from(rustix::io::Errno::TIMEDOUT);
437        assert_eq!(code, StatusCode::TimedOut);
438
439        let code = StatusCode::from(rustix::io::Errno::BADF);
440        assert_eq!(code, StatusCode::BadFd);
441
442        let code = StatusCode::from(rustix::io::Errno::from_raw_os_error(64));
443        assert_eq!(code, StatusCode::Errno(-64));
444    }
445
446    // Regression: `From<std::io::Error>` must preserve the underlying
447    // errno through the Errno mapping instead of flattening every I/O
448    // failure to `BadFd`. Non-OS errors (no errno) surface as `Unknown`.
449    #[test]
450    fn status_code_from_io_error_preserves_errno() {
451        let enoent = std::io::Error::from_raw_os_error(rustix::io::Errno::NOENT.raw_os_error());
452        assert_eq!(
453            StatusCode::from(enoent),
454            StatusCode::NameNotFound,
455            "ENOENT must map to NameNotFound, not be flattened to BadFd"
456        );
457
458        // BADF still resolves to BadFd — via the errno mapping, not a blanket default.
459        let ebadf = std::io::Error::from_raw_os_error(rustix::io::Errno::BADF.raw_os_error());
460        assert_eq!(StatusCode::from(ebadf), StatusCode::BadFd);
461
462        // A non-OS error has no errno to map → Unknown.
463        let non_os = std::io::Error::other("no errno");
464        assert_eq!(non_os.raw_os_error(), None);
465        assert_eq!(StatusCode::from(non_os), StatusCode::Unknown);
466    }
467}