cloudfox-coreshift-core 2.31.0

Low-level Linux and Android systems primitives for CoreShift (CloudFox)
Documentation
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

//! The `DisplayInfo` tail-walk protocol logic: the read surface and the
//! generic walk from the end of `supportedModes` to the `state` field.
//!
//! Compiled on Android — where `ParcelReader` implements the surface — and in
//! `cargo test` on host, where a byte cursor over synthetic wire bytes pins
//! the exact walk sequence (the same split `binder::wire` uses for its
//! codecs). The `Display.STATE_*` constants this walk yields live in
//! `binder::display`.

use crate::CoreError;

/// The read surface [`parse_display_state_tail`] needs. `ParcelReader` is the
/// on-device implementation (android-gated); host tests drive a `ByteCursor`
/// over synthetic wire bytes, so the exact tail-walk sequence is pinned
/// without a live `AParcel`.
pub(crate) trait DisplayTailRead {
    fn read_i32(&self) -> Result<i32, CoreError>;
    fn read_float(&self) -> Result<f32, CoreError>;
    fn read_int64(&self) -> Result<i64, CoreError>;
    fn read_bool(&self) -> Result<bool, CoreError>;
    /// Consume a String16 (probe only — the decoded value is irrelevant).
    fn read_string(&self) -> Result<Option<String>, CoreError>;
    fn skip_int_array(&self) -> Result<(), CoreError>;
}

/// Walk the `DisplayInfo` tail from the end of `supportedModes` to the
/// `state` field (the `Display.STATE_*` discriminant). Fail-closed: any
/// shape that does not match the Android 14 layout is a tagged error, never
/// a guessed value.
///
/// The order is pinned against `android.view.DisplayInfo.writeToParcel`:
/// `appsSupportedModes` (n + n modes), `colorMode`, `supportedColorModes`
/// (n + n ints), `hdrCapabilities` (nullable parcelable), `isForceSdr`,
/// `minimalPostProcessingSupported`, `logicalDensityDpi`, `physicalXDpi` /
/// `physicalYDpi` (f32), `appVsyncOffsetNanos` / `presentationDeadlineNanos`
/// (i64), then `state` (i32). Version-sensitive: a shape mismatch fails
/// closed, never returns garbage.
pub(crate) fn parse_display_state_tail(r: &impl DisplayTailRead) -> Result<i32, CoreError> {
    let n_app_modes = r.read_i32()?.max(0) as usize;
    for _ in 0..n_app_modes {
        let _mode = r.read_i32()?; // modeId
        r.read_i32()?; // width
        r.read_i32()?; // height
        let _refresh = r.read_float()?;
        let alt = r.read_i32()?.max(0) as usize;
        for _ in 0..alt {
            let _f = r.read_float()?;
        }
        let hdr = r.read_i32()?.max(0) as usize;
        for _ in 0..hdr {
            r.read_i32()?;
        }
    }
    r.read_i32()?; // colorMode
    let n_color_modes = r.read_i32()?.max(0) as usize;
    for _ in 0..n_color_modes {
        r.read_i32()?;
    }
    // hdrCapabilities: nullable parcelable — class-name String16 probe, or
    // a -1 null marker; the body is an int[] + 3 floats.
    if r.read_string()?.is_some() {
        r.skip_int_array()?; // supportedHdrTypes
        r.read_float()?; // maxLuminance
        r.read_float()?; // maxAverageLuminance
        r.read_float()?; // minLuminance
    }
    r.read_bool()?; // isForceSdr
    r.read_bool()?; // minimalPostProcessingSupported
    r.read_i32()?; // logicalDensityDpi
    r.read_float()?; // physicalXDpi
    r.read_float()?; // physicalYDpi
    r.read_int64()?; // appVsyncOffsetNanos
    r.read_int64()?; // presentationDeadlineNanos
    r.read_i32() // state
}

