mirror of
https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation.git
synced 2026-08-09 15:19:11 +02:00
refactor: more pre-parsing
This commit is contained in:
parent
240dde1a97
commit
77a52be830
2 changed files with 63 additions and 36 deletions
|
|
@ -19,8 +19,9 @@
|
|||
//!
|
||||
//! Despite being "low-level", we can still "have cake and eat it too";
|
||||
//! After all, what's the point of an abstraction if it doesn't come with goodies?:
|
||||
//! We can take some freedoms, such as:
|
||||
//! - pre-parsing or validanting output, to provide types with invariants.
|
||||
//! We can reserve some artistic license, such as:
|
||||
//! - shorter names, complemented by context
|
||||
//! - pre-parsing or validanting output, to provide types with invariants
|
||||
//! - strongly-typed rather than "stringly-typed" APIs
|
||||
//! - nicer IDE support
|
||||
//! - compile-time prevention of malformed cmds
|
||||
|
|
@ -77,14 +78,31 @@ impl Cmd {
|
|||
self.0.arg("shell");
|
||||
ShCmd(self)
|
||||
}
|
||||
/// List all detected devices:
|
||||
/// List attached devices (as serials) and their status:
|
||||
/// - USB
|
||||
/// - TCP/IP: WIFI, Ethernet, etc...
|
||||
/// - Local emulators
|
||||
/// Some may not be authorized by the user (yet)
|
||||
pub fn devices(mut self) -> Result<String, String> {
|
||||
/// Status can be (but not limited to):
|
||||
/// - "unauthorized"
|
||||
/// - "device"
|
||||
pub fn devices(mut self) -> Result<Vec<(String, String)>, String> {
|
||||
self.0.arg("devices");
|
||||
self.run()
|
||||
Ok(self
|
||||
.run()?
|
||||
.lines()
|
||||
.skip(1) // header
|
||||
.map(|dev_stat| {
|
||||
let tab_idx = dev_stat
|
||||
.find('\t')
|
||||
.expect("There must be 1 tab after serial");
|
||||
(
|
||||
// serial
|
||||
dev_stat[..tab_idx].to_string(),
|
||||
// status
|
||||
dev_stat[(tab_idx + 1)..].to_string(),
|
||||
)
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
/// Reboots default device
|
||||
pub fn reboot(mut self) -> Result<String, String> {
|
||||
|
|
@ -182,9 +200,9 @@ impl ToString for PmLsPackFlag {
|
|||
}
|
||||
}
|
||||
|
||||
pub const PACK_URI_SCHEME: &str = "package:";
|
||||
pub const PACK_PREFIX: &str = "package:";
|
||||
#[expect(clippy::cast_possible_truncation, reason = "")]
|
||||
pub const PACK_URI_LEN: u8 = PACK_URI_SCHEME.len() as _;
|
||||
pub const PACK_URI_LEN: u8 = PACK_PREFIX.len() as _;
|
||||
|
||||
pub const PM_LIST_PACKS: &str = "pm list packages";
|
||||
pub const PM_CLEAR_PACK: &str = "pm clear";
|
||||
|
|
@ -195,7 +213,8 @@ pub const PM_CLEAR_PACK: &str = "pm clear";
|
|||
#[derive(Debug)]
|
||||
pub struct PmCmd(ShCmd);
|
||||
impl PmCmd {
|
||||
/// `list packages` sub-command
|
||||
/// `list packages` sub-command,
|
||||
/// stripped of "package:" prefix
|
||||
pub fn ls_packs(
|
||||
mut self,
|
||||
f: Option<PmLsPackFlag>,
|
||||
|
|
@ -214,13 +233,14 @@ impl PmCmd {
|
|||
pack_ls
|
||||
.lines()
|
||||
.map(|p_ln| {
|
||||
debug_assert!(p_ln.starts_with(PACK_URI_SCHEME));
|
||||
debug_assert!(p_ln.starts_with(PACK_PREFIX));
|
||||
String::from(&p_ln[PACK_URI_LEN as usize..])
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
/// `list packages` sub-command, but pre-validated
|
||||
/// `list packages` sub-command, pre-validated.
|
||||
/// This is strongly-typed, at the cost of regex overhead.
|
||||
pub fn ls_packs_valid(
|
||||
self,
|
||||
f: Option<PmLsPackFlag>,
|
||||
|
|
@ -230,9 +250,12 @@ impl PmCmd {
|
|||
.into_iter()
|
||||
.map(|p| PackId::new(p).expect("One of these is wrong: `PackId` regex, ADB implementation. Or the spec now allows a wider char-set")).collect())
|
||||
}
|
||||
/// `list users` sub-command
|
||||
pub fn ls_users(mut self) -> Result<String, String> {
|
||||
#[allow(clippy::doc_markdown, reason = "Multi URL")]
|
||||
/// `list users` sub-command.
|
||||
/// - https://source.android.com/docs/devices/admin/multi-user-testing
|
||||
/// - https://stackoverflow.com/questions/37495126/android-get-list-of-users-and-profile-name
|
||||
pub fn ls_users(mut self) -> Result<Vec<String>, String> {
|
||||
self.0 .0 .0.args(["list", "users"]);
|
||||
self.0 .0.run()
|
||||
Ok(self.0 .0.run()?.lines().skip(1).map(String::from).collect())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@ use regex::Regex;
|
|||
use retry::{delay::Fixed, retry, OperationResult};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use static_init::dynamic;
|
||||
use std::collections::HashSet;
|
||||
use std::process::Command;
|
||||
use std::{collections::HashSet, process::Command};
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
use std::os::windows::process::CommandExt;
|
||||
|
|
@ -327,45 +326,50 @@ pub fn is_protected_user(user_id: &str, device_serial: &str) -> bool {
|
|||
}
|
||||
|
||||
/// `pm list users` parsed into a vec with extra info.
|
||||
pub fn list_users_parsed(device_serial: &str) -> Vec<User> {
|
||||
pub fn ls_users_parsed(device_serial: &str) -> Vec<User> {
|
||||
#[dynamic]
|
||||
static RE: Regex = Regex::new(r"\{([0-9]+)").unwrap_or_else(|_| unreachable!());
|
||||
|
||||
AdbCmd::new()
|
||||
.sh(device_serial)
|
||||
.pm()
|
||||
.ls_users()
|
||||
.map(|users| {
|
||||
RE.find_iter(&users)
|
||||
.enumerate()
|
||||
.map(|(i, u)| User {
|
||||
id: u.as_str()[1..].parse().unwrap(),
|
||||
index: i,
|
||||
protected: is_protected_user(&u.as_str()[1..], device_serial),
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
// if default, then empty iter, which becomes empty vec (again)
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, user)| {
|
||||
// It seems each line is a user,
|
||||
// optionally associated with a work-profile.
|
||||
// This will ignore the work-profiles!
|
||||
let u = &RE.captures(&user).expect("Each user should have an ID")[1];
|
||||
User {
|
||||
id: u
|
||||
.parse()
|
||||
.unwrap_or_else(|_| unreachable!("User ID must be valid `u16`")),
|
||||
index: i,
|
||||
protected: is_protected_user(u, device_serial),
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
// getprop ro.serialno
|
||||
/// This matches serials (`getprop ro.serialno`)
|
||||
/// that are authorized by the user.
|
||||
pub async fn get_devices_list() -> Vec<Device> {
|
||||
/// This matches serials that are authorized by the user.
|
||||
#[dynamic]
|
||||
static RE: Regex = Regex::new(r"\n(\S+)\s+device").unwrap_or_else(|_| unreachable!());
|
||||
|
||||
retry(Fixed::from_millis(500).take(120), || {
|
||||
match AdbCmd::new().devices() {
|
||||
Ok(devices) => {
|
||||
let mut device_list: Vec<Device> = vec![];
|
||||
if !RE.is_match(&devices) {
|
||||
if devices.iter().all(|(_, stat)| stat != "device") {
|
||||
return OperationResult::Retry(vec![]);
|
||||
}
|
||||
for device in RE.captures_iter(&devices) {
|
||||
let serial = &device[1];
|
||||
for device in devices {
|
||||
let serial = &device.0;
|
||||
device_list.push(Device {
|
||||
model: get_device_brand(serial),
|
||||
android_sdk: get_android_sdk(serial),
|
||||
user_list: list_users_parsed(serial),
|
||||
user_list: ls_users_parsed(serial),
|
||||
adb_id: serial.to_string(),
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue