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
11static ANDROID_VERSION_CACHE: Lazy<Mutex<HashMap<String, f32>>> = Lazy::new(|| {
13 Mutex::new(HashMap::new())
14});
15
16static PID_CACHE: Lazy<Mutex<HashMap<String, (i32, Instant)>>> = Lazy::new(|| {
18 Mutex::new(HashMap::new())
19});
20
21const PID_CACHE_TIMEOUT: Duration = Duration::from_secs(3);
23
24impl ADB {
25 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 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 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 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") .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 for line in stdout.lines().skip(1) {
93 if line.trim().is_empty() {
94 continue;
95 }
96
97 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 let mut device = crate::device::ADBDevice::new(&id, status);
106
107 if parts.len() > 2 {
109 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 device = device.with_name(model);
116 }
117
118 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 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 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 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 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 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 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 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 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 if !device_id.is_empty() {
270 cmd.arg("-s").arg(device_id);
271 }
272
273 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 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 pub fn find_device_by_ip(&self, ip: &str) -> ADBResult<Option<String>> {
291 let devices = self.list_devices()?;
293
294 for device in devices {
296 if device.id.contains(ip) {
297 return Ok(Some(device.id));
298 }
299 }
300
301 Ok(None)
303 }
304
305 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 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 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 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 pub fn restart_server(&self) -> ADBResult<()> {
340 self.with_retry(|| {
341 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 }
352
353 std::thread::sleep(Duration::from_millis(500));
355
356 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 std::thread::sleep(Duration::from_millis(1000));
374
375 Ok(())
376 })
377 }
378
379 pub fn run_command(&self, args: &[&str]) -> ADBResult<String> {
381 self.with_retry(|| {
382 let mut cmd = Command::new(&self.config.path);
383
384 if let Some(additional_args) = &self.config.additional_args {
386 for arg in additional_args {
387 cmd.arg(arg);
388 }
389 }
390
391 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 pub fn wait_for_device(&self, device_id: &str, timeout_ms: Option<u64>) -> ADBResult<bool> {
423 let timeout = timeout_ms.unwrap_or(30000); let poll_interval = 500; 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 { 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 pub fn get_server_version(&self) -> ADBResult<u32> {
450 let output = self.run_command(&["version"])?;
451
452 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 let version = major * 10000 + minor * 100 + patch;
461 return Ok(version);
462 }
463
464 Err(ADBError::CommandError("无法解析 ADB 服务器版本".to_string()))
465 }
466
467 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 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 let android_version = self.get_android_version(device_id)?;
483
484 if android_version >= 8.0 {
485 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 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 let ps_command = if android_version >= 7.0 {
502 format!("ps -A | grep {} | grep -v grep", package_name)
504 } else {
505 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 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 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 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 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 pub fn is_package_running_optimized(
558 &self,
559 device_id: &str,
560 package_name: &str,
561 ) -> ADBResult<(bool, Option<i32>)> {
562 if let Ok(Some(pid)) = self.get_pid_optimized(device_id, package_name) {
564 return Ok((true, Some(pid)));
565 }
566
567 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 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 fn get_android_version(&self, device_id: &str) -> ADBResult<f32> {
590 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 let output = self.shell(device_id, "getprop ro.build.version.release")?;
599 let version_str = output.trim();
600
601 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 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 if let Ok(mut cache) = ANDROID_VERSION_CACHE.lock() {
620 cache.insert(device_id.to_string(), version);
621 }
622
623 Ok(version)
624 }
625}