|
11 | 11 | import zipfile |
12 | 12 | from dataclasses import dataclass |
13 | 13 | from pathlib import Path |
| 14 | +import re |
14 | 15 | from typing import Iterable, List, Optional |
15 | 16 |
|
16 | 17 | DEFAULT_TIMEOUT = 15 |
| 18 | +DEFAULT_TCPIP_PORT = 5555 |
17 | 19 |
|
18 | 20 |
|
19 | 21 | @dataclass |
@@ -330,12 +332,124 @@ def find_authorized_device( |
330 | 332 | "ADBError", |
331 | 333 | "ADBNotFoundError", |
332 | 334 | "DEFAULT_TIMEOUT", |
| 335 | + "DEFAULT_TCPIP_PORT", |
333 | 336 | "DeviceSelectionError", |
334 | 337 | "DeviceState", |
| 338 | + "activate_tcpip_mode", |
| 339 | + "connect_wifi_device", |
| 340 | + "disconnect_device", |
335 | 341 | "find_authorized_device", |
| 342 | + "is_wifi_serial", |
336 | 343 | "list_devices", |
337 | 344 | "parse_devices_output", |
| 345 | + "pair_device", |
338 | 346 | "resolve_adb_path", |
339 | 347 | "run_command", |
340 | 348 | "start_server", |
341 | 349 | ] |
| 350 | + |
| 351 | + |
| 352 | +WIFI_SERIAL_PATTERN = re.compile(r"^[\w.-]+:\d+$") |
| 353 | + |
| 354 | + |
| 355 | +def is_wifi_serial(serial: str) -> bool: |
| 356 | + """Return True when the serial refers to a network-connected device.""" |
| 357 | + |
| 358 | + if not serial: |
| 359 | + return False |
| 360 | + if serial.startswith("emulator-"): |
| 361 | + return False |
| 362 | + return bool(WIFI_SERIAL_PATTERN.match(serial)) |
| 363 | + |
| 364 | + |
| 365 | +def activate_tcpip_mode( |
| 366 | + adb_path: str, |
| 367 | + *, |
| 368 | + device_serial: Optional[str] = None, |
| 369 | + port: int = DEFAULT_TCPIP_PORT, |
| 370 | + timeout: int = DEFAULT_TIMEOUT, |
| 371 | +) -> None: |
| 372 | + """Ask a connected device to listen for TCP/IP debugging connections.""" |
| 373 | + |
| 374 | + run_command( |
| 375 | + adb_path, |
| 376 | + ["tcpip", str(port)], |
| 377 | + device_serial=device_serial, |
| 378 | + timeout=timeout, |
| 379 | + check=True, |
| 380 | + ) |
| 381 | + |
| 382 | + |
| 383 | +def pair_device( |
| 384 | + adb_path: str, |
| 385 | + host_with_port: str, |
| 386 | + pairing_code: str, |
| 387 | + *, |
| 388 | + timeout: int = DEFAULT_TIMEOUT, |
| 389 | +) -> None: |
| 390 | + """Pair with a device over Wi-Fi using the provided pairing code.""" |
| 391 | + |
| 392 | + result = run_command( |
| 393 | + adb_path, |
| 394 | + ["pair", host_with_port, pairing_code], |
| 395 | + timeout=timeout, |
| 396 | + check=False, |
| 397 | + ) |
| 398 | + |
| 399 | + if result.returncode != 0: |
| 400 | + raise ADBCommandError( |
| 401 | + "Failed to pair with Wi-Fi device", |
| 402 | + returncode=result.returncode, |
| 403 | + stdout=result.stdout, |
| 404 | + stderr=result.stderr, |
| 405 | + ) |
| 406 | + |
| 407 | + |
| 408 | +def connect_wifi_device( |
| 409 | + adb_path: str, |
| 410 | + host_with_port: str, |
| 411 | + *, |
| 412 | + timeout: int = DEFAULT_TIMEOUT, |
| 413 | +) -> DeviceState: |
| 414 | + """Connect to a Wi-Fi adb endpoint and return the resulting device state.""" |
| 415 | + |
| 416 | + result = run_command( |
| 417 | + adb_path, |
| 418 | + ["connect", host_with_port], |
| 419 | + timeout=timeout, |
| 420 | + check=False, |
| 421 | + ) |
| 422 | + |
| 423 | + if result.returncode != 0: |
| 424 | + raise ADBCommandError( |
| 425 | + "Failed to connect to Wi-Fi device", |
| 426 | + returncode=result.returncode, |
| 427 | + stdout=result.stdout, |
| 428 | + stderr=result.stderr, |
| 429 | + ) |
| 430 | + |
| 431 | + refreshed = list_devices(adb_path, timeout=timeout) |
| 432 | + for device in refreshed: |
| 433 | + if device.serial == host_with_port: |
| 434 | + return device |
| 435 | + |
| 436 | + raise DeviceSelectionError( |
| 437 | + "Connected to Wi-Fi device but it did not appear in the device list", |
| 438 | + devices=refreshed, |
| 439 | + ) |
| 440 | + |
| 441 | + |
| 442 | +def disconnect_device( |
| 443 | + adb_path: str, |
| 444 | + host_with_port: str, |
| 445 | + *, |
| 446 | + timeout: int = DEFAULT_TIMEOUT, |
| 447 | +) -> None: |
| 448 | + """Disconnect a specific adb endpoint.""" |
| 449 | + |
| 450 | + run_command( |
| 451 | + adb_path, |
| 452 | + ["disconnect", host_with_port], |
| 453 | + timeout=timeout, |
| 454 | + check=False, |
| 455 | + ) |
0 commit comments