Skip to main content

rsbinder/
status.rs

1// Copyright 2022 Jeff Kim <hiking90@gmail.com>
2// SPDX-License-Identifier: Apache-2.0
3
4//! Status and exception handling for binder operations.
5//!
6//! This module provides status types for handling exceptions and errors
7//! that occur during binder transactions, including both system-level
8//! errors and application-specific exceptions.
9
10use crate::error;
11use crate::error::StatusCode;
12use crate::parcel::*;
13use crate::parcelable::*;
14use std::fmt::{Debug, Display, Formatter};
15
16/// Result type for operations that can return a `Status` error.
17pub type Result<T> = std::result::Result<T, Status>;
18
19/// Exception codes for binder operations.
20///
21/// These codes represent different types of exceptions that can occur
22/// during binder transactions, corresponding to Java exception types
23/// in the Android framework.
24#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
25#[repr(i32)]
26pub enum ExceptionCode {
27    /// No exception occurred
28    None = 0,
29    /// Security exception
30    Security = -1,
31    /// Bad parcelable data
32    BadParcelable = -2,
33    /// Illegal argument provided
34    IllegalArgument = -3,
35    /// Null pointer exception
36    NullPointer = -4,
37    /// Illegal state exception
38    IllegalState = -5,
39    /// Network operation on main thread
40    NetworkMainThread = -6,
41    /// Unsupported operation
42    UnsupportedOperation = -7,
43    /// Service-specific exception
44    ServiceSpecific = -8,
45    /// Parcelable exception
46    Parcelable = -9,
47
48    // This is special and Java specific; see Parcel.java.
49    /// Has reply header (Java-specific)
50    HasReplyHeader = -128,
51    // This is special, and indicates to C++ binder proxies that the
52    // transaction has failed at a low level.
53    /// Transaction failed at low level
54    TransactionFailed = -129,
55    /// Generic error
56    JustError = -256,
57}
58
59impl Display for ExceptionCode {
60    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
61        match self {
62            ExceptionCode::None => write!(f, "None"),
63            ExceptionCode::Security => write!(f, "Security"),
64            ExceptionCode::BadParcelable => write!(f, "BadParcelable"),
65            ExceptionCode::IllegalArgument => write!(f, "IllegalArgument"),
66            ExceptionCode::NullPointer => write!(f, "NullPointer"),
67            ExceptionCode::IllegalState => write!(f, "IllegalState"),
68            ExceptionCode::NetworkMainThread => write!(f, "NetworkMainThread"),
69            ExceptionCode::UnsupportedOperation => write!(f, "UnsupportedOperation"),
70            ExceptionCode::ServiceSpecific => write!(f, "ServiceSpecific"),
71            ExceptionCode::Parcelable => write!(f, "Parcelable"),
72            ExceptionCode::HasReplyHeader => write!(f, "HasReplyHeader"),
73            ExceptionCode::TransactionFailed => write!(f, "TransactionFailed"),
74            ExceptionCode::JustError => write!(f, "JustError"),
75        }
76    }
77}
78
79impl Serialize for ExceptionCode {
80    fn serialize(&self, parcel: &mut Parcel) -> error::Result<()> {
81        parcel.write::<i32>(&(*self as i32))
82    }
83}
84
85impl Deserialize for ExceptionCode {
86    fn deserialize(parcel: &mut Parcel) -> error::Result<Self> {
87        let exception = parcel.read::<i32>()?;
88        let code = match exception {
89            exception if exception == ExceptionCode::None as i32 => ExceptionCode::None,
90            exception if exception == ExceptionCode::Security as i32 => ExceptionCode::Security,
91            exception if exception == ExceptionCode::BadParcelable as i32 => {
92                ExceptionCode::BadParcelable
93            }
94            exception if exception == ExceptionCode::IllegalArgument as i32 => {
95                ExceptionCode::IllegalArgument
96            }
97            exception if exception == ExceptionCode::NullPointer as i32 => {
98                ExceptionCode::NullPointer
99            }
100            exception if exception == ExceptionCode::IllegalState as i32 => {
101                ExceptionCode::IllegalState
102            }
103            exception if exception == ExceptionCode::NetworkMainThread as i32 => {
104                ExceptionCode::NetworkMainThread
105            }
106            exception if exception == ExceptionCode::UnsupportedOperation as i32 => {
107                ExceptionCode::UnsupportedOperation
108            }
109            exception if exception == ExceptionCode::ServiceSpecific as i32 => {
110                ExceptionCode::ServiceSpecific
111            }
112            exception if exception == ExceptionCode::Parcelable as i32 => ExceptionCode::Parcelable,
113            exception if exception == ExceptionCode::HasReplyHeader as i32 => {
114                ExceptionCode::HasReplyHeader
115            }
116            exception if exception == ExceptionCode::TransactionFailed as i32 => {
117                ExceptionCode::TransactionFailed
118            }
119            _ => ExceptionCode::JustError,
120        };
121        Ok(code)
122    }
123}
124
125/// Status information for binder operations.
126///
127/// `Status` combines an exception code, status code, and optional message
128/// to provide comprehensive error information for binder transactions.
129/// It can represent both successful operations and various failure modes.
130pub struct Status {
131    code: StatusCode,
132    exception: ExceptionCode,
133    message: Option<String>,
134}
135
136impl PartialEq for Status {
137    fn eq(&self, other: &Self) -> bool {
138        self.code == other.code && self.exception == other.exception
139    }
140}
141
142impl Status {
143    fn new(exception: ExceptionCode, status: StatusCode, message: Option<String>) -> Self {
144        Status {
145            code: status,
146            exception,
147            message,
148        }
149    }
150
151    pub fn new_service_specific_error(err: i32, message: Option<String>) -> Self {
152        Self::new(
153            ExceptionCode::ServiceSpecific,
154            StatusCode::ServiceSpecific(err),
155            message,
156        )
157    }
158
159    pub fn is_ok(&self) -> bool {
160        self.exception == ExceptionCode::None
161    }
162
163    pub fn exception_code(&self) -> ExceptionCode {
164        self.exception
165    }
166
167    pub fn transaction_error(&self) -> StatusCode {
168        if self.exception == ExceptionCode::TransactionFailed {
169            self.code
170        } else {
171            StatusCode::Ok
172        }
173    }
174
175    pub fn service_specific_error(&self) -> i32 {
176        if let StatusCode::ServiceSpecific(err) = self.code {
177            err
178        } else {
179            0
180        }
181    }
182}
183
184impl std::error::Error for Status {}
185
186impl Display for Status {
187    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
188        if self.exception == ExceptionCode::None {
189            write!(f, "{}", self.code)
190        } else {
191            write!(
192                f,
193                "{} / {}: {}",
194                self.exception,
195                self.code,
196                self.message.as_ref().unwrap_or(&"".to_owned())
197            )
198        }
199    }
200}
201
202impl Debug for Status {
203    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
204        Display::fmt(self, f)
205    }
206}
207
208impl From<ExceptionCode> for StatusCode {
209    fn from(exception: ExceptionCode) -> Self {
210        match exception {
211            ExceptionCode::TransactionFailed => StatusCode::FailedTransaction,
212            _ => StatusCode::Ok,
213        }
214    }
215}
216
217impl From<Status> for StatusCode {
218    fn from(status: Status) -> Self {
219        status.code
220    }
221}
222
223impl From<StatusCode> for ExceptionCode {
224    fn from(status: StatusCode) -> Self {
225        match status {
226            StatusCode::Ok => ExceptionCode::None,
227            StatusCode::UnexpectedNull => ExceptionCode::NullPointer,
228            StatusCode::ServiceSpecific(_) => ExceptionCode::ServiceSpecific,
229            _ => ExceptionCode::TransactionFailed,
230        }
231    }
232}
233
234impl From<ExceptionCode> for Status {
235    fn from(exception: ExceptionCode) -> Self {
236        Status::new(exception, exception.into(), None)
237    }
238}
239
240impl From<(ExceptionCode, &str)> for Status {
241    fn from(arg: (ExceptionCode, &str)) -> Self {
242        Status::new(arg.0, arg.0.into(), Some(arg.1.to_owned()))
243    }
244}
245
246impl From<StatusCode> for Status {
247    fn from(status: StatusCode) -> Self {
248        Status::new(status.into(), status, None)
249    }
250}
251
252impl Serialize for Status {
253    fn serialize(&self, parcel: &mut Parcel) -> error::Result<()> {
254        if self.exception == ExceptionCode::TransactionFailed {
255            return Err(self.code);
256        }
257
258        parcel.write::<i32>(&(self.exception as _))?;
259        if self.exception == ExceptionCode::None {
260            return Ok(());
261        }
262
263        parcel.write::<String>(self.message.as_ref().unwrap_or(&"".to_owned()))?;
264        parcel.write::<i32>(&0)?; // Empty remote stack trace header
265
266        if self.exception == ExceptionCode::ServiceSpecific {
267            parcel.write::<i32>(&(self.code.into()))?;
268        } else if self.exception == ExceptionCode::Parcelable {
269            parcel.write::<i32>(&0)?;
270        }
271
272        Ok(())
273    }
274}
275
276fn read_check_header_size(parcel: &mut Parcel) -> error::Result<()> {
277    // Skip over the blob of Parcelable data
278    let header_start = parcel.data_position();
279    // Get available size before reading more
280    let header_avail = parcel.data_avail();
281
282    let header_size = parcel.read::<i32>()?;
283
284    // Check for negative values first
285    if header_size < 0 {
286        log::error!("0x534e4554:132650049 Negative header_size({header_size}).");
287        return Err(StatusCode::BadValue);
288    }
289
290    // Safe conversion after negativity check
291    let header_size_usize = header_size as usize;
292
293    // Check against available data
294    if header_size_usize > header_avail {
295        log::error!(
296            "0x534e4554:132650049 Invalid header_size({header_size}) exceeds available({header_avail})."
297        );
298        return Err(StatusCode::BadValue);
299    }
300
301    // Prevent integer overflow in position calculation
302    let new_position = header_start.checked_add(header_size_usize).ok_or_else(|| {
303        log::error!("0x534e4554:132650049 Position overflow with header_size({header_size})");
304        StatusCode::BadValue
305    })?;
306
307    parcel.set_data_position(new_position);
308    Ok(())
309}
310
311impl Deserialize for Status {
312    fn deserialize(parcel: &mut Parcel) -> error::Result<Self> {
313        let mut exception = parcel.read::<ExceptionCode>()?;
314
315        if exception == ExceptionCode::HasReplyHeader {
316            read_check_header_size(parcel)?;
317            exception = ExceptionCode::None;
318        }
319        let status = if exception == ExceptionCode::None {
320            exception.into()
321        } else {
322            let message: String = parcel.read::<String>()?;
323            let remote_stack_trace_header_size = parcel.read::<i32>()?;
324
325            // Check for negative values first
326            if remote_stack_trace_header_size < 0 {
327                log::error!(
328                    "0x534e4554:132650049 Negative remote_stack_trace_header_size({remote_stack_trace_header_size})."
329                );
330                return Err(StatusCode::BadValue);
331            }
332
333            // Safe conversion after negativity check
334            let trace_size_usize = remote_stack_trace_header_size as usize;
335
336            // Check against available data
337            if trace_size_usize > parcel.data_avail() {
338                log::error!(
339                    "0x534e4554:132650049 Invalid remote_stack_trace_header_size({remote_stack_trace_header_size}) exceeds available({}).",
340                    parcel.data_avail()
341                );
342                return Err(StatusCode::BadValue);
343            }
344
345            // Prevent integer overflow in position calculation
346            let current_pos = parcel.data_position();
347            let new_position = current_pos.checked_add(trace_size_usize).ok_or_else(|| {
348                log::error!(
349                    "0x534e4554:132650049 Position overflow with remote_stack_trace_header_size({remote_stack_trace_header_size})"
350                );
351                StatusCode::BadValue
352            })?;
353
354            parcel.set_data_position(new_position);
355
356            let code = if exception == ExceptionCode::ServiceSpecific {
357                let code = parcel.read::<i32>()?;
358                StatusCode::ServiceSpecific(code)
359            } else if exception == ExceptionCode::Parcelable {
360                read_check_header_size(parcel)?;
361                StatusCode::Ok
362            } else {
363                StatusCode::Ok
364            };
365
366            Status::new(exception, code, Some(message))
367        };
368
369        Ok(status)
370    }
371}
372
373#[cfg(test)]
374mod tests {
375    use crate::*;
376
377    #[test]
378    fn test_status() -> Result<()> {
379        let _status = Status::from(StatusCode::Unknown);
380
381        Ok(())
382    }
383
384    #[test]
385    fn test_status_display() -> Result<()> {
386        let unknown = Status::from(StatusCode::Unknown);
387        assert_eq!(format!("{unknown}"), "TransactionFailed / Unknown: ");
388
389        let service_specific =
390            Status::new_service_specific_error(1, Some("Service specific error".to_owned()));
391        assert_eq!(
392            format!("{service_specific}"),
393            "ServiceSpecific / ServiceSpecific(1): Service specific error"
394        );
395
396        let exception = Status::new(
397            ExceptionCode::BadParcelable,
398            StatusCode::Unknown,
399            Some("Bad parcelable".to_owned()),
400        );
401        assert_eq!(
402            format!("{exception}"),
403            "BadParcelable / Unknown: Bad parcelable"
404        );
405
406        Ok(())
407    }
408
409    #[test]
410    fn test_status_serialize() -> Result<()> {
411        let status = Status::from(StatusCode::ServiceSpecific(1));
412        let mut parcel = Parcel::new();
413        status.serialize(&mut parcel).unwrap();
414
415        // deserialize
416        parcel.set_data_position(0);
417        let deserialized = Status::deserialize(&mut parcel).unwrap();
418        assert_eq!(status, deserialized);
419
420        // serialize parcelable
421        let status = Status::new(
422            ExceptionCode::Parcelable,
423            StatusCode::Ok,
424            Some("Parcelable".to_owned()),
425        );
426        let mut parcel = Parcel::new();
427        status.serialize(&mut parcel).unwrap();
428
429        // deserialize parcelable
430        parcel.set_data_position(0);
431        let deserialized = Status::deserialize(&mut parcel).unwrap();
432        assert_eq!(status, deserialized);
433
434        Ok(())
435    }
436}