diff --git a/Cargo.lock b/Cargo.lock index 372b218..4b4d558 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -37,15 +37,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - [[package]] name = "allocator-api2" version = "0.2.21" @@ -3100,35 +3091,6 @@ dependencies = [ "thiserror 2.0.12", ] -[[package]] -name = "regex" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" - [[package]] name = "renderdoc-sys" version = "1.1.0" @@ -3934,7 +3896,6 @@ dependencies = [ "flate2", "iced", "log", - "regex", "retry", "rfd", "serde", diff --git a/Cargo.toml b/Cargo.toml index 65c18fe..e61bba0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,6 @@ chrono = { version = "^0.4", default-features = false, features = [ "clock", ] } log = "^0.4" -regex = "^1.10.2" toml = "^0" dirs = "^6" ureq = { version = "3", features = ["json"] } diff --git a/src/core/adb.rs b/src/core/adb.rs index e74a4e6..cca3c11 100644 --- a/src/core/adb.rs +++ b/src/core/adb.rs @@ -356,15 +356,66 @@ impl PmCommand { }) } - /// `list users` sub-command. - /// Output isn't parsed, because - /// we don't know if the format is stable across Android versions. + /// `list users` sub-command, deserialized/parsed. /// /// - /// - - pub fn list_users(mut self) -> Result { + pub fn list_users(mut self) -> Result, String> { self.0.0.0.args(["list", "users"]); - self.0.0.run() + Ok(self + .0 + .0 + .run()? + .lines() + .skip(1) // omit header + .map(|ln| { + // this could be optimized by making more API-stability assumptions + let ln = ln.trim_ascii_start(); + let ln = ln.strip_prefix("UserInfo").unwrap_or(ln).trim_ascii_start(); + let ln = ln.strip_prefix('{').unwrap_or(ln).trim_ascii(); + let ln = ln.strip_suffix('}').unwrap_or(ln).trim_ascii_end(); + // https://android.googlesource.com/platform/frameworks/base/+/refs/heads/main/core/java/android/content/pm/UserInfo.java + // the format seems to be stable across Android versions: + // "\tUserInfo{::}" + + let mut comps = ln.split(':'); + + let id = comps + .next() + .expect("There must be at least 1 ':'-separated component") + .parse() + .expect("string assumed to be UID numeral"); + //let name = comps + // .next() + // .expect("There must be at least 2 ':'-separated components. 2nd is user-name"); + //let flags = u32::from_str_radix( + // comps.next().expect( + // "There must be at least 3 ':'-separated components. 3rd is user bit-flags", + // ), + // 16, + //) + //.expect("string assumed to be hexadecimal bit-flags"); + UserInfo { + id, + //name: name.into(), + //flags, + } + }) + .collect()) + } +} + +/// Mirror of AOSP `UserInfo` Java Class +#[derive(Debug, Clone)] +pub struct UserInfo { + id: u16, + //name: Box, + //flags: u32, +} +impl UserInfo { + #[must_use] + pub const fn get_id(&self) -> u16 { + self.id } } diff --git a/src/core/sync.rs b/src/core/sync.rs index 81a4392..8b90e89 100644 --- a/src/core/sync.rs +++ b/src/core/sync.rs @@ -3,11 +3,9 @@ use crate::core::{ uad_lists::PackageState, }; use crate::gui::{views::list::PackageInfo, widgets::package_row::PackageRow}; -use regex::Regex; use retry::{OperationResult, delay::Fixed, retry}; use serde::{Deserialize, Serialize}; use std::process::Command; -use std::sync::LazyLock; #[cfg(target_os = "windows")] use std::os::windows::process::CommandExt; @@ -43,6 +41,7 @@ impl std::fmt::Display for Phone { } } +/// `UserInfo` but relevant to UAD #[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] pub struct User { pub id: u16, @@ -292,29 +291,20 @@ pub fn is_protected_user>(user_id: u16, device_serial: S) -> bool .is_err() } -/// `pm list users` parsed into a vector with extra info -pub fn list_users_parsed(device_serial: &str) -> Vec { - // this could be thread-local (no lock overhead), - // but then each thread would compile its own clone of the regex, - // I guess? - static RE: LazyLock = - LazyLock::new(|| Regex::new(r"\{([0-9]+)").unwrap_or_else(|_| unreachable!())); - +pub fn list_users_idx_prot(device_serial: &str) -> Vec { AdbCommand::new() .shell(device_serial) .pm() .list_users() .map(|out| { - RE.find_iter(&out) + out.into_iter() .enumerate() .map(|(i, user)| { - let u = user.as_str()[1..] - .parse() - .unwrap_or_else(|_| unreachable!("User ID must always be a valid `u16`")); + let id = user.get_id(); User { - id: u, + id, index: i, - protected: is_protected_user(u, device_serial), + protected: is_protected_user(id, device_serial), } }) .collect() @@ -338,7 +328,7 @@ pub async fn get_devices_list() -> Vec { device_list.push(Phone { model: format!("{} {}", get_device_brand(serial), get_device_model(serial)), android_sdk: get_android_sdk(serial), - user_list: list_users_parsed(serial), + user_list: list_users_idx_prot(serial), adb_id: serial.to_string(), }); }