#!/usr/bin/env python3
"""
通过 USB Type-C 连接的 Android 设备：提取 IMEI（或 Android ID/序列号）并输出其 MD5。

用法（在电脑上运行，手机用 Type-C 连上并开启 USB 调试）：
  python get_imei_md5.py
  python get_imei_md5.py -s <device_serial>   # 多设备时指定序列号

依赖：已安装 adb 且设备已授权。
"""

from __future__ import annotations

import argparse
import hashlib
import re
import subprocess
import sys
from typing import Optional


def run_adb(serial: Optional[str], *args: str, timeout: int = 10) -> subprocess.CompletedProcess:
    cmd = ["adb"]
    if serial:
        cmd.extend(["-s", serial])
    cmd.extend(args)
    return subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)


def get_first_device() -> Optional[str]:
    r = run_adb(None, "devices")
    if r.returncode != 0:
        return None
    for line in r.stdout.splitlines()[1:]:
        line = line.strip()
        if not line or line.startswith("*"):
            continue
        parts = line.split()
        if len(parts) >= 2 and parts[1] == "device":
            return parts[0]
    return None


def get_imei_via_dumpsys(serial: Optional[str]) -> Optional[str]:
    r = run_adb(serial, "shell", "dumpsys iphonesubinfo 2>/dev/null")
    if r.returncode != 0 or not r.stdout:
        return None
    # 匹配 "Device ID = xxx" 或 "Imei = xxx"
    for pattern in (r"Device ID\s*=\s*(\S+)", r"Imei\s*=\s*(\S+)", r"IMEI\s*=\s*(\S+)"):
        m = re.search(pattern, r.stdout, re.IGNORECASE)
        if m:
            raw = m.group(1).strip()
            if raw.isdigit() and 14 <= len(raw) <= 16:
                return raw
    return None


def get_imei_via_service_call(serial: Optional[str]) -> Optional[str]:
    r = run_adb(serial, "shell", "service call iphonesubinfo 1 2>/dev/null")
    if r.returncode != 0 or "Parcel" not in r.stdout:
        return None
    # 解析 Parcel 中的 UTF-16 字符串（常见格式）
    hex_part = re.sub(r"\s+", "", r.stdout)
    hex_match = re.search(r"([0-9a-fA-F]{8})([0-9a-fA-F]+)", hex_part)
    if not hex_match:
        return None
    try:
        hex_str = hex_match.group(2)
        if len(hex_str) % 4 != 0:
            return None
        s = "".join(
            chr(int(hex_str[i : i + 4], 16))
            for i in range(0, len(hex_str), 4)
            if int(hex_str[i : i + 4], 16) < 0x10000
        )
        s = s.strip("\x00 ")
        if s.isdigit() and 14 <= len(s) <= 16:
            return s
    except Exception:
        pass
    return None


def get_android_id(serial: Optional[str]) -> Optional[str]:
    r = run_adb(serial, "shell", "settings get secure android_id")
    if r.returncode != 0:
        return None
    raw = (r.stdout or "").strip()
    return raw if raw and raw != "null" else None


def get_serial_no(serial: Optional[str]) -> Optional[str]:
    r = run_adb(serial, "shell", "getprop ro.serialno")
    if r.returncode != 0:
        return None
    raw = (r.stdout or "").strip()
    return raw if raw else None


def md5_hex(s: str) -> str:
    return hashlib.md5(s.encode("utf-8")).hexdigest()


def main() -> None:
    parser = argparse.ArgumentParser(description="从 USB 连接的 Android 设备提取 IMEI/ID 并输出 MD5")
    parser.add_argument("-s", "--serial", help="设备序列号（多设备时指定）")
    parser.add_argument("-q", "--quiet", action="store_true", help="仅输出 MD5 一行")
    args = parser.parse_args()
    serial = args.serial

    if not serial:
        serial = get_first_device()
    if not serial:
        print("错误：未检测到已连接的 Android 设备。请用 Type-C 连接并开启 USB 调试。", file=sys.stderr)
        sys.exit(1)

    raw_id: Optional[str] = None
    source = ""

    raw_id = get_imei_via_dumpsys(serial)
    if raw_id:
        source = "IMEI (dumpsys)"

    if not raw_id:
        raw_id = get_imei_via_service_call(serial)
        if raw_id:
            source = "IMEI (service call)"

    if not raw_id:
        raw_id = get_android_id(serial)
        if raw_id:
            source = "Android ID"

    if not raw_id:
        raw_id = get_serial_no(serial)
        if raw_id:
            source = "ro.serialno"

    if not raw_id:
        print("错误：无法获取 IMEI/Android ID/序列号（需设备已授权且部分机型需 root 才能读 IMEI）。", file=sys.stderr)
        sys.exit(2)

    md5_val = md5_hex(raw_id)
    if args.quiet:
        print(md5_val)
    else:
        print(f"设备: {serial}")
        print(f"原始 ID 来源: {source}")
        print(f"原始 ID: {raw_id}")
        print(f"MD5: {md5_val}")
    sys.exit(0)


if __name__ == "__main__":
    main()
