cargo_mobile2/android/adb/
device_list.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
use super::{device_name, get_prop};
use crate::{
    android::{device::Device, env::Env, target::Target},
    env::ExplicitEnv as _,
    util::cli::{Report, Reportable},
};
use once_cell_regex::regex_multi_line;
use std::{collections::BTreeSet, process::Command};
use thiserror::Error;

#[derive(Debug, Error)]
pub enum Error {
    #[error("Failed to run `adb devices`: {0}")]
    DevicesFailed(#[from] super::RunCheckedError),
    #[error(transparent)]
    NameFailed(#[from] device_name::Error),
    #[error(transparent)]
    ModelFailed(get_prop::Error),
    #[error(transparent)]
    AbiFailed(get_prop::Error),
    #[error("{0:?} isn't a valid target ABI.")]
    AbiInvalid(String),
    #[error(transparent)]
    Io(#[from] std::io::Error),
}

impl Reportable for Error {
    fn report(&self) -> Report {
        let msg = "Failed to detect connected Android devices";
        match self {
            Self::DevicesFailed(err) => err.report("Failed to run `adb devices`"),
            Self::NameFailed(err) => err.report(),
            Self::ModelFailed(err) | Self::AbiFailed(err) => err.report(),
            Self::AbiInvalid(_) => Report::error(msg, self),
            Self::Io(err) => Report::error(msg, err),
        }
    }
}

const ADB_DEVICE_REGEX: &str = r"^([\S]{6,100})	device\b";

pub fn device_list(env: &Env) -> Result<BTreeSet<Device<'static>>, Error> {
    let mut cmd = Command::new(env.platform_tools_path().join("adb"));
    cmd.arg("devices").envs(env.explicit_env());

    super::check_authorized(&cmd.output()?)
        .map(|raw_list| {
            regex_multi_line!(ADB_DEVICE_REGEX)
                .captures_iter(&raw_list)
                .map(|caps| {
                    assert_eq!(caps.len(), 2);
                    let serial_no = caps.get(1).unwrap().as_str().to_owned();
                    let model = get_prop(env, &serial_no, "ro.product.model")
                        .map_err(Error::ModelFailed)?;
                    let name = device_name(env, &serial_no).unwrap_or_else(|_| model.clone());
                    let abi = get_prop(env, &serial_no, "ro.product.cpu.abi")
                        .map_err(Error::AbiFailed)?;
                    let target =
                        Target::for_abi(&abi).ok_or_else(|| Error::AbiInvalid(abi.clone()))?;
                    Ok(Device::new(serial_no, name, model, target))
                })
                .collect()
        })
        .map_err(Error::DevicesFailed)?
}

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

    #[rstest(input, devices,
        case("* daemon not running; starting now at tcp:5020\n\
            * daemon started successfully\n\
            List of devices attached\n\
            AB1234DEFG\tdevice\n\
            192.168.100.103:55555\tdevice\n\
            ", vec!["AB1234DEFG", "192.168.100.103:55555"]
        ),
        case("List of devices attached \n", vec![]),
        case("** daemon not running; starting now at tcp:5037\n\
            * daemon started successfully\n\
            List of devices attached\n\
            emulator-5556	device product:sdk_google_phone_x86_64 model:Android_SDK_built_for_x86_64 device:generic_x86_64\n\
            emulator-5554	device product:sdk_google_phone_x86 model:Android_SDK_built_for_x86 device:generic_x86\n\
            0a388e93	device usb:1-1 product:razor model:Nexus_7 device:flo\n\
            ", vec!["emulator-5556", "emulator-5554", "0a388e93"]
        ),

    )]
    fn test_adb_output_regex(input: &str, devices: Vec<&'static str>) {
        let regex = regex_multi_line!(ADB_DEVICE_REGEX);
        println!("{}", input);
        let captures = regex
            .captures_iter(input)
            .map(|x| x.get(1).unwrap().as_str())
            .collect::<Vec<_>>();
        assert_eq!(captures, devices);
    }
}