// ── Tests ──────────────────────────────────────────────────────────────

#[cfg(test)]
mod tests {
    use super::*;

    /// A byte cursor implementing [`DisplayTailRead`] over synthetic wire
    /// bytes, so the tail walk is pinned without a live `AParcel`. `read_string`
    /// and `read_bool` mirror the Parcel primitives: String16 and a boolean
    /// written as an i32. Position advances through interior mutability
    /// (`Cell`) because the read surface borrows `&self` (the same design
    /// `binder::wire::ByteCursor` uses).
    struct TailCursor {
        buf: Vec<u8>,
        pos: std::cell::Cell<usize>,
    }

    impl TailCursor {
        fn take(&self, n: usize) -> Result<&[u8], CoreError> {
            let pos = self.pos.get();
            if pos + n > self.buf.len() {
                return Err(CoreError::binder(-1, "cursor:past_end"));
            }
            self.pos.set(pos + n);
            Ok(&self.buf[pos..pos + n])
        }
    }

    impl DisplayTailRead for TailCursor {
        fn read_i32(&self) -> Result<i32, CoreError> {
            let b: &[u8] = self.take(4)?;
            Ok(i32::from_le_bytes(b.try_into().unwrap()))
        }
        fn read_float(&self) -> Result<f32, CoreError> {
            let b: &[u8] = self.take(4)?;
            Ok(f32::from_le_bytes(b.try_into().unwrap()))
        }
        fn read_int64(&self) -> Result<i64, CoreError> {
            let b: &[u8] = self.take(8)?;
            Ok(i64::from_le_bytes(b.try_into().unwrap()))
        }
        fn read_bool(&self) -> Result<bool, CoreError> {
            Ok(self.read_i32()? != 0)
        }
        fn read_string(&self) -> Result<Option<String>, CoreError> {
            let len16 = self.read_i32()?;
            if len16 < 0 {
                return Ok(None);
            }
            let padded = ((len16 as usize + 1) * 2).next_multiple_of(4);
            let bytes = self.take(padded)?;
            let mut u16s = Vec::with_capacity(len16 as usize);
            for pair in bytes[..len16 as usize * 2].chunks_exact(2) {
                u16s.push(u16::from_le_bytes([pair[0], pair[1]]));
            }
            Ok(Some(String::from_utf16_lossy(&u16s)))
        }
        fn skip_int_array(&self) -> Result<(), CoreError> {
            let n = self.read_i32()?.max(0) as usize;
            for _ in 0..n {
                self.read_i32()?;
            }
            Ok(())
        }
    }

    fn i32b(v: i32) -> Vec<u8> {
        v.to_le_bytes().to_vec()
    }
    fn f32b(v: f32) -> Vec<u8> {
        v.to_le_bytes().to_vec()
    }
    fn i64b(v: i64) -> Vec<u8> {
        v.to_le_bytes().to_vec()
    }
    /// A String16 wire payload (`writeString` shape: len16 + `(len+1)*2`
    /// chars incl. NUL, padded to 4).
    fn str16(s: &str) -> Vec<u8> {
        let mut b = i32b(s.len() as i32);
        for c in s.chars() {
            b.extend((c as u16).to_le_bytes());
        }
        b.extend(0u16.to_le_bytes());
        let payload = (s.len() + 1) * 2;
        b.extend(std::iter::repeat_n(
            0u8,
            payload.next_multiple_of(4) - payload,
        ));
        b
    }

    /// A valid Android 14 tail (the caller may append `state` or extend):
    /// appsSupportedModes (2 modes), colorMode, supportedColorModes (2 ints),
    /// hdrCapabilities (present), isForceSdr, minimalPostProcessingSupported,
    /// logicalDensityDpi, physicalXDpi/YDpi, appVsyncOffsetNanos,
    /// presentationDeadlineNanos.
    fn tail_prefix() -> Vec<u8> {
        let mut b = Vec::new();
        // appsSupportedModes: n=2 modes, each modeId/width/height/refresh,
        // n_alt + alts, n_hdr + hdrs.
        b.extend(i32b(2));
        for (id, refresh) in [(1u32, 60.0f32), (2, 90.0)] {
            b.extend(i32b(id as i32));
            b.extend(i32b(1080));
            b.extend(i32b(2400));
            b.extend(f32b(refresh));
            b.extend(i32b(1));
            b.extend(f32b(refresh));
            b.extend(i32b(0));
        }
        b.extend(i32b(3)); // colorMode
        b.extend(i32b(2)); // supportedColorModes
        b.extend(i32b(2));
        b.extend(i32b(3));
        // hdrCapabilities present: class name + int[] + 3 floats.
        b.extend(str16("android.view.Display$HdrCapabilities"));
        b.extend(i32b(1));
        b.extend(i32b(1)); // hdr type BE10
        b.extend(f32b(1000.0));
        b.extend(f32b(540.0));
        b.extend(f32b(0.01));
        b.extend(i32b(1)); // isForceSdr (true)
        b.extend(i32b(0)); // minimalPostProcessingSupported (false)
        b.extend(i32b(420)); // logicalDensityDpi
        b.extend(f32b(420.0)); // physicalXDpi
        b.extend(f32b(420.0)); // physicalYDpi
        b.extend(i64b(1_000_000)); // appVsyncOffsetNanos
        b.extend(i64b(1_000_000)); // presentationDeadlineNanos
        b
    }

    #[test]
    fn tail_walk_decodes_state_on() {
        let mut b = tail_prefix();
        b.extend(i32b(2)); // DISPLAY_STATE_ON
        let c = TailCursor {
            buf: b,
            pos: std::cell::Cell::new(0),
        };
        assert_eq!(parse_display_state_tail(&c).unwrap(), 2);
        assert_eq!(c.pos.get(), c.buf.len());
    }

    #[test]
    fn tail_walk_decodes_state_doze() {
        let mut b = tail_prefix();
        b.extend(i32b(3)); // DISPLAY_STATE_DOZE
        let c = TailCursor {
            buf: b,
            pos: std::cell::Cell::new(0),
        };
        assert_eq!(parse_display_state_tail(&c).unwrap(), 3);
    }

    #[test]
    fn tail_walk_decodes_null_hdr_capabilities() {
        // hdrCapabilities null marker (-1) instead of a parcelable — the
        // wire has ONLY the -1 where the class-name probe + body sat.
        let mut b = tail_prefix();
        let probe = str16("android.view.Display$HdrCapabilities");
        let pos = b
            .windows(probe.len())
            .position(|w| w == probe.as_slice())
            .unwrap();
        // Remove the probe and its body: int[] count + 1 type + 3 floats.
        let body = 4 + 4 + 3 * 4;
        b.drain(pos..pos + probe.len() + body);
        b.splice(pos..pos, i32b(-1));
        b.extend(i32b(1)); // DISPLAY_STATE_OFF
        let c = TailCursor {
            buf: b,
            pos: std::cell::Cell::new(0),
        };
        assert_eq!(parse_display_state_tail(&c).unwrap(), 1);
    }

    #[test]
    fn tail_walk_fails_closed_on_truncation() {
        let mut b = tail_prefix();
        b.extend(i32b(2));
        b.pop(); // one byte short
        let c = TailCursor {
            buf: b,
            pos: std::cell::Cell::new(0),
        };
        let err = parse_display_state_tail(&c).unwrap_err();
        assert!(err.to_string().contains("cursor"), "err: {err}");
    }

    #[test]
    fn tail_walk_fails_closed_on_empty_input() {
        let c = TailCursor {
            buf: Vec::new(),
            pos: std::cell::Cell::new(0),
        };
        assert!(parse_display_state_tail(&c).is_err());
    }
}