adb_kit/
cmd.rs

1use crate::device::ADB;
2use crate::error::{ADBError, ADBResult};
3use log::{debug, info, trace, warn};
4use std::collections::HashMap;
5use std::process::Command;
6use std::str;
7use std::sync::Mutex;
8use std::time::{Duration, Instant};
9use once_cell::sync::Lazy;
10
11// 缓存 Android 版本号
12static ANDROID_VERSION_CACHE: Lazy<Mutex<HashMap<String, f32>>> = Lazy::new(|| {
13    Mutex::new(HashMap::new())
14});
15
16// 缓存 PID 信息
17static PID_CACHE: Lazy<Mutex<HashMap<String, (i32, Instant)>>> = Lazy::new(|| {
18    Mutex::new(HashMap::new())
19});
20
21// 缓存超时时间(3秒)
22const PID_CACHE_TIMEOUT: Duration = Duration::from_secs(3);
23
24impl ADB {
25    /// 使用指数退避策略重试操作
26    pub fn with_retry<F, T>(&self, f: F) -> ADBResult<T>
27    where
28        F: Fn() -> ADBResult<T>,
29    {
30        crate::utils::retry_with_backoff(self.config.max_retries, self.config.retry_delay, f)
31    }
32
33    /// 带超时的操作执行
34    pub fn with_timeout<F, T>(&self, f: F) -> ADBResult<T>
35    where
36        F: FnOnce() -> ADBResult<T> + Send + 'static,
37        T: Send + 'static,
38    {
39        crate::utils::with_timeout(self.config.timeout, f)
40    }
41
42    /// 检查 ADB 是否可用并获取版本
43    pub fn check_adb(&self) -> ADBResult<String> {
44        self.with_retry(|| {
45            let output = Command::new(&self.config.path)
46                .arg("version")
47                .output()
48                .map_err(|e| ADBError::CommandError(format!("无法执行 ADB: {}", e)))?;
49
50            if !output.status.success() {
51                let stderr = String::from_utf8_lossy(&output.stderr);
52                return Err(ADBError::CommandError(format!(
53                    "ADB 命令失败: {}",
54                    stderr
55                )));
56            }
57
58            let stdout = String::from_utf8_lossy(&output.stdout);
59            let version_line = stdout
60                .lines()
61                .next()
62                .ok_or_else(|| ADBError::CommandError("无法解析 ADB 版本".to_string()))?;
63
64            debug!("ADB 版本检查成功: {}", version_line);
65            Ok(version_line.to_string())
66        })
67    }
68
69    /// 列出可用设备
70    pub fn list_devices(&self) -> ADBResult<Vec<crate::device::ADBDevice>> {
71        self.with_retry(|| {
72            let output = Command::new(&self.config.path)
73                .arg("devices")
74                .arg("-l") // 长格式以获取更多详细信息
75                .output()
76                .map_err(|e| ADBError::CommandError(format!("无法执行 ADB: {}", e)))?;
77
78            if !output.status.success() {
79                let stderr = String::from_utf8_lossy(&output.stderr);
80                return Err(ADBError::CommandError(format!(
81                    "ADB devices 命令失败: {}",
82                    stderr
83                )));
84            }
85
86            let stdout = String::from_utf8_lossy(&output.stdout);
87            let mut devices = Vec::new();
88
89            trace!("ADB devices 输出: {}", stdout);
90
91            // 跳过第一行(标题)
92            for line in stdout.lines().skip(1) {
93                if line.trim().is_empty() {
94                    continue;
95                }
96
97                // 解析设备行
98                let parts: Vec<&str> = line.split_whitespace().collect();
99                if parts.len() >= 2 {
100                    let id = parts[0].to_string();
101                    let status_str = parts[1];
102                    let status = crate::device::DeviceStatus::from(status_str);
103
104                    // 创建基础设备
105                    let mut device = crate::device::ADBDevice::new(&id, status);
106
107                    // 提取设备名称和其他属性
108                    if parts.len() > 2 {
109                        // 提取设备型号
110                        if let Some(model_part) = parts.iter().find(|p| p.starts_with("model:")) {
111                            let model = model_part.trim_start_matches("model:");
112                            device = device.with_model(model);
113
114                            // 使用型号作为设备名称
115                            device = device.with_name(model);
116                        }
117
118                        // 提取产品信息
119                        if let Some(product_part) = parts.iter().find(|p| p.starts_with("product:")) {
120                            let product = product_part.trim_start_matches("product:");
121                            device = device.with_product(product);
122                        }
123
124                        // 提取传输 ID
125                        if let Some(transport_part) = parts.iter().find(|p| p.starts_with("transport_id:")) {
126                            let transport = transport_part.trim_start_matches("transport_id:");
127                            device = device.with_transport_id(transport);
128                        }
129                    }
130
131                    // 如果名称还是默认的设备 ID,尝试获取更好的名称
132                    if device.name == format!("Device {}", id) && device.is_online() {
133                        if let Ok(model) = self.shell(&id, "getprop ro.product.model") {
134                            let model = model.trim();
135                            if !model.is_empty() {
136                                device = device.with_name(model);
137                            }
138                        }
139                    }
140
141                    devices.push(device);
142                }
143            }
144
145            info!("发现 {} 个 ADB 设备", devices.len());
146            Ok(devices)
147        })
148    }
149
150    /// 连接到远程设备
151    pub fn connect(&self, ip: &str, port: u16) -> ADBResult<()> {
152        self.with_retry(|| {
153            let output = Command::new(&self.config.path)
154                .arg("connect")
155                .arg(format!("{}:{}", ip, port))
156                .output()
157                .map_err(|e| {
158                    ADBError::CommandError(format!("无法连接到远程设备: {}", e))
159                })?;
160
161            let stdout = String::from_utf8_lossy(&output.stdout).to_string();
162            let stderr = String::from_utf8_lossy(&output.stderr).to_string();
163
164            if !output.status.success() || stdout.contains("failed") || stdout.contains("unable") {
165                let error_msg = if !stderr.is_empty() {
166                    format!("ADB 连接失败: {}", stderr)
167                } else {
168                    format!("ADB 连接失败: {}", stdout)
169                };
170                return Err(ADBError::CommandError(error_msg));
171            }
172
173            info!("成功连接到远程设备 {}:{}", ip, port);
174            Ok(())
175        })
176    }
177
178    /// 断开与远程设备的连接
179    pub fn disconnect(&self, ip: &str, port: Option<u16>) -> ADBResult<()> {
180        self.with_retry(|| {
181            let mut cmd = Command::new(&self.config.path);
182            cmd.arg("disconnect");
183
184            if let Some(p) = port {
185                cmd.arg(format!("{}:{}", ip, p));
186            } else {
187                cmd.arg(ip);
188            }
189
190            let output = cmd.output().map_err(|e| {
191                ADBError::CommandError(format!("无法断开与远程设备的连接: {}", e))
192            })?;
193
194            if !output.status.success() {
195                let stderr = String::from_utf8_lossy(&output.stderr);
196                return Err(ADBError::CommandError(format!(
197                    "ADB 断开连接失败: {}",
198                    stderr
199                )));
200            }
201
202            info!("成功断开与远程设备 {} 的连接", ip);
203            Ok(())
204        })
205    }
206
207    /// 断开所有远程连接
208    pub fn disconnect_all(&self) -> ADBResult<()> {
209        self.with_retry(|| {
210            let output = Command::new(&self.config.path)
211                .arg("disconnect")
212                .output()
213                .map_err(|e| {
214                    ADBError::DeviceError(format!("无法断开所有设备连接: {}", e))
215                })?;
216
217            if !output.status.success() {
218                let stderr = String::from_utf8_lossy(&output.stderr);
219                return Err(ADBError::DeviceError(format!(
220                    "ADB 断开所有连接失败: {}",
221                    stderr
222                )));
223            }
224
225            debug!("成功断开与所有远程设备的连接");
226            Ok(())
227        })
228    }
229
230    /// 在设备上执行 shell 命令
231    pub fn shell(&self, device_id: &str, command: &str) -> ADBResult<String> {
232        self.with_retry(|| {
233            let mut cmd = Command::new(&self.config.path);
234
235            // 添加设备 ID
236            if !device_id.is_empty() {
237                cmd.arg("-s").arg(device_id);
238            }
239
240            let output = cmd.arg("shell").arg(command).output().map_err(|e| {
241                ADBError::DeviceError(format!("无法执行 ADB shell: {}", e))
242            })?;
243
244            let stdout = String::from_utf8_lossy(&output.stdout).to_string();
245            let stderr = String::from_utf8_lossy(&output.stderr).to_string();
246
247            if !output.status.success() {
248                return Err(ADBError::DeviceError(format!(
249                    "ADB shell 命令失败: {}",
250                    stderr
251                )));
252            }
253
254            if !stderr.is_empty() {
255                warn!("ADB shell 命令产生了 stderr 输出: {}", stderr);
256            }
257
258            trace!("Shell 命令 '{}' 输出: {}", command, stdout);
259            Ok(stdout)
260        })
261    }
262
263    /// 执行 shell 命令但不等待完成
264    pub fn shell_no_wait(&self, device_id: &str, command: &str) -> ADBResult<()> {
265        self.with_retry(|| {
266            let mut cmd = Command::new(&self.config.path);
267
268            // 添加设备 ID
269            if !device_id.is_empty() {
270                cmd.arg("-s").arg(device_id);
271            }
272
273            // 启动进程但不等待
274            let child = cmd.arg("shell").arg(command).spawn().map_err(|e| {
275                ADBError::DeviceError(format!("无法执行 ADB shell: {}", e))
276            })?;
277
278            debug!("在设备 {} 上启动命令: {}", device_id, command);
279
280            // 如果启用了连接池,可以在这里存储子进程
281            if let Ok(mut pool) = self.connections.lock() {
282                pool.insert(format!("{}:{}", device_id, command), std::sync::Arc::new(std::sync::Mutex::new(child)));
283            }
284
285            Ok(())
286        })
287    }
288
289    /// 通过 IP 地址查找设备
290    pub fn find_device_by_ip(&self, ip: &str) -> ADBResult<Option<String>> {
291        // 获取已连接设备列表
292        let devices = self.list_devices()?;
293
294        // 查找包含该 IP 的设备
295        for device in devices {
296            if device.id.contains(ip) {
297                return Ok(Some(device.id));
298            }
299        }
300
301        // 未找到则返回 None
302        Ok(None)
303    }
304
305    /// 获取设备属性
306    pub fn get_prop(&self, device_id: &str, prop_name: &str) -> ADBResult<String> {
307        let command = format!("getprop {}", prop_name);
308        let output = self.shell(device_id, &command)?;
309        Ok(output.trim().to_string())
310    }
311
312    /// 设置设备属性
313    pub fn set_prop(&self, device_id: &str, prop_name: &str, prop_value: &str) -> ADBResult<()> {
314        let command = format!("setprop {} {}", prop_name, prop_value);
315        self.shell(device_id, &command)?;
316        Ok(())
317    }
318
319    /// 获取设备所有属性
320    pub fn get_all_props(&self, device_id: &str) -> ADBResult<HashMap<String, String>> {
321        let output = self.shell(device_id, "getprop")?;
322        Ok(crate::utils::parse_properties(&output))
323    }
324
325    /// 检查设备是否在线
326    pub fn is_device_online(&self, device_id: &str) -> ADBResult<bool> {
327        let devices = self.list_devices()?;
328
329        for device in devices {
330            if device.id == device_id {
331                return Ok(device.is_online());
332            }
333        }
334
335        Ok(false)
336    }
337
338    /// 重启 ADB 服务器
339    pub fn restart_server(&self) -> ADBResult<()> {
340        self.with_retry(|| {
341            // 首先停止服务器
342            let output = Command::new(&self.config.path)
343                .arg("kill-server")
344                .output()
345                .map_err(|e| ADBError::CommandError(format!("无法停止 ADB 服务器: {}", e)))?;
346
347            if !output.status.success() {
348                let stderr = String::from_utf8_lossy(&output.stderr);
349                warn!("ADB kill-server 可能失败: {}", stderr);
350                // 我们继续尝试启动服务器,即使停止可能失败
351            }
352
353            // 短暂延迟,确保服务器停止
354            std::thread::sleep(Duration::from_millis(500));
355
356            // 启动服务器
357            let output = Command::new(&self.config.path)
358                .arg("start-server")
359                .output()
360                .map_err(|e| ADBError::CommandError(format!("无法启动 ADB 服务器: {}", e)))?;
361
362            if !output.status.success() {
363                let stderr = String::from_utf8_lossy(&output.stderr);
364                return Err(ADBError::CommandError(format!(
365                    "ADB start-server 失败: {}",
366                    stderr
367                )));
368            }
369
370            info!("成功重启 ADB 服务器");
371
372            // 短暂延迟,确保服务器启动完成
373            std::thread::sleep(Duration::from_millis(1000));
374
375            Ok(())
376        })
377    }
378
379    /// 执行任意 ADB 命令
380    pub fn run_command(&self, args: &[&str]) -> ADBResult<String> {
381        self.with_retry(|| {
382            let mut cmd = Command::new(&self.config.path);
383
384            // 添加全局附加参数(如果有)
385            if let Some(additional_args) = &self.config.additional_args {
386                for arg in additional_args {
387                    cmd.arg(arg);
388                }
389            }
390
391            // 添加命令特定参数
392            for arg in args {
393                cmd.arg(arg);
394            }
395
396            let output = cmd.output().map_err(|e| {
397                ADBError::CommandError(format!("无法执行 ADB 命令: {}", e))
398            })?;
399
400            let stdout = String::from_utf8_lossy(&output.stdout).to_string();
401            let stderr = String::from_utf8_lossy(&output.stderr).to_string();
402
403            if !output.status.success() {
404                let error_msg = if !stderr.is_empty() {
405                    stderr
406                } else {
407                    stdout.clone()
408                };
409
410                return Err(ADBError::CommandError(format!(
411                    "ADB 命令失败: {}",
412                    error_msg
413                )));
414            }
415
416            debug!("ADB 命令执行成功: {:?}", args);
417            Ok(stdout)
418        })
419    }
420
421    /// 等待设备连接
422    pub fn wait_for_device(&self, device_id: &str, timeout_ms: Option<u64>) -> ADBResult<bool> {
423        let timeout = timeout_ms.unwrap_or(30000); // 默认30秒
424        let poll_interval = 500; // 500毫秒
425
426        info!("等待设备 {} 连接...", device_id);
427
428        let result = crate::utils::wait_with_polling(
429            timeout,
430            poll_interval,
431            || self.is_device_online(device_id),
432            Some(|elapsed| {
433                if elapsed % 5000 == 0 { // 每5秒打印一次日志
434                    debug!("等待设备 {} 连接,已等待 {}s...", device_id, elapsed / 1000);
435                }
436            })
437        )?;
438
439        if result {
440            info!("设备 {} 已连接", device_id);
441        } else {
442            warn!("等待设备 {} 连接超时", device_id);
443        }
444
445        Ok(result)
446    }
447
448    /// 获取 ADB 服务器版本
449    pub fn get_server_version(&self) -> ADBResult<u32> {
450        let output = self.run_command(&["version"])?;
451
452        // 尝试从输出中提取版本号
453        let re = regex::Regex::new(r"Android Debug Bridge version (\d+)\.(\d+)\.(\d+)").unwrap();
454        if let Some(caps) = re.captures(&output) {
455            let major: u32 = caps[1].parse().unwrap_or(0);
456            let minor: u32 = caps[2].parse().unwrap_or(0);
457            let patch: u32 = caps[3].parse().unwrap_or(0);
458
459            // 转换为一个整数表示
460            let version = major * 10000 + minor * 100 + patch;
461            return Ok(version);
462        }
463
464        Err(ADBError::CommandError("无法解析 ADB 服务器版本".to_string()))
465    }
466
467    /// 优化版的进程 ID 获取
468    pub fn get_pid_optimized(&self, device_id: &str, package_name: &str) -> ADBResult<Option<i32>> {
469        let cache_key = format!("{}:{}", device_id, package_name);
470
471        // 检查缓存
472        if let Ok(cache) = PID_CACHE.lock() {
473            if let Some((pid, timestamp)) = cache.get(&cache_key) {
474                if Instant::now().duration_since(*timestamp) < PID_CACHE_TIMEOUT {
475                    trace!("使用缓存的 PID: {} -> {}", package_name, pid);
476                    return Ok(Some(*pid));
477                }
478            }
479        }
480
481        // 在 Android 8+ 系统上,首选 pidof 命令
482        let android_version = self.get_android_version(device_id)?;
483
484        if android_version >= 8.0 {
485            // 使用 pidof(Android 8+ 的首选方法)
486            let command = format!("pidof {}", package_name);
487            let output = self.shell(device_id, &command)?;
488
489            if !output.trim().is_empty() {
490                if let Ok(pid) = output.trim().parse::<i32>() {
491                    // 更新缓存
492                    if let Ok(mut cache) = PID_CACHE.lock() {
493                        cache.insert(cache_key, (pid, Instant::now()));
494                    }
495                    return Ok(Some(pid));
496                }
497            }
498        }
499
500        // 尝试使用 ps 命令(更通用的方法)
501        let ps_command = if android_version >= 7.0 {
502            // Android 7+ 系统使用不同的 ps 格式
503            format!("ps -A | grep {} | grep -v grep", package_name)
504        } else {
505            // 较旧的 Android 版本使用传统 ps 格式
506            format!("ps | grep {} | grep -v grep", package_name)
507        };
508
509        let output = self.shell(device_id, &ps_command)?;
510
511        if !output.trim().is_empty() {
512            let lines = output.lines().collect::<Vec<&str>>();
513
514            for line in lines {
515                let parts: Vec<&str> = line.split_whitespace().collect();
516
517                // 确定 PID 位置(根据 Android 版本有所不同)
518                let pid_index = if android_version >= 7.0 { 1 } else { 2 };
519
520                if parts.len() > pid_index {
521                    if let Ok(pid) = std::str::FromStr::from_str(parts[pid_index]) {
522                        // 更新缓存
523                        if let Ok(mut cache) = PID_CACHE.lock() {
524                            cache.insert(cache_key, (pid, Instant::now()));
525                        }
526                        return Ok(Some(pid));
527                    }
528                }
529            }
530        }
531
532        // 最后的尝试 - 使用 dumpsys
533        let dumpsys_command = format!("dumpsys activity services | grep -i {}", package_name);
534        let output = self.shell(device_id, &dumpsys_command)?;
535
536        if !output.trim().is_empty() {
537            let re = regex::Regex::new(r"pid=(\d+)")?;
538
539            if let Some(caps) = re.captures(&output) {
540                if let Some(pid_match) = caps.get(1) {
541                    if let Ok(pid) = std::str::FromStr::from_str(pid_match.as_str()) {
542                        // 更新缓存
543                        if let Ok(mut cache) = PID_CACHE.lock() {
544                            cache.insert(cache_key, (pid, Instant::now()));
545                        }
546                        return Ok(Some(pid));
547                    }
548                }
549            }
550        }
551
552        debug!("无法找到包 {} 的 PID", package_name);
553        Ok(None)
554    }
555
556    /// 检查包是否运行的优化版本
557    pub fn is_package_running_optimized(
558        &self,
559        device_id: &str,
560        package_name: &str,
561    ) -> ADBResult<(bool, Option<i32>)> {
562        // 直接使用优化版的 PID 获取方法
563        if let Ok(Some(pid)) = self.get_pid_optimized(device_id, package_name) {
564            return Ok((true, Some(pid)));
565        }
566
567        // 检查前台应用
568        let current_app_cmd = "dumpsys window windows | grep -E 'mCurrentFocus|mFocusedApp'";
569        let current_app = self.shell(device_id, current_app_cmd)?;
570
571        if current_app.contains(package_name) {
572            debug!("通过前台应用检查确认 {} 正在运行", package_name);
573            return Ok((true, None));
574        }
575
576        // 最后一次尝试 - 检查服务
577        let service_cmd = format!("dumpsys activity services | grep -i {}", package_name);
578        let service_output = self.shell(device_id, &service_cmd)?;
579
580        if !service_output.trim().is_empty() {
581            debug!("通过服务检查确认 {} 正在运行", package_name);
582            return Ok((true, None));
583        }
584
585        Ok((false, None))
586    }
587
588    /// 获取设备的 Android 版本
589    fn get_android_version(&self, device_id: &str) -> ADBResult<f32> {
590        // 先检查缓存
591        if let Ok(cache) = ANDROID_VERSION_CACHE.lock() {
592            if let Some(version) = cache.get(device_id) {
593                return Ok(*version);
594            }
595        }
596
597        // 如果缓存中没有,则查询设备
598        let output = self.shell(device_id, "getprop ro.build.version.release")?;
599        let version_str = output.trim();
600
601        // 解析版本号
602        let version = match version_str.split('.').next() {
603            Some(major) => {
604                if let Ok(major_num) = major.parse::<f32>() {
605                    major_num
606                } else {
607                    // 默认返回一个保守的版本号
608                    warn!("无法解析 Android 版本 '{}', 默认使用 5.0", version_str);
609                    5.0
610                }
611            },
612            None => {
613                warn!("无法获取 Android 版本, 默认使用 5.0");
614                5.0
615            }
616        };
617
618        // 更新缓存
619        if let Ok(mut cache) = ANDROID_VERSION_CACHE.lock() {
620            cache.insert(device_id.to_string(), version);
621        }
622
623        Ok(version)
624    }
625}