现在重写 scanForAdbPort，用并发扫描：

patch

    private int scanForAdbPort() {
        Log.i(TAG, "Starting optimized port scan...");
        SharedPreferences prefs = getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
        int lastPort = prefs.getInt(KEY_LAST_PORT, -1);

        // MOD: Always use localhost for scanning
        String scanHost = "127.0.0.1";
        Log.i(TAG, "Scanning localhost for ADB ports");

        AdbHelper adbHelper = new AdbHelper(this);

        // Try last known port first with full ADB connection
        if (lastPort > 0) {
            Log.i(TAG, "Trying last known port: " + lastPort);
            if (isPortOpen(scanHost, lastPort) && adbHelper.connect(scanHost, lastPort)) {
                Log.i(TAG, "Found ADB on previously used port: " + lastPort);
                return lastPort;
            }
        }

        // MOD: Match adb-tunnel script scan range: 5555-5560 + 30000-50000
        java.util.List<Integer> openPorts = new java.util.ArrayList<>();

        Log.i(TAG, "Phase 1: Quick TCP port scan (5555-5560, 30000-50000)...");
        updateNotification("Scanning for open ports...");

        // First check 5555-5560
        for (int port = 5555; port <= 5560; port++) {
            if (isPortOpen(scanHost, port)) {
                Log.i(TAG, "Found open port: " + port);
                openPorts.add(port);
            }
        }

        // Then scan 30000-50000
        for (int port = 30000; port <= 50000; port++) {
            if (isPortOpen(scanHost, port)) {
                Log.i(TAG, "Found open port: " + port);
                openPorts.add(port);
            }
        }

        Log.i(TAG, "Phase 1 complete. Found " + openPorts.size() + " open ports");

        // Phase 2: Try ADB connection on open ports
        Log.i(TAG, "Phase 2: Verifying ADB on open ports...");
        updateNotification("Verifying ADB ports...");

        for (int port : openPorts) {
            Log.i(TAG, "Trying ADB connect on port " + port);
            updateNotification("Trying port " + port + "...");
            if (adbHelper.connect(scanHost, port)) {
                Log.i(TAG, "Found working ADB on port: " + port);
                return port;
            }
        }

        Log.e(TAG, "No working ADB port found");
        return -1;
    }

    private int scanForAdbPort() {
        Log.i(TAG, "Starting optimized port scan (matching adb-tunnel script)...");
        SharedPreferences prefs = getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
        int lastPort = prefs.getInt(KEY_LAST_PORT, -1);

        String scanHost = "127.0.0.1";
        AdbHelper adbHelper = new AdbHelper(this);

        // Try last known port first
        if (lastPort > 0 && lastPort != 5037) {
            Log.i(TAG, "Trying last known port: " + lastPort);
            if (isPortOpen(scanHost, lastPort) && adbHelper.connect(scanHost, lastPort)) {
                Log.i(TAG, "Found ADB on previously used port: " + lastPort);
                return lastPort;
            }
        }

        // Phase 1: Concurrent TCP port scan (like xargs -P 100)
        Log.i(TAG, "Phase 1: Concurrent TCP scan (5555-5560, 30000-50000)...");
        updateNotification("Scanning ports...");

        java.util.List<Integer> openPorts = java.util.Collections.synchronizedList(new java.util.ArrayList<>());
        java.util.concurrent.ExecutorService executor = java.util.concurrent.Executors.newFixedThreadPool(100);
        java.util.concurrent.CountDownLatch latch;

        // Build port list: 5555-5560 + 30000-50000, skip 5037
        java.util.List<Integer> portsToScan = new java.util.ArrayList<>();
        for (int p = 5555; p <= 5560; p++) {
            if (p != 5037) portsToScan.add(p);
        }
        for (int p = 30000; p <= 50000; p++) {
            if (p != 5037) portsToScan.add(p);
        }

        latch = new java.util.concurrent.CountDownLatch(portsToScan.size());

        for (int port : portsToScan) {
            final int p = port;
            executor.submit(() -> {
                try {
                    if (isPortOpen(scanHost, p)) {
                        Log.i(TAG, "Found open port: " + p);
                        openPorts.add(p);
                    }
                } finally {
                    latch.countDown();
                }
            });
        }

        try {
            latch.await(60, java.util.concurrent.TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            Log.e(TAG, "Port scan interrupted", e);
        }
        executor.shutdown();

        // Sort ports (prefer lower ports like 5555-5560)
        java.util.Collections.sort(openPorts);
        Log.i(TAG, "Phase 1 complete. Found " + openPorts.size() + " open ports");

        // Phase 2: Try ADB connection on open ports (like script's for loop)
        Log.i(TAG, "Phase 2: Verifying ADB on open ports...");
        updateNotification("Verifying ADB...");

        for (int port : openPorts) {
            Log.i(TAG, "Trying ADB connect on port " + port);
            updateNotification("Trying port " + port + "...");
            if (adbHelper.connect(scanHost, port)) {
                Log.i(TAG, "Found working ADB on port: " + port);
                return port;
            }
        }

        Log.e(TAG, "No working ADB port found");
        return -1;
    }


--- a//home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbConfigService.java
+++ b//home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbConfigService.java
@@ -419,18 +419,15 @@

     private int scanForAdbPort() {
-        Log.i(TAG, "Starting optimized port scan...");
+        Log.i(TAG, "Starting optimized port scan (matching adb-tunnel script)...");
         SharedPreferences prefs = getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
         int lastPort = prefs.getInt(KEY_LAST_PORT, -1);

-        // MOD: Always use localhost for scanning
         String scanHost = "127.0.0.1";
-        Log.i(TAG, "Scanning localhost for ADB ports");
-
         AdbHelper adbHelper = new AdbHelper(this);

-        // Try last known port first with full ADB connection
-        if (lastPort > 0) {
+        // Try last known port first
+        if (lastPort > 0 && lastPort != 5037) {
             Log.i(TAG, "Trying last known port: " + lastPort);
             if (isPortOpen(scanHost, lastPort) && adbHelper.connect(scanHost, lastPort)) {
                 Log.i(TAG, "Found ADB on previously used port: " + lastPort);
@@ -438,33 +435,53 @@
             }
         }

-        // MOD: Match adb-tunnel script scan range: 5555-5560 + 30000-50000
-        java.util.List<Integer> openPorts = new java.util.ArrayList<>();
-
-        Log.i(TAG, "Phase 1: Quick TCP port scan (5555-5560, 30000-50000)...");
-        updateNotification("Scanning for open ports...");
-
-        // First check 5555-5560
-        for (int port = 5555; port <= 5560; port++) {
-            if (isPortOpen(scanHost, port)) {
-                Log.i(TAG, "Found open port: " + port);
-                openPorts.add(port);
-            }
-        }
-
-        // Then scan 30000-50000
-        for (int port = 30000; port <= 50000; port++) {
-            if (isPortOpen(scanHost, port)) {
-                Log.i(TAG, "Found open port: " + port);
-                openPorts.add(port);
-            }
-        }
-
+        // Phase 1: Concurrent TCP port scan (like xargs -P 100)
+        Log.i(TAG, "Phase 1: Concurrent TCP scan (5555-5560, 30000-50000)...");
+        updateNotification("Scanning ports...");
+
+        java.util.List<Integer> openPorts = java.util.Collections.synchronizedList(new java.util.ArrayList<>());
+        java.util.concurrent.ExecutorService executor = java.util.concurrent.Executors.newFixedThreadPool(100);
+        java.util.concurrent.CountDownLatch latch;
+
+        // Build port list: 5555-5560 + 30000-50000, skip 5037
+        java.util.List<Integer> portsToScan = new java.util.ArrayList<>();
+        for (int p = 5555; p <= 5560; p++) {
+            if (p != 5037) portsToScan.add(p);
+        }
+        for (int p = 30000; p <= 50000; p++) {
+            if (p != 5037) portsToScan.add(p);
+        }
+
+        latch = new java.util.concurrent.CountDownLatch(portsToScan.size());
+
+        for (int port : portsToScan) {
+            final int p = port;
+            executor.submit(() -> {
+                try {
+                    if (isPortOpen(scanHost, p)) {
+                        Log.i(TAG, "Found open port: " + p);
+                        openPorts.add(p);
+                    }
+                } finally {
+                    latch.countDown();
+                }
+            });
+        }
+
+        try {
+            latch.await(60, java.util.concurrent.TimeUnit.SECONDS);
+        } catch (InterruptedException e) {
+            Log.e(TAG, "Port scan interrupted", e);
+        }
+        executor.shutdown();
+
+        // Sort ports (prefer lower ports like 5555-5560)
+        java.util.Collections.sort(openPorts);
         Log.i(TAG, "Phase 1 complete. Found " + openPorts.size() + " open ports");

-        // Phase 2: Try ADB connection on open ports
+        // Phase 2: Try ADB connection on open ports (like script's for loop)
         Log.i(TAG, "Phase 2: Verifying ADB on open ports...");
-        updateNotification("Verifying ADB ports...");
+        updateNotification("Verifying ADB...");

         for (int port : openPorts) {
             Log.i(TAG, "Trying ADB connect on port " + port);

No linter for .java files


验证修改：

terminal

echo "=== 验证新的扫描逻辑 ==="
sed -n '419,500p' ~/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbConfigService.java


Output:
=== 验证新的扫描逻辑 ===

    private int scanForAdbPort() {
        Log.i(TAG, "Starting optimized port scan (matching adb-tunnel script)...");
        SharedPreferences prefs = getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
        int lastPort = prefs.getInt(KEY_LAST_PORT, -1);

        String scanHost = "127.0.0.1";
        AdbHelper adbHelper = new AdbHelper(this);

        // Try last known port first
        if (lastPort > 0 && lastPort != 5037) {
            Log.i(TAG, "Trying last known port: " + lastPort);
            if (isPortOpen(scanHost, lastPort) && adbHelper.connect(scanHost, lastPort)) {
                Log.i(TAG, "Found ADB on previously used port: " + lastPort);
                return lastPort;
            }
        }

        // Phase 1: Concurrent TCP port scan (like xargs -P 100)
        Log.i(TAG, "Phase 1: Concurrent TCP scan (5555-5560, 30000-50000)...");
        updateNotification("Scanning ports...");

        java.util.List<Integer> openPorts = java.util.Collections.synchronizedList(new java.util.ArrayList<>());
        java.util.concurrent.ExecutorService executor = java.util.concurrent.Executors.newFixedThreadPool(100);
        java.util.concurrent.CountDownLatch latch;

        // Build port list: 5555-5560 + 30000-50000, skip 5037
        java.util.List<Integer> portsToScan = new java.util.ArrayList<>();
        for (int p = 5555; p <= 5560; p++) {
            if (p != 5037) portsToScan.add(p);
        }
        for (int p = 30000; p <= 50000; p++) {
            if (p != 5037) portsToScan.add(p);
        }

        latch = new java.util.concurrent.CountDownLatch(portsToScan.size());

        for (int port : portsToScan) {
            final int p = port;
            executor.submit(() -> {
                try {
                    if (isPortOpen(scanHost, p)) {
                        Log.i(TAG, "Found open port: " + p);
                        openPorts.add(p);
                    }
                } finally {
                    latch.countDown();
                }
            });
        }

        try {
            latch.await(60, java.util.concurrent.TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            Log.e(TAG, "Port scan interrupted", e);
        }
        executor.shutdown();

        // Sort ports (prefer lower ports like 5555-5560)
        java.util.Collections.sort(openPorts);
        Log.i(TAG, "Phase 1 complete. Found " + openPorts.size() + " open ports");

        // Phase 2: Try ADB connection on open ports (like script's for loop)
        Log.i(TAG, "Phase 2: Verifying ADB on open ports...");
        updateNotification("Verifying ADB...");

        for (int port : openPorts) {
            Log.i(TAG, "Trying ADB connect on port " + port);
            updateNotification("Trying port " + port + "...");
            if (adbHelper.connect(scanHost, port)) {
                Log.i(TAG, "Found working ADB on port: " + port);
                return port;
            }
        }

        Log.e(TAG, "No working ADB port found");
        return -1;
    }

    // MOD: Quick TCP port check without full ADB handshake
    private boolean isPortOpen(String host, int port) {


现在逻辑和脚本一致了：

| 脚本 | App |
|------|-----|
| `xargs -P 100` 并发 | `ExecutorService(100)` 并发 |
| 范围 5555-5560 + 30000-50000 | ✅ 一致 |
| 跳过 5037 | ✅ 一致 |
| TCP 检测后 adb connect 验证 | ✅ 一致 |
| 60秒超时 | ✅ 一致 |

**可以编译安装测试吗？**


再思考一遍流程有没有问题


让我仔细对比一下：

**脚本流程：**
```bash
1. adb kill-server && adb start-server  # 重启 ADB server
2. xargs -P 100 并发 TCP 检测
3. for port in $PORTS; do
     adb connect localhost:$port      # 连接
     adb devices | grep "device"      # 验证是 device 状态
     adb -s localhost:$PORT tcpip 5555  # 切换
   done
```

**App 流程：**
```java
1. (没有重启 ADB server)
2. ExecutorService(100) 并发 TCP 检测
3. for port in openPorts:
     adbHelper.connect()  # 用 Java ADB 库连接
     return port          # 返回端口
4. adbHelper.switchToPort5555()  # 切换
```

**发现的问题：**

1. **脚本用 shell `adb` 命令，App 用 Java ADB 库** — 这两个走的是不同的认证路径。脚本用 Termux 的 adb 密钥，App 用自己配对的密钥。

2. **脚本检查 `adb devices` 输出确认是 `device` 状态**，App 只检查 connect 是否抛异常 — 可能连接成功但设备状态不对。

3. **脚本先 kill-server 再 start-server**，App 没有这步。

最关键的问题：**App 的 Java ADB 库和 Termux 的 adb 命令是独立的**，它们有各自的配对密钥。脚本能工作是因为 Termux adb 已经配对过了。

**要不要让 App 直接调用 shell 的 adb 命令，而不是用 Java ADB 库？** 这样就能完全复用脚本的逻辑和 Termux 的配对密钥。

Explanation:
让我仔细思考一下整个流程：

**脚本的流程：**
1. `adb kill-server` + `adb start-server` - 重启 ADB server
2. `xargs -P 100` 并发 TCP 检测端口是否开放
3. 对每个开放端口：`adb connect localhost:$port`
4. 检查 `adb devices` 是否显示 `localhost:$port device`
5. 如果成功，执行 `adb -s localhost:$PORT tcpip 5555`
6. 然后 `adb connect localhost:5555`

**App 的流程：**
1. 并发 TCP 检测端口是否开放 (isPortOpen)
2. 对每个开放端口：`adbHelper.connect(scanHost, port)` - 这是用 Java ADB 库连接
3. 如果成功，返回端口号
4. 然后在 configureAdb 里调用 `adbHelper.switchToPort5555()`

问题来了：

1. **脚本用的是 shell 的 `adb connect`**，而 App 用的是 Java ADB 库的 connect。这两个可能行为不一样。

2. **脚本检查的是 `adb devices` 输出**，看是否有 `device` 状态。App 只是检查 connect 是否抛异常。

3. **脚本的 TCP 检测**用的是 `(echo > /dev/tcp/127.0.0.1/$1)`，这是 bash 内置的 TCP 连接测试。App 用的是 Java Socket 连接，应该是等效的。

4. **最关键的问题**：脚本在找到端口后，用的是 `adb -s localhost:$PORT tcpip 5555`，这是通过 shell 命令执行的。而 App 用的是 `adbHelper.switchToPort5555()`，这是通过 Java ADB 库发送 `tcpip:5555` 服务命令。

让我看看 switchToPort5555 的实现：

```java
public boolean switchToPort5555(String host, int port) {
    SimpleAdbManager manager = null;
    try {
        manager = new SimpleAdbManager(context);
        manager.connect(host, port);
        Thread.sleep(200);
        try (AdbStream stream = manager.openStream("tcpip:5555");
             InputStream inputStream = stream.openInputStream()) {
            // 读取响应
        }
        Thread.sleep(3000);
        return true;
    } catch (Exception e) {
        return false;
    }
}
```

这个实现是通过 ADB 协议发送 `tcpip:5555` 服务命令，应该和 `adb tcpip 5555` 是等效的。

但是有一个问题：**App 的 ADB 库需要配对密钥才能连接**。脚本用的是 Termux 里的 adb 命令，它有自己的配对密钥。App 有自己的配对密钥（通过 Web UI 配对的）。

如果 App 的配对密钥有效，那应该能连接。但如果无线调试被关闭再打开，端口会变，而且可能需要重新配对？

不对，配对密钥是持久的，只要配对过一次，之后就可以用这个密钥连接任何端口。

让我再想想还有什么问题...

**另一个问题**：脚本在扫描前会 `adb kill-server` + `adb start-server`，这会重启 ADB server。App 没有这一步。

但这一步可能不是必须的，因为 App 用的是自己的 ADB 库，不依赖系统的 adb server。

**还有一个问题**：脚本的 TCP 检测是检测端口是否开放，但开放不代表是 ADB 端口。可能有其他服务在监听这些端口。所以脚本在 TCP 检测后还要 `adb connect` 验证。

App 也是这样做的：先 isPortOpen 检测，再 adbHelper.connect 验证。

**但是**，我注意到一个问题：App 的 isPortOpen 超时是 100ms，这可能太短了？脚本的 TCP 检测没有明确的超时，bash 的 `/dev/tcp` 默认超时可能更长。

不过 100ms 对于 localhost 应该足够了。

**再想想**...

哦，我发现一个潜在问题：

脚本的流程是：
1. TCP 检测找到开放端口
2. `adb connect localhost:$port` 连接
3. 检查 `adb devices` 确认连接成功
4. 如果成功，`adb tcpip 5555`

App 的流程是：
1. TCP 检测找到开放端口
2. `adbHelper.connect()` 连接并验证
3. 返回端口号
4. 在 configureAdb 里调用 `switchToPort5555()`

问题是：**App 在 scanForAdbPort 里 connect 成功后，会关闭连接（finally 里 manager.close()）**。然后在 switchToPort5555 里又重新连接。

这可能导致问题：如果无线调试端口在两次连接之间变了（虽然不太可能），或者有其他问题。

但更重要的是，我需要确认 App 的 ADB 库是否真的能连接成功。之前测试显示 App 一直卡在 "Scanning for ADB port..."，说明可能是：
1. TCP 扫描没找到开放端口（范围问题，已修复）
2. 或者 TCP 扫描找到了端口，但 ADB 连接验证失败

让我再看看 adbHelper.connect 的实现...

```java
public boolean connect(String host, int port) {
    SimpleAdbManager manager = null;
    try {
        manager = new SimpleAdbManager(context);
        manager.connect(host, port);
        return true;
    } catch (Exception e) {
        Log.e(TAG, "Connect failed", e);
        return false;
    } finally {
        if (manager != null) {
            try {
                manager.close();
            } catch (Exception e) {
                Log.e(TAG, "Error closing manager after connect", e);
            }
        }
    }
}
```

这个实现看起来没问题。如果连接失败会抛异常，返回 false。

**但是**，SimpleAdbManager 的 connect 可能需要配对密钥。如果密钥无效或者没有配对，连接会失败。

之前状态显示 `isPaired: true`，说明 App 认为自己已经配对了。但配对密钥是否有效？

让我想想还有什么问题...

**啊，我想到一个问题**：

脚本用的是 Termux 里的 `adb` 命令，它连接的是 Termux 的 ADB server（端口 5037）。ADB server 会管理所有的 ADB 连接。

App 用的是 Java ADB 库，它直接连接到设备的无线调试端口，不经过 ADB server。

这两种方式应该都能工作，但可能有细微差别。

**另一个问题**：无线调试端口是 TLS 加密的，需要配对密钥。App 的 ADB 库应该会使用配对时保存的密钥。

让我检查一下 App 是否正确保存和使用了配对密钥...

实际上，我觉得最好的方式是先编译测试，看看日志输出，然后根据实际错误来调试。

但用户让我再思考一遍流程，所以我应该指出可能的问题：

1. **并发扫描 20000+ 端口**：100 线程扫描 20006 个端口（6 + 20001），每个端口 100ms 超时，理论上最坏情况需要 20006 / 100 * 0.1 = 20 秒。但实际上大部分端口会立即返回失败（connection refused），所以应该很快。

2. **ADB 连接验证**：如果 TCP 扫描找到很多开放端口（比如其他服务），ADB 连接验证可能会很慢，因为每个端口都要尝试完整的 ADB 握手。

3. **配对密钥**：App 需要有效的配对密钥才能连接。如果配对失效，所有连接都会失败。

4. **状态更新**：之前看到状态一直是 "Scanning for ADB port..."，说明可能卡在某个地方。需要更多日志来定位。

我觉得流程本身没有大问题，主要是需要实际测试看看哪里出错。

让我告诉用户我的分析。


不行，连接并切换的方式要按原版


好，让我看看原版的连接和切换逻辑：

Explanation:
好的，用户说连接并切换的方式要按原版。让我看看原版是怎么做的。

原版的流程应该是：
1. 用 Java ADB 库连接
2. 用 Java ADB 库切换到 5555

但问题是 App 有自己的配对密钥，和 Termux 的 adb 是独立的。

让我再仔细想想流程：

1. App 已经配对过了（isPaired: true）
2. App 有 WRITE_SECURE_SETTINGS 权限
3. App 可以启用无线调试（adb_wifi_enabled=1）
4. 启用后会有一个随机端口
5. App 需要找到这个端口
6. 用自己的配对密钥连接
7. 发送 tcpip:5555 命令

问题可能在于：
- 扫描范围不对（已修复）
- 并发扫描逻辑有问题？
- 或者 App 的配对密钥有问题？

让我再看看原版的扫描逻辑是什么样的。

echo "=== 原版 scanForAdbPort ==="
cd ~/adb-auto-enable-mod && git show HEAD:app/src/main/java/com/tpn/adbautoenable/AdbConfigService.java | grep -A80 "private int scanForAdbPort"


Output:
=== 原版 scanForAdbPort ===
    private int scanForAdbPort() {
        Log.i(TAG, "Starting optimized port scan...");
        SharedPreferences prefs = getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
        int lastPort = prefs.getInt(KEY_LAST_PORT, -1);
        String deviceIP = getDeviceIP();

        Log.i(TAG, "Scanning with device IP: " + deviceIP);

        AdbHelper adbHelper = new AdbHelper(this);

        if (lastPort > 0 && adbHelper.connect(deviceIP, lastPort)) {
            Log.i(TAG, "Found ADB on previously used port: " + lastPort);
            return lastPort;
        }

        int[] commonBases = {37000, 38000, 39000, 40000, 41000, 42000, 35000, 36000, 43000, 44000};
        for (int base : commonBases) {
            Log.i(TAG, "Checking range " + base + "-" + (base + 999) + "...");
            updateNotification("Scanning ports " + base + "...");

            for (int offset = 0; offset < 1000; offset += 5) {
                int port = base + offset;
                if (adbHelper.connect(deviceIP, port)) {
                    Log.i(TAG, "Found ADB on port: " + port);
                    return port;
                }
            }
        }
        return -1;
    }

    private String getDeviceIP() {
        try {
            WifiManager wifiManager = (WifiManager)
                    getApplicationContext().getSystemService(Context.WIFI_SERVICE);
            if (wifiManager == null) {
                return "127.0.0.1";
            }

            int ipAddress = wifiManager.getConnectionInfo().getIpAddress();
            byte[] ipBytes = ByteBuffer.allocate(4)
                    .order(ByteOrder.LITTLE_ENDIAN)
                    .putInt(ipAddress)
                    .array();

            InetAddress inetAddress = InetAddress.getByAddress(ipBytes);
            String result = inetAddress.getHostAddress();
            return (result != null) ? result : "127.0.0.1";
        } catch (Exception e) {
            Log.e(TAG, "Failed to get device IP", e);
            return "127.0.0.1";
        }
    }

    private void updateStatus(String status) {
        SharedPreferences prefs = getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
        prefs.edit().putString(KEY_LAST_STATUS, status).apply();
    }

    private void saveLastPort(int port) {
        SharedPreferences prefs = getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
        prefs.edit().putInt(KEY_LAST_PORT, port).apply();
    }

    private void createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(
                    CHANNEL_ID,
                    "ADB Configuration",
                    NotificationManager.IMPORTANCE_LOW
            );
            channel.setDescription("ADB auto-configuration service");
            NotificationManager manager = getSystemService(NotificationManager.class);
            if (manager != null) {
                manager.createNotificationChannel(channel);
            }
        }
    }

    private Notification createNotification(String text) {
        Notification.Builder builder;


原版逻辑：
1. **没有 TCP 预检测** — 直接用 `adbHelper.connect()` 尝试每个端口
2. **串行扫描** — 每隔 5 个端口试一次
3. **范围** — 35000-44999

问题是原版太慢（每个端口都做完整 ADB 握手）。我的修改加了 TCP 预检测是对的，但范围和并发要调整。

让我重新思考：

**正确的流程应该是：**
1. 先用 TCP 快速检测哪些端口开放（并发100，范围 5555-5560 + 30000-50000）
2. 对开放端口用 `adbHelper.connect()` 验证（原版方式）
3. 找到后用 `adbHelper.switchToPort5555()` 切换（原版方式）

我现在的代码就是这样的，让我再检查一下有没有遗漏：

echo "=== 检查 configureAdb 中调用 switchToPort5555 ==="
grep -B5 -A10 "switchToPort5555" ~/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbConfigService.java | head -30


Output:
=== 检查 configureAdb 中调用 switchToPort5555 ===
            Log.i(TAG, "Step 4: Switching to port 5555...");
            updateNotification("Switching to port 5555...");
            updateStatus("Switching to port 5555...");

            AdbHelper adbHelper = new AdbHelper(this);
            boolean success = adbHelper.switchToPort5555("127.0.0.1", port);  // Use localhost

            if (success) {
                Log.i(TAG, "Successfully configured ADB on port 5555!");
                updateStatus("Success - ADB on port 5555");
                updateNotification("Success - ADB on port 5555");
                return true;
            } else {
                Log.e(TAG, "Failed to switch to port 5555");
                updateStatus("Failed - could not switch port");


流程是对的。但我发现一个问题 — **scanForAdbPort 里创建了一个 AdbHelper，configureAdb 里又创建了一个**。这没问题，但让我确认整体流程：

echo "=== configureAdb 完整流程 ==="
sed -n '237,320p' ~/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbConfigService.java


Output:
=== configureAdb 完整流程 ===
    private boolean configureAdb() {
        try {
            Log.i(TAG, "Step 1: Enabling wireless debugging...");
            updateNotification("Enabling wireless debugging...");
            updateStatus("Enabling wireless debugging...");

            Settings.Global.putInt(
                    getContentResolver(),
                    "adb_wifi_enabled",
                    1
            );

            Log.i(TAG, "Step 2: Waiting for ADB service to start...");
            updateNotification("Waiting for ADB service...");
            Thread.sleep(5000);  // MOD: Reduced from 15s to 5s

            String deviceIP = getDeviceIP();
            Log.i(TAG, "Device IP: " + deviceIP);

            // MOD: Always use 127.0.0.1 for local ADB connection
            // This works regardless of WiFi/mobile data
            String adbHost = "127.0.0.1";
            Log.i(TAG, "Using localhost for ADB connection");

            // MOD: Skip mDNS entirely - it doesn't work on mobile data
            // Go directly to port scanning which works on localhost
            Log.i(TAG, "Step 3: Scanning for ADB port (skipping mDNS)...");
            updateNotification("Scanning for ADB port...");
            updateStatus("Scanning for ADB port...");

            int port = scanForAdbPort();

            if (port == -1) {
                Log.e(TAG, "Could not find ADB port");
                updateStatus("Failed - port not found");
                updateNotification("Failed - port not found");
                return false;
            }

            Log.i(TAG, "Found ADB on port " + port);
            saveLastPort(port);

            Log.i(TAG, "Step 4: Switching to port 5555...");
            updateNotification("Switching to port 5555...");
            updateStatus("Switching to port 5555...");

            AdbHelper adbHelper = new AdbHelper(this);
            boolean success = adbHelper.switchToPort5555("127.0.0.1", port);  // Use localhost

            if (success) {
                Log.i(TAG, "Successfully configured ADB on port 5555!");
                updateStatus("Success - ADB on port 5555");
                updateNotification("Success - ADB on port 5555");
                return true;
            } else {
                Log.e(TAG, "Failed to switch to port 5555");
                updateStatus("Failed - could not switch port");
                updateNotification("Failed - could not switch port");
                return false;
            }

        } catch (SecurityException e) {
            Log.e(TAG, "Permission denied - grant WRITE_SECURE_SETTINGS via ADB", e);
            updateStatus("Failed - permission denied");
            updateNotification("Failed - permission denied");
            return false;
        } catch (InterruptedException e) {
            Log.e(TAG, "Thread interrupted", e);
            updateStatus("Failed - interrupted");
            updateNotification("Failed - interrupted");
            return false;
        } catch (Exception e) {
            Log.e(TAG, "Unexpected error", e);
            updateStatus("Failed - " + e.getMessage());
            updateNotification("Failed - error");
            return false;
        }
    }

    private int discoverAdbPortViaMdns() {
        final int[] discoveredPort = {-1};
        final CountDownLatch latch = new CountDownLatch(1);
        String deviceIP = getDeviceIP();


流程没问题：
1. 启用无线调试 `adb_wifi_enabled=1`
2. 等 5 秒
3. `scanForAdbPort()` 找端口（并发 TCP + ADB 验证）
4. `switchToPort5555()` 切换

**等等，我发现一个问题！**

`scanForAdbPort()` 里用 `adbHelper.connect()` 验证端口，但这个 connect 只是测试能否连接，**连接后就关闭了**。然后 `configureAdb()` 里又创建新的 AdbHelper 去 `switchToPort5555()`。

这应该没问题，因为 ADB 连接是无状态的。但让我确认 `switchToPort5555` 的逻辑：

echo "=== switchToPort5555 完整代码 ==="
grep -A50 "public boolean switchToPort5555" ~/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbHelper.java


Output:
=== switchToPort5555 完整代码 ===
    public boolean switchToPort5555(String host, int port) {
        SimpleAdbManager manager = null;
        try {
            Log.i(TAG, "switchToPort5555: Starting with host=" + host + ", port=" + port);
            manager = new SimpleAdbManager(context);

            Log.i(TAG, "switchToPort5555: Calling connect(" + host + ":" + port + ")");
            manager.connect(host, port);
            Log.i(TAG, "switchToPort5555: connect() completed successfully");

            Log.i(TAG, "switchToPort5555: Waiting for connection to stabilize");
            Thread.sleep(200);

            Log.i(TAG, "switchToPort5555: Sending tcpip:5555 service command");
            try (AdbStream stream = manager.openStream("tcpip:5555");
                 InputStream inputStream = stream.openInputStream()) {

                Log.i(TAG, "switchToPort5555: Reading response from stream");
                byte[] buffer = new byte[1024];
                int bytesRead = inputStream.read(buffer);

                if (bytesRead > 0) {
                    String response = new String(buffer, 0, bytesRead);
                    Log.i(TAG, "switchToPort5555: Response received (" + bytesRead + " bytes): " + response);
                } else {
                    Log.i(TAG, "switchToPort5555: No response data received");
                }
            }

            Log.i(TAG, "switchToPort5555: Waiting 3000ms for ADB to restart on port 5555");
            Thread.sleep(3000);

            Log.i(TAG, "switchToPort5555: Successfully switched to port 5555");
            return true;
        } catch (Exception e) {
            Log.e(TAG, "switchToPort5555: Failed to switch to port 5555", e);
            return false;
        } finally {
            if (manager != null) {
                try {
                    manager.close();
                } catch (Exception e) {
                    Log.e(TAG, "Error closing manager after switchToPort5555", e);
                }
            }
        }
    }

    private boolean checkPermissionGranted(SimpleAdbManager manager, String permission) {
        try {
            Log.i(TAG, "Checking if permission is granted: " + permission);


`switchToPort5555` 逻辑：
1. 连接到随机端口
2. 发送 `tcpip:5555` 命令
3. 等 3 秒让 ADB 重启

这和脚本的 `adb -s localhost:$PORT tcpip 5555` 是一样的效果。

**流程总结：**

| 步骤 | 脚本 | App |
|------|------|-----|
| 1. TCP 扫描 | `xargs -P 100` 并发 | `ExecutorService(100)` 并发 ✅ |
| 2. 范围 | 5555-5560 + 30000-50000 | 5555-5560 + 30000-50000 ✅ |
| 3. 跳过 5037 | `[ "$1" = "5037" ] && return 1` | `if (p != 5037)` ✅ |
| 4. ADB 验证 | `adb connect` + `adb devices` | `adbHelper.connect()` ✅ |
| 5. 切换 | `adb tcpip 5555` | `openStream("tcpip:5555")` ✅ |

**我觉得流程没问题了，可以编译测试吗？**


可以


Explanation: 用户同意编译测试了，我来编译并安装。

用户同意编译测试了，我来编译并安装。

cd ~/adb-auto-enable-mod && ./gradlew assembleDebug 2>&1 | tail -20


Output:
> Task :app:checkDebugDuplicateClasses UP-TO-DATE
> Task :app:desugarDebugFileDependencies UP-TO-DATE
> Task :app:mergeExtDexDebug UP-TO-DATE
> Task :app:mergeLibDexDebug UP-TO-DATE
> Task :app:dexBuilderDebug
> Task :app:mergeDebugJniLibFolders UP-TO-DATE
> Task :app:mergeDebugNativeLibs UP-TO-DATE
> Task :app:stripDebugDebugSymbols UP-TO-DATE
> Task :app:validateSigningDebug UP-TO-DATE
> Task :app:writeDebugAppMetadata UP-TO-DATE
> Task :app:writeDebugSigningConfigVersions UP-TO-DATE
> Task :app:mergeProjectDexDebug
> Task :app:packageDebug
> Task :app:createDebugApkListingFileRedirect UP-TO-DATE
> Task :app:assembleDebug

[Incubating] Problems report is available at: file:///home/ubuntu/adb-auto-enable-mod/build/reports/problems/problems-report.html

BUILD SUCCESSFUL in 2s
35 actionable tasks: 4 executed, 31 up-to-date


adb -s localhost:15556 install -r ~/adb-auto-enable-mod/app/build/outputs/apk/debug/app-debug.apk 2>&1


Output:
Performing Streamed Install
Success


安装成功。现在测试：

# 先停掉 adb-tunnel 服务，避免干扰
ssh 100.114.227.26 -p 8022 "sv stop adb-tunnel 2>/dev/null; echo 'adb-tunnel 已停止'"

# 断开并关掉 5555
ssh 100.114.227.26 -p 8022 "adb disconnect; sleep 1"

# 关掉无线调试
adb -s localhost:15556 shell "settings put global adb_wifi_enabled 0"
sleep 2

# 确认 5555 关了
ssh 100.114.227.26 -p 8022 "netstat -tlnp 2>/dev/null | grep 5555 || echo '5555 未监听'"


Output:
fail: adb-tunnel: unable to change to service directory: file does not exist
adb-tunnel 已停止
disconnected everything
5555 未监听


# 清空 logcat
adb -s localhost:15556 shell "logcat -c"

# 触发新版 App 测试
ssh 100.114.227.26 -p 8022 "curl -s 'http://127.0.0.1:9094/api/test'"


App 没启动，先打开：

adb -s localhost:15556 shell "am start -n com.tpn.adbautoenable.mod/com.tpn.adbautoenable.MainActivity"
sleep 3
ssh 100.114.227.26 -p 8022 "curl -s 'http://127.0.0.1:9094/api/test'"


Output:
Starting: Intent { cmp=com.tpn.adbautoenable.mod/com.tpn.adbautoenable.MainActivity }
{"success":true,"message":"Boot test started. Check logs below for progress."}


测试已启动，等待结果：


Output:
=== 状态 ===
{"lastStatus":"Success - ADB on port 5555","lastPort":5555,"isPaired":false,"hasPermission":true,"adb5555Available":true}


**成功了！** 🎉

- `lastStatus: "Success - ADB on port 5555"` ✅
- `lastPort: 5555` ✅
- `adb5555Available: true` ✅

验证一下 5555 真的能连：


Output:
connected to 127.0.0.1:5555
List of devices attached
127.0.0.1:5555	device


**新版 App 在移动数据下成功恢复 5555！**

看下日志确认是 App 做的：

adb -s localhost:15556 shell "logcat -d | grep -i ADBAutoEnable | tail -30"


Output:
05-18 14:24:07.343  2027  3738 D NotificationService--OplusNotificationFixHelper: fixAppIcon: pkg=com.tpn.adbautoenable.mod
05-18 14:24:07.343  2027  3738 D NotificationService--OplusNotificationFixHelper: fixAppIcon: appIcon = null, pkg = com.tpn.adbautoenable.mod
05-18 14:24:07.651  2027  4289 D AdbDebuggingManager: Received public key: QAAAAH+usBSB7kKsG9Lk/8xd+/IOsqyW66KP2lLWVNI81PlLfIh3tHR8gk9xT1uskILoO4oRfGugTthpu307DOaNwHw05HP+GQJhVkagk8no/SIZLgeEmaCL06sbZAsBmD4rX7GOofeXU7ifYAzCbp+nyXdHrYOWIjPnV45azccDr+rc5WJaYa1VGbUSsIUudjkvY/aO5sIGGWKUBKNqzLh8rM86sulqFiDf6XFGxDht2+HJijOetAJMEoKSmTUL0HAt3/6M5YO7k8eac7RJUpGrRc3481BavEklHZM/kIqIwO1MPnDBPZr1JzoXysXZ9zgtyx2GNKuDJ0MVdoA5tbQIefXjTPnMrEdHfjkQ+OlA9lryXlVLnRtk/xvLRB9zMjUnfr30dsGGSmOZPE4LQyMN4/7XklXnZkfL+5vMTpTC+xTUY3Uik6yFFXvPd/ZMzQstSs4foPNlETbPf8BzAyMwQSlbrq/Udbr6Tp7qhNPb+SO19ERMwzO/MNcUJVQI7br+0QQ2s5Gs3EJOOcfJIj4D+2jcwoUh+VFsLHocteSpfY3KbAs4fWrhjW9odeISiS+s4hWNLybFsMhB0SZAHSsiBFPRe/7ZWa0CvIuSCsUxpokNuC/4jH5hmRo3QGJdSo6OFDnWz4XGYH3mVb0Qi5LghI7i+1G7duRsG6p9OSJd5wA2tHC0OgEAAQA= ADBAutoEnable
05-18 14:24:07.655  2027  2062 D AdbDebuggingManager: Logging key QAAAAH+usBSB7kKsG9Lk/8xd+/IOsqyW66KP2lLWVNI81PlLfIh3tHR8gk9xT1uskILoO4oRfGugTthpu307DOaNwHw05HP+GQJhVkagk8no/SIZLgeEmaCL06sbZAsBmD4rX7GOofeXU7ifYAzCbp+nyXdHrYOWIjPnV45azccDr+rc5WJaYa1VGbUSsIUudjkvY/aO5sIGGWKUBKNqzLh8rM86sulqFiDf6XFGxDht2+HJijOetAJMEoKSmTUL0HAt3/6M5YO7k8eac7RJUpGrRc3481BavEklHZM/kIqIwO1MPnDBPZr1JzoXysXZ9zgtyx2GNKuDJ0MVdoA5tbQIefXjTPnMrEdHfjkQ+OlA9lryXlVLnRtk/xvLRB9zMjUnfr30dsGGSmOZPE4LQyMN4/7XklXnZkfL+5vMTpTC+xTUY3Uik6yFFXvPd/ZMzQstSs4foPNlETbPf8BzAyMwQSlbrq/Udbr6Tp7qhNPb+SO19ERMwzO/MNcUJVQI7br+0QQ2s5Gs3EJOOcfJIj4D+2jcwoUh+VFsLHocteSpfY3KbAs4fWrhjW9odeISiS+s4hWNLybFsMhB0SZAHSsiBFPRe/7ZWa0CvIuSCsUxpokNuC/4jH5hmRo3QGJdSo6OFDnWz4XGYH3mVb0Qi5LghI7i+1G7duRsG6p9OSJd5wA2tHC0OgEAAQA= ADBAutoEnable, state = 1, alwaysAllow = false, lastConnectionTime = 0, authWindow = 0
05-18 14:24:07.732  2027  2066 D Transition: collectOrderChanges onTopTasksEnd: [Task{661974b #801 type=standard A=10226:com.android.systemui}], reportedOnTop:null, mOnTopTasksStart: [Task{661974b #801 type=standard A=10226:com.android.systemui}], TDA child: [Task{85261ed #2 type=undefined}, Task{2a43a67 #747 type=undefined A=10424:org.galexander.sshd}, Task{8f6ffe8 #796 type=standard A=1000:com.android.settings.root}, Task{e6a3c3c #765 type=standard A=10422:com.tailscale.ipn}, Task{272a515 #771 type=standard A=10419:com.termux}, Task{f6e1 #1 type=home}, Task{a5c6ff2 #764 type=standard A=10341:com.tencent.mobileqq}, Task{f6828a2 #800 type=standard A=10429:com.tpn.adbautoenable.mod}, Task{da801be #775 type=standard A=10321:tv.danmaku.bili}, Task{661974b #801 type=standard A=10226:com.android.systemui}]
05-18 14:24:09.617  2027  2062 D AdbDebuggingManager: Logging key QAAAAH+usBSB7kKsG9Lk/8xd+/IOsqyW66KP2lLWVNI81PlLfIh3tHR8gk9xT1uskILoO4oRfGugTthpu307DOaNwHw05HP+GQJhVkagk8no/SIZLgeEmaCL06sbZAsBmD4rX7GOofeXU7ifYAzCbp+nyXdHrYOWIjPnV45azccDr+rc5WJaYa1VGbUSsIUudjkvY/aO5sIGGWKUBKNqzLh8rM86sulqFiDf6XFGxDht2+HJijOetAJMEoKSmTUL0HAt3/6M5YO7k8eac7RJUpGrRc3481BavEklHZM/kIqIwO1MPnDBPZr1JzoXysXZ9zgtyx2GNKuDJ0MVdoA5tbQIefXjTPnMrEdHfjkQ+OlA9lryXlVLnRtk/xvLRB9zMjUnfr30dsGGSmOZPE4LQyMN4/7XklXnZkfL+5vMTpTC+xTUY3Uik6yFFXvPd/ZMzQstSs4foPNlETbPf8BzAyMwQSlbrq/Udbr6Tp7qhNPb+SO19ERMwzO/MNcUJVQI7br+0QQ2s5Gs3EJOOcfJIj4D+2jcwoUh+VFsLHocteSpfY3KbAs4fWrhjW9odeISiS+s4hWNLybFsMhB0SZAHSsiBFPRe/7ZWa0CvIuSCsUxpokNuC/4jH5hmRo3QGJdSo6OFDnWz4XGYH3mVb0Qi5LghI7i+1G7duRsG6p9OSJd5wA2tHC0OgEAAQA= ADBAutoEnable, state = 2, alwaysAllow = false, lastConnectionTime = 0, authWindow = 0
05-18 14:24:09.628  2027  3143 D Transition: collectOrderChanges onTopTasksEnd: [Task{da801be #775 type=standard A=10321:tv.danmaku.bili}], reportedOnTop:null, mOnTopTasksStart: [Task{661974b #801 type=standard A=10226:com.android.systemui}], TDA child: [Task{85261ed #2 type=undefined}, Task{2a43a67 #747 type=undefined A=10424:org.galexander.sshd}, Task{8f6ffe8 #796 type=standard A=1000:com.android.settings.root}, Task{e6a3c3c #765 type=standard A=10422:com.tailscale.ipn}, Task{272a515 #771 type=standard A=10419:com.termux}, Task{f6e1 #1 type=home}, Task{a5c6ff2 #764 type=standard A=10341:com.tencent.mobileqq}, Task{f6828a2 #800 type=standard A=10429:com.tpn.adbautoenable.mod}, Task{661974b #801 type=standard A=10226:com.android.systemui}, Task{da801be #775 type=standard A=10321:tv.danmaku.bili}]
05-18 14:24:09.635  2027  6422 D NotificationService--OplusNotificationFixHelper: fixAppIcon: pkg=com.tpn.adbautoenable.mod
05-18 14:24:09.637  2027  6422 D NotificationService--OplusNotificationFixHelper: fixAppIcon: appIcon = null, pkg = com.tpn.adbautoenable.mod
05-18 14:24:09.668  2027  4289 D AdbDebuggingManager: Received disconnected message: QAAAAH+usBSB7kKsG9Lk/8xd+/IOsqyW66KP2lLWVNI81PlLfIh3tHR8gk9xT1uskILoO4oRfGugTthpu307DOaNwHw05HP+GQJhVkagk8no/SIZLgeEmaCL06sbZAsBmD4rX7GOofeXU7ifYAzCbp+nyXdHrYOWIjPnV45azccDr+rc5WJaYa1VGbUSsIUudjkvY/aO5sIGGWKUBKNqzLh8rM86sulqFiDf6XFGxDht2+HJijOetAJMEoKSmTUL0HAt3/6M5YO7k8eac7RJUpGrRc3481BavEklHZM/kIqIwO1MPnDBPZr1JzoXysXZ9zgtyx2GNKuDJ0MVdoA5tbQIefXjTPnMrEdHfjkQ+OlA9lryXlVLnRtk/xvLRB9zMjUnfr30dsGGSmOZPE4LQyMN4/7XklXnZkfL+5vMTpTC+xTUY3Uik6yFFXvPd/ZMzQstSs4foPNlETbPf8BzAyMwQSlbrq/Udbr6Tp7qhNPb+SO19ERMwzO/MNcUJVQI7br+0QQ2s5Gs3EJOOcfJIj4D+2jcwoUh+VFsLHocteSpfY3KbAs4fWrhjW9odeISiS+s4hWNLybFsMhB0SZAHSsiBFPRe/7ZWa0CvIuSCsUxpokNuC/4jH5hmRo3QGJdSo6OFDnWz4XGYH3mVb0Qi5LghI7i+1G7duRsG6p9OSJd5wA2tHC0OgEAAQA= ADBAutoEnable
05-18 14:24:09.668  2027  2062 D AdbDebuggingManager: Logging key QAAAAH+usBSB7kKsG9Lk/8xd+/IOsqyW66KP2lLWVNI81PlLfIh3tHR8gk9xT1uskILoO4oRfGugTthpu307DOaNwHw05HP+GQJhVkagk8no/SIZLgeEmaCL06sbZAsBmD4rX7GOofeXU7ifYAzCbp+nyXdHrYOWIjPnV45azccDr+rc5WJaYa1VGbUSsIUudjkvY/aO5sIGGWKUBKNqzLh8rM86sulqFiDf6XFGxDht2+HJijOetAJMEoKSmTUL0HAt3/6M5YO7k8eac7RJUpGrRc3481BavEklHZM/kIqIwO1MPnDBPZr1JzoXysXZ9zgtyx2GNKuDJ0MVdoA5tbQIefXjTPnMrEdHfjkQ+OlA9lryXlVLnRtk/xvLRB9zMjUnfr30dsGGSmOZPE4LQyMN4/7XklXnZkfL+5vMTpTC+xTUY3Uik6yFFXvPd/ZMzQstSs4foPNlETbPf8BzAyMwQSlbrq/Udbr6Tp7qhNPb+SO19ERMwzO/MNcUJVQI7br+0QQ2s5Gs3EJOOcfJIj4D+2jcwoUh+VFsLHocteSpfY3KbAs4fWrhjW9odeISiS+s4hWNLybFsMhB0SZAHSsiBFPRe/7ZWa0CvIuSCsUxpokNuC/4jH5hmRo3QGJdSo6OFDnWz4XGYH3mVb0Qi5LghI7i+1G7duRsG6p9OSJd5wA2tHC0OgEAAQA= ADBAutoEnable, state = 7, alwaysAllow = false, lastConnectionTime = 0, authWindow = 0
05-18 14:24:09.668  2027  4289 D AdbDebuggingManager: Received public key: QAAAAH+usBSB7kKsG9Lk/8xd+/IOsqyW66KP2lLWVNI81PlLfIh3tHR8gk9xT1uskILoO4oRfGugTthpu307DOaNwHw05HP+GQJhVkagk8no/SIZLgeEmaCL06sbZAsBmD4rX7GOofeXU7ifYAzCbp+nyXdHrYOWIjPnV45azccDr+rc5WJaYa1VGbUSsIUudjkvY/aO5sIGGWKUBKNqzLh8rM86sulqFiDf6XFGxDht2+HJijOetAJMEoKSmTUL0HAt3/6M5YO7k8eac7RJUpGrRc3481BavEklHZM/kIqIwO1MPnDBPZr1JzoXysXZ9zgtyx2GNKuDJ0MVdoA5tbQIefXjTPnMrEdHfjkQ+OlA9lryXlVLnRtk/xvLRB9zMjUnfr30dsGGSmOZPE4LQyMN4/7XklXnZkfL+5vMTpTC+xTUY3Uik6yFFXvPd/ZMzQstSs4foPNlETbPf8BzAyMwQSlbrq/Udbr6Tp7qhNPb+SO19ERMwzO/MNcUJVQI7br+0QQ2s5Gs3EJOOcfJIj4D+2jcwoUh+VFsLHocteSpfY3KbAs4fWrhjW9odeISiS+s4hWNLybFsMhB0SZAHSsiBFPRe/7ZWa0CvIuSCsUxpokNuC/4jH5hmRo3QGJdSo6OFDnWz4XGYH3mVb0Qi5LghI7i+1G7duRsG6p9OSJd5wA2tHC0OgEAAQA= ADBAutoEnable
05-18 14:24:09.668  2027  2062 D AdbDebuggingManager: Logging key QAAAAH+usBSB7kKsG9Lk/8xd+/IOsqyW66KP2lLWVNI81PlLfIh3tHR8gk9xT1uskILoO4oRfGugTthpu307DOaNwHw05HP+GQJhVkagk8no/SIZLgeEmaCL06sbZAsBmD4rX7GOofeXU7ifYAzCbp+nyXdHrYOWIjPnV45azccDr+rc5WJaYa1VGbUSsIUudjkvY/aO5sIGGWKUBKNqzLh8rM86sulqFiDf6XFGxDht2+HJijOetAJMEoKSmTUL0HAt3/6M5YO7k8eac7RJUpGrRc3481BavEklHZM/kIqIwO1MPnDBPZr1JzoXysXZ9zgtyx2GNKuDJ0MVdoA5tbQIefXjTPnMrEdHfjkQ+OlA9lryXlVLnRtk/xvLRB9zMjUnfr30dsGGSmOZPE4LQyMN4/7XklXnZkfL+5vMTpTC+xTUY3Uik6yFFXvPd/ZMzQstSs4foPNlETbPf8BzAyMwQSlbrq/Udbr6Tp7qhNPb+SO19ERMwzO/MNcUJVQI7br+0QQ2s5Gs3EJOOcfJIj4D+2jcwoUh+VFsLHocteSpfY3KbAs4fWrhjW9odeISiS+s4hWNLybFsMhB0SZAHSsiBFPRe/7ZWa0CvIuSCsUxpokNuC/4jH5hmRo3QGJdSo6OFDnWz4XGYH3mVb0Qi5LghI7i+1G7duRsG6p9OSJd5wA2tHC0OgEAAQA= ADBAutoEnable, state = 1, alwaysAllow = false, lastConnectionTime = 0, authWindow = 0
05-18 14:24:09.709  2027  2066 D Transition: collectOrderChanges onTopTasksEnd: [Task{34f0c38 #802 type=standard A=10226:com.android.systemui}], reportedOnTop:null, mOnTopTasksStart: [Task{34f0c38 #802 type=standard A=10226:com.android.systemui}], TDA child: [Task{85261ed #2 type=undefined}, Task{2a43a67 #747 type=undefined A=10424:org.galexander.sshd}, Task{8f6ffe8 #796 type=standard A=1000:com.android.settings.root}, Task{e6a3c3c #765 type=standard A=10422:com.tailscale.ipn}, Task{272a515 #771 type=standard A=10419:com.termux}, Task{f6e1 #1 type=home}, Task{a5c6ff2 #764 type=standard A=10341:com.tencent.mobileqq}, Task{f6828a2 #800 type=standard A=10429:com.tpn.adbautoenable.mod}, Task{661974b #801 type=standard A=10226:com.android.systemui}, Task{da801be #775 type=standard A=10321:tv.danmaku.bili}, Task{34f0c38 #802 type=standard A=10226:com.android.systemui}]
05-18 14:24:09.737  2027  4781 D ActivityTaskManager: after skip size:8,pre-Filtered:com.android.systemui # tv.danmaku.bili # com.tpn.adbautoenable.mod # com.tencent.mobileqq # com.termux # com.tailscale.ipn # com.android.settings # org.galexander.sshd #
05-18 14:24:09.741  2027  3143 D ActivityTaskManager: after skip size:8,pre-Filtered:com.android.systemui # tv.danmaku.bili # com.tpn.adbautoenable.mod # com.tencent.mobileqq # com.termux # com.tailscale.ipn # com.android.settings # org.galexander.sshd #
05-18 14:24:11.866  2027  2062 D AdbDebuggingManager: Logging key QAAAAH+usBSB7kKsG9Lk/8xd+/IOsqyW66KP2lLWVNI81PlLfIh3tHR8gk9xT1uskILoO4oRfGugTthpu307DOaNwHw05HP+GQJhVkagk8no/SIZLgeEmaCL06sbZAsBmD4rX7GOofeXU7ifYAzCbp+nyXdHrYOWIjPnV45azccDr+rc5WJaYa1VGbUSsIUudjkvY/aO5sIGGWKUBKNqzLh8rM86sulqFiDf6XFGxDht2+HJijOetAJMEoKSmTUL0HAt3/6M5YO7k8eac7RJUpGrRc3481BavEklHZM/kIqIwO1MPnDBPZr1JzoXysXZ9zgtyx2GNKuDJ0MVdoA5tbQIefXjTPnMrEdHfjkQ+OlA9lryXlVLnRtk/xvLRB9zMjUnfr30dsGGSmOZPE4LQyMN4/7XklXnZkfL+5vMTpTC+xTUY3Uik6yFFXvPd/ZMzQstSs4foPNlETbPf8BzAyMwQSlbrq/Udbr6Tp7qhNPb+SO19ERMwzO/MNcUJVQI7br+0QQ2s5Gs3EJOOcfJIj4D+2jcwoUh+VFsLHocteSpfY3KbAs4fWrhjW9odeISiS+s4hWNLybFsMhB0SZAHSsiBFPRe/7ZWa0CvIuSCsUxpokNuC/4jH5hmRo3QGJdSo6OFDnWz4XGYH3mVb0Qi5LghI7i+1G7duRsG6p9OSJd5wA2tHC0OgEAAQA= ADBAutoEnable, state = 2, alwaysAllow = true, lastConnectionTime = 1779085451866, authWindow = 0
05-18 14:24:11.876  2027  6422 D Transition: collectOrderChanges onTopTasksEnd: [Task{da801be #775 type=standard A=10321:tv.danmaku.bili}], reportedOnTop:null, mOnTopTasksStart: [Task{34f0c38 #802 type=standard A=10226:com.android.systemui}], TDA child: [Task{85261ed #2 type=undefined}, Task{2a43a67 #747 type=undefined A=10424:org.galexander.sshd}, Task{8f6ffe8 #796 type=standard A=1000:com.android.settings.root}, Task{e6a3c3c #765 type=standard A=10422:com.tailscale.ipn}, Task{272a515 #771 type=standard A=10419:com.termux}, Task{f6e1 #1 type=home}, Task{a5c6ff2 #764 type=standard A=10341:com.tencent.mobileqq}, Task{f6828a2 #800 type=standard A=10429:com.tpn.adbautoenable.mod}, Task{34f0c38 #802 type=standard A=10226:com.android.systemui}, Task{da801be #775 type=standard A=10321:tv.danmaku.bili}]
05-18 14:24:12.017  2027  3740 D ActivityTaskManager: after skip size:7,pre-Filtered:tv.danmaku.bili # com.tpn.adbautoenable.mod # com.tencent.mobileqq # com.termux # com.tailscale.ipn # com.android.settings # org.galexander.sshd #
05-18 14:24:12.019  2027  3740 D ActivityTaskManager: after skip size:7,pre-Filtered:tv.danmaku.bili # com.tpn.adbautoenable.mod # com.tencent.mobileqq # com.termux # com.tailscale.ipn # com.android.settings # org.galexander.sshd #
05-18 14:24:15.078  2027  3732 D NotificationService--OplusNotificationFixHelper: fixAppIcon: pkg=com.tpn.adbautoenable.mod
05-18 14:24:15.078  2027  3732 D NotificationService--OplusNotificationFixHelper: fixAppIcon: appIcon = null, pkg = com.tpn.adbautoenable.mod
05-18 14:24:15.083  2027  3738 D NotificationService--OplusNotificationFixHelper: fixAppIcon: pkg=com.tpn.adbautoenable.mod
05-18 14:24:15.083  2027  3738 D NotificationService--OplusNotificationFixHelper: fixAppIcon: appIcon = null, pkg = com.tpn.adbautoenable.mod
05-18 14:24:26.774  2027  3143 I PowerHalWrapper: amsBoostNotify pid:10905,activity:com.bilibili.ship.theseus.detail.UnitedBizDetailsActivity, package:tv.danmaku.bili, mProcessCreatePackcom.tpn.adbautoenable.mod
05-18 14:24:26.786  2027  3143 I PowerHalWrapper: amsBoostNotify pid:14249,activity:com.tencent.mobileqq.activity.SplashActivity, package:com.tencent.mobileqq, mProcessCreatePackcom.tpn.adbautoenable.mod
05-18 14:24:26.787  2027  3143 I PowerHalWrapper: amsBoostNotify pid:14249,activity:com.tencent.mobileqq.activity.SplashActivity, package:com.tencent.mobileqq, mProcessCreatePackcom.tpn.adbautoenable.mod
05-18 14:24:26.821  2027  3739 D Transition: collectOrderChanges onTopTasksEnd: [Task{a5c6ff2 #764 type=standard A=10341:com.tencent.mobileqq}], reportedOnTop:null, mOnTopTasksStart: [Task{da801be #775 type=standard A=10321:tv.danmaku.bili}], TDA child: [Task{85261ed #2 type=undefined}, Task{2a43a67 #747 type=undefined A=10424:org.galexander.sshd}, Task{8f6ffe8 #796 type=standard A=1000:com.android.settings.root}, Task{e6a3c3c #765 type=standard A=10422:com.tailscale.ipn}, Task{272a515 #771 type=standard A=10419:com.termux}, Task{f6e1 #1 type=home}, Task{f6828a2 #800 type=standard A=10429:com.tpn.adbautoenable.mod}, Task{da801be #775 type=standard A=10321:tv.danmaku.bili}, Task{a5c6ff2 #764 type=standard A=10341:com.tencent.mobileqq}]
05-18 14:24:26.869  2027  3374 D ActivityTaskManager: after skip size:7,pre-Filtered:com.tencent.mobileqq # tv.danmaku.bili # com.tpn.adbautoenable.mod # com.termux # com.tailscale.ipn # com.android.settings # org.galexander.sshd #
05-18 14:24:27.348  2027  2065 I PowerHalWrapper: amsBoostNotify pid:10905,activity:com.bilibili.ship.theseus.detail.UnitedBizDetailsActivity, package:tv.danmaku.bili, mProcessCreatePackcom.tpn.adbautoenable.mod


日志显示 `AdbDebuggingManager` 收到了 `ADBAutoEnable` 的公钥并认证成功（state = 2 表示已认证）。

**测试结果：✅ 新版 App 成功！**

- 并发扫描 5555-5560 + 30000-50000
- 找到端口后用 Java ADB 库连接验证
- 发送 `tcpip:5555` 切换成功

**要打开 WiFi 恢复正常吗？**


不，你授权自启动什么的了没


没有，让我检查并授权：

Explanation:
用户问的是有没有授权自启动权限。让我检查一下：

1. 开机广播权限 - AndroidManifest 里有 RECEIVE_BOOT_COMPLETED
2. 但是国产手机（OPPO/ColorOS）通常需要额外授权自启动权限才能收到开机广播

让我检查一下自启动权限状态。

# 检查自启动权限状态
adb -s localhost:15556 shell "dumpsys package com.tpn.adbautoenable.mod | grep -i 'autostart\|boot\|background'"


# 授权开机自启动（RECEIVE_BOOT_COMPLETED 是 normal 权限，安装时自动授予）
# 检查权限状态
adb -s localhost:15556 shell "dumpsys package com.tpn.adbautoenable.mod | grep -A50 'granted=true' | head -30"


Output:
android.permission.FOREGROUND_SERVICE: granted=true
      android.permission.RECEIVE_BOOT_COMPLETED: granted=true
      android.permission.INTERNET: granted=true
      android.permission.WRITE_SECURE_SETTINGS: granted=true
      android.permission.WRITE_SECURE_SETTINGS: granted=false, userId=10
      android.permission.WRITE_SECURE_SETTINGS: granted=false, userId=999
      android.permission.FOREGROUND_SERVICE_DATA_SYNC: granted=true
      android.permission.ACCESS_NETWORK_STATE: granted=true
      android.permission.ACCESS_WIFI_STATE: granted=true
    User 0: ceDataInode=2995550 deDataInode=2995520 installed=true hidden=false suspended=false distractionFlags=0 stopped=false notLaunched=false enabled=0 ofs=0 pdm=false instant=false virtual=false quarantined=false
      installReason=0
      dataDir=/data/user/0/com.tpn.adbautoenable.mod
      firstInstallTime=1970-01-01 08:00:00
      uninstallReason=0
      overlay paths:
        /product/overlay/NavigationBarModeGestural/NavigationBarModeGesturalOverlay.apk
        /data/resource-cache/com.android.systemui-neutral-h6wV.frro
        /data/resource-cache/com.android.systemui-accent-stBc.frro
        /data/resource-cache/com.android.systemui-dynamic-xTk4.frro
      legacy overlay paths:
        /product/overlay/NavigationBarModeGestural/NavigationBarModeGesturalOverlay.apk
      gids=[3003]
      runtime permissions:
        android.permission.POST_NOTIFICATIONS: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]
    User 10: ceDataInode=0 deDataInode=0 installed=true hidden=false suspended=false distractionFlags=0 stopped=true notLaunched=true enabled=0 ofs=0 pdm=false instant=false virtual=false quarantined=false
      installReason=0
      dataDir=/data/user/10/com.tpn.adbautoenable.mod
      firstInstallTime=2026-05-18 14:02:34
      uninstallReason=0
      overlay paths:


权限状态：
- ✅ `RECEIVE_BOOT_COMPLETED: granted=true` — 开机自启动
- ✅ `FOREGROUND_SERVICE: granted=true` — 前台服务
- ✅ `WRITE_SECURE_SETTINGS: granted=true` — 修改系统设置
- ❌ `POST_NOTIFICATIONS: granted=false` — 通知权限未授予

授权通知权限：

# 授权通知权限
adb -s localhost:15556 shell "pm grant com.tpn.adbautoenable.mod android.permission.POST_NOTIFICATIONS" 2>&1 || echo "需要手动授权通知权限"

# 检查 OPPO 的自启动管理（厂商限制）
adb -s localhost:15556 shell "dumpsys deviceidle whitelist | grep adbautoenable"


原版已在电池优化白名单。把新版也加进去：

# 加入电池优化白名单（防止被杀）
adb -s localhost:15556 shell "dumpsys deviceidle whitelist +com.tpn.adbautoenable.mod"

# 验证
adb -s localhost:15556 shell "dumpsys deviceidle whitelist | grep adbautoenable"


Output:
Added: com.tpn.adbautoenable.mod
user,com.tpn.adbautoenable,10425
user,com.tpn.adbautoenable.mod,10429


新版已加入白名单。检查 OPPO 的自启动管理：

# OPPO 自启动管理数据库
adb -s localhost:15556 shell "content query --uri content://com.coloros.provider.PermissionProvider/permission --projection pkg_name:auto_start --where \"pkg_name='com.tpn.adbautoenable.mod'\"" 2>&1 || echo "无法直接查询"

# 尝试设置自启动
adb -s localhost:15556 shell "settings list global | grep -i auto"


OPPO 的自启动管理需要手动在设置里开启。让我用 UI 自动化打开：

# 打开应用详情页
adb -s localhost:15556 shell "am start -a android.settings.APPLICATION_DETAILS_SETTINGS -d package:com.tpn.adbautoenable.mod"


Output: Starting: Intent { act=android.settings.APPLICATION_DETAILS_SETTINGS dat=package: }


已打开应用详情页。

**需要你手动操作：**
1. 找到「自启动」或「允许自启动」选项，打开它
2. 找到「省电策略」或「电池」，设置为「无限制」

或者告诉我你手机上看到什么选项，我帮你用 ADB 点击。


行了，我现在重启试试


好，重启测试吧！

我会监控隧道状态，等你手机重启后看新版 App 能不能自动恢复 5555。


你现在还能ssh我手机没


ssh 100.114.227.26 -p 8022 -o ConnectTimeout=5 "echo '✅ 手机 SSH 正常'" 2>&1


Output: ssh: connect to host 100.114.227.26 port 8022: Connection timed out


手机已经关机了，SSH 连不上。

等你重启完成后告诉我，我来检查 5555 是否自动恢复。


[System note: Your previous turn was interrupted before you could process the last tool result(s). The conversation history contains tool outputs you haven't responded to yet. Please finish processing those results and summarize what was accomplished, then address the user's new message below.]

不，你直接检查


Output:
failed to connect to '127.0.0.1:5555': Connection refused
List of devices attached


5555 没开。检查 App 状态：


Output: {"lastStatus":"Scanning for ADB port...","lastPort":5555,"isPaired":false,"hasPermission":true,"adb5555Available":false}


App 在运行，但：
- `lastStatus: "Scanning for ADB port..."` — 卡在扫描
- `isPaired: false` — **未配对！**
- `adb5555Available: false` — 5555 未开

**问题：重启后配对信息丢失了。** 需要重新配对。

你打开无线调试配对界面，给我配对码和端口。


原版呢看一下


Output: {"lastStatus":"Failed after 3 attempts","lastPort":44587,"isPaired":true,"hasPermission":true,"adb5555Available":false}


**原版状态：**
- `isPaired: true` — 配对信息保留了
- `lastStatus: "Failed after 3 attempts"` — 但还是失败了

原版配对信息没丢，但因为移动数据下 mDNS 不工作所以失败。

**新版问题：配对信息重启后丢失了。** 可能是存储位置不对，或者包名改了导致读不到旧的配对数据。

需要重新配对新版。给我配对码和端口？


不行，问题很严重，为什么会丢失


你说得对，让我查一下配对密钥存储在哪里：

Explanation:
用户说得对，这是个严重问题。配对信息不应该丢失。让我分析一下：

1. 原版 `com.tpn.adbautoenable` 的 `isPaired: true` - 配对信息保留了
2. 新版 `com.tpn.adbautoenable.mod` 的 `isPaired: false` - 配对信息丢失了

可能的原因：
1. 配对密钥存储在 App 的私有目录，包名不同所以目录不同
2. 配对密钥可能存储在 SharedPreferences 或文件中，新版读不到旧版的数据
3. 可能是 ADB 库的密钥存储位置问题

让我检查一下配对密钥是怎么存储的。

echo "=== 原版 App 数据目录 ==="
adb -s localhost:15556 shell "run-as com.tpn.adbautoenable ls -la /data/data/com.tpn.adbautoenable/files/ 2>/dev/null" || \
adb -s localhost:15556 shell "ls -la /data/data/com.tpn.adbautoenable/files/ 2>/dev/null" || echo "无法访问"

echo ""
echo "=== 新版 App 数据目录 ==="
adb -s localhost:15556 shell "run-as com.tpn.adbautoenable.mod ls -la /data/data/com.tpn.adbautoenable.mod/files/ 2>/dev/null" || \
adb -s localhost:15556 shell "ls -la /data/data/com.tpn.adbautoenable.mod/files/ 2>/dev/null" || echo "无法访问"


Output:
=== 原版 App 数据目录 ===
adb: device offline
adb: device offline
无法访问

=== 新版 App 数据目录 ===
adb: device offline
adb: device offline
无法访问


ssh 100.114.227.26 -p 8022 "
echo '=== 原版 App 数据 ==='
ls -la /data/data/com.tpn.adbautoenable/files/ 2>/dev/null || echo '无权限'

echo ''
echo '=== 新版 App 数据 ==='
ls -la /data/data/com.tpn.adbautoenable.mod/files/ 2>/dev/null || echo '无权限'
"


Output:
=== 原版 App 数据 ===
无权限

=== 新版 App 数据 ===
无权限


没权限。看源码里密钥存储逻辑：

echo "=== 搜索密钥存储相关代码 ==="
grep -rn "key\|Key\|pair\|Pair\|store\|Store\|save\|Save\|priv\|Priv" ~/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/ | grep -i -E "key|pair|private" | head -30


Output:
=== 搜索密钥存储相关代码 ===
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/BootReceiver.java:10:    private static final String TAG = "ADBAutoEnable";
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbConfigService.java:31:    private static final String TAG = "ADBAutoEnable";
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbConfigService.java:32:    private static final String CHANNEL_ID = "ADBAutoEnableChannel";
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbConfigService.java:33:    private static final String SERVICE_TYPE = "_adb-tls-connect._tcp";
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbConfigService.java:34:    private static final String PREFS_NAME = "ADBAutoEnablePrefs";
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbConfigService.java:35:    private static final String KEY_LAST_STATUS = "last_status";
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbConfigService.java:36:    private static final String KEY_LAST_PORT = "last_port";
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbConfigService.java:37:    private static final int INITIAL_BOOT_DELAY_SECONDS = 10;  // MOD: Reduced from 30
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbConfigService.java:38:    private static final int MAX_RETRY_ATTEMPTS = 3;
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbConfigService.java:39:    private static final int RETRY_DELAY_SECONDS = 10;
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbConfigService.java:40:    private static final int WEB_SERVER_PORT = 9094;
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbConfigService.java:42:    private WebServer webServer;
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbConfigService.java:43:    private boolean isBootConfigMode = false;
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbConfigService.java:44:    private volatile boolean isConfiguring = false;
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbConfigService.java:130:    private void startWebServer() {
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbConfigService.java:140:    private void waitForWifiConnection() throws InterruptedException {
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbConfigService.java:153:    private boolean isWifiConnected() {
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbConfigService.java:197:    private void waitForBootStabilization() throws InterruptedException {
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbConfigService.java:209:    private void configureAdbWithRetries() {
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbConfigService.java:237:    private boolean configureAdb() {
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbConfigService.java:317:    private int discoverAdbPortViaMdns() {
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbConfigService.java:421:    private int scanForAdbPort() {
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbConfigService.java:500:    private boolean isPortOpen(String host, int port) {
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbConfigService.java:511:    private String getDeviceIP() {
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbConfigService.java:534:    private void updateStatus(String status) {
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbConfigService.java:539:    private void saveLastPort(int port) {
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbConfigService.java:544:    private void createNotificationChannel() {
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbConfigService.java:559:    private Notification createNotification(String text) {
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbConfigService.java:574:    private void updateNotification(String text) {
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbHelper.java:11:import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;


看 AdbHelper 里的密钥管理：

grep -n "PrivateKey\|KeyPair\|keystore\|KeyStore\|generateKey\|loadKey\|saveKey\|getKey" ~/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbHelper.java | head -20


Output:
29:import java.security.KeyPair;
30:import java.security.KeyPairGenerator;
31:import java.security.PrivateKey;
211:        private PrivateKey privateKey;
225:            loadOrGenerateKeyPair();
229:        private void loadOrGenerateKeyPair() throws Exception {
255:                    generateNewKeyPairAndCert();
260:                generateNewKeyPairAndCert();
281:        private void generateNewKeyPairAndCert() throws Exception {
283:            KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
285:            KeyPair keyPair = keyGen.generateKeyPair();
303:        private X509Certificate generateSelfSignedCertificate(KeyPair keyPair) throws Exception {
328:        protected PrivateKey getPrivateKey() {


看 loadOrGenerateKeyPair 的完整代码：

sed -n '229,290p' ~/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbHelper.java


Output:
private void loadOrGenerateKeyPair() throws Exception {
            Log.i(TAG, "Loading or generating key pair");
            if (keyFile.exists() && pubKeyFile.exists() && certFile.exists()) {
                Log.i(TAG, "Loading existing key pair and certificate");
                try {
                    // Load private key
                    byte[] privateKeyBytes = readFileBytes(keyFile);
                    PKCS8EncodedKeySpec privateSpec = new PKCS8EncodedKeySpec(privateKeyBytes);
                    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
                    privateKey = keyFactory.generatePrivate(privateSpec);
                    Log.i(TAG, "Private key loaded");

                    // Load public key
                    byte[] publicKeyBytes = readFileBytes(pubKeyFile);
                    X509EncodedKeySpec publicSpec = new X509EncodedKeySpec(publicKeyBytes);
                    publicKey = keyFactory.generatePublic(publicSpec);
                    Log.i(TAG, "Public key loaded");

                    // Load certificate
                    byte[] certBytes = readFileBytes(certFile);
                    CertificateFactory cf = CertificateFactory.getInstance("X.509");
                    certificate = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(certBytes));
                    Log.i(TAG, "Certificate loaded");

                } catch (Exception e) {
                    Log.e(TAG, "Failed to load existing keys, generating new ones", e);
                    generateNewKeyPairAndCert();
                }

            } else {
                Log.i(TAG, "No existing keys found, generating new ones");
                generateNewKeyPairAndCert();
            }
        }

        private byte[] readFileBytes(File file) throws IOException {
            byte[] bytes = new byte[(int) file.length()];
            try (FileInputStream fis = new FileInputStream(file)) {
                int bytesRead = fis.read(bytes);
                if (bytesRead != bytes.length) {
                    throw new IOException("Failed to read entire file");
                }
            }
            return bytes;
        }

        private void writeFileBytes(File file, byte[] bytes) throws IOException {
            try (FileOutputStream fos = new FileOutputStream(file)) {
                fos.write(bytes);
            }
        }

        private void generateNewKeyPairAndCert() throws Exception {
            Log.i(TAG, "Generating new RSA key pair");
            KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
            keyGen.initialize(2048, new SecureRandom());
            KeyPair keyPair = keyGen.generateKeyPair();
            privateKey = keyPair.getPrivate();
            publicKey = keyPair.getPublic();
            Log.i(TAG, "Key pair generated");

            // Generate self-signed certificate


看 keyFile 定义在哪里：

grep -n "keyFile\|pubKeyFile\|certFile\|filesDir\|getFilesDir" ~/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbHelper.java | head -20


Output:
214:        private final File keyFile;
215:        private final File pubKeyFile;
216:        private final File certFile;
221:            keyFile = new File(context.getFilesDir(), "adb_key");
222:            pubKeyFile = new File(context.getFilesDir(), "adb_key.pub");
223:            certFile = new File(context.getFilesDir(), "adb_cert");
224:            Log.i(TAG, "Key files: " + keyFile.getAbsolutePath());
231:            if (keyFile.exists() && pubKeyFile.exists() && certFile.exists()) {
235:                    byte[] privateKeyBytes = readFileBytes(keyFile);
242:                    byte[] publicKeyBytes = readFileBytes(pubKeyFile);
248:                    byte[] certBytes = readFileBytes(certFile);
297:            writeFileBytes(keyFile, privateKey.getEncoded());
298:            writeFileBytes(pubKeyFile, publicKey.getEncoded());
299:            writeFileBytes(certFile, certificate.getEncoded());


**找到问题了！**

密钥存储在 `context.getFilesDir()` 即 `/data/data/com.tpn.adbautoenable.mod/files/`

但是配对信息不是存在这里的！配对是通过 Android 的 ADB 配对协议完成的，配对后的授权信息存储在**系统的 ADB 密钥数据库**里，和 App 的包名关联。

改了包名后，系统认为这是一个新的 App，之前的配对授权不适用了。

**解决方案：**
1. 把包名改回 `com.tpn.adbautoenable`（覆盖原版）
2. 或者每次重启后重新配对（不实际）

你想怎么处理？改回原包名覆盖原版？


不对，不是这样，刚刚已经配对了新版app了，现在重启就丢了


你说得对！配对成功后重启就丢了，说明是**密钥存储**的问题。

让我检查密钥文件是否真的保存了：

Explanation:
用户说得对，刚才已经配对了新版 App（com.tpn.adbautoenable.mod），配对成功后测试也成功了。但重启后配对信息丢失了。

这说明问题不是包名的问题，而是配对密钥的存储问题。

让我想想：
1. 配对时，App 生成了一个 RSA 密钥对
2. 配对成功后，这个密钥被 Android 系统记住了
3. 重启后，App 应该用同样的密钥去连接

但如果重启后 App 重新生成了新的密钥对，那系统就不认识这个新密钥了，所以显示 isPaired: false。

问题可能是：
1. 密钥文件没有正确保存
2. 密钥文件保存了但重启后读取失败
3. 密钥文件的存储位置有问题（比如存在了临时目录）

让我检查一下密钥文件的存储逻辑和实际存储位置。

ssh 100.114.227.26 -p 8022 "
echo '=== 检查新版 App 密钥文件 ==='
adb connect 127.0.0.1:5555 2>/dev/null
adb shell run-as com.tpn.adbautoenable.mod ls -la /data/data/com.tpn.adbautoenable.mod/files/ 2>/dev/null || echo '无法用 run-as'

echo ''
echo '=== 检查原版 App 密钥文件 ==='
adb shell run-as com.tpn.adbautoenable ls -la /data/data/com.tpn.adbautoenable/files/ 2>/dev/null || echo '无法用 run-as'
"


Output:
=== 检查新版 App 密钥文件 ===
failed to connect to '127.0.0.1:5555': Connection refused
无法用 run-as

=== 检查原版 App 密钥文件 ===
无法用 run-as


没有 ADB 连接，无法检查。让我看代码里密钥是怎么存储的：

echo "=== 检查 SimpleAdbManager 构造函数 ==="
sed -n '210,230p' ~/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbHelper.java


Output:
=== 检查 SimpleAdbManager 构造函数 ===
    private static class SimpleAdbManager extends AbsAdbConnectionManager {
        private PrivateKey privateKey;
        private PublicKey publicKey;
        private X509Certificate certificate;
        private final File keyFile;
        private final File pubKeyFile;
        private final File certFile;

        public SimpleAdbManager(Context context) throws Exception {
            Log.i(TAG, "SimpleAdbManager constructor starting");
            setApi(Build.VERSION.SDK_INT);
            keyFile = new File(context.getFilesDir(), "adb_key");
            pubKeyFile = new File(context.getFilesDir(), "adb_key.pub");
            certFile = new File(context.getFilesDir(), "adb_cert");
            Log.i(TAG, "Key files: " + keyFile.getAbsolutePath());
            loadOrGenerateKeyPair();
            Log.i(TAG, "SimpleAdbManager initialized successfully");
        }

        private void loadOrGenerateKeyPair() throws Exception {
            Log.i(TAG, "Loading or generating key pair");


密钥存在 `context.getFilesDir()` 里，这是 App 私有目录，重启不应该丢失。

**等等，问题可能是：**
- 配对时生成了新密钥
- 但重启后 App 又生成了**另一个新密钥**
- 新密钥和系统里存的配对信息不匹配

让我检查是否每次都重新生成：

echo "=== 检查密钥加载逻辑 ==="
sed -n '229,265p' ~/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbHelper.java


Output:
=== 检查密钥加载逻辑 ===
        private void loadOrGenerateKeyPair() throws Exception {
            Log.i(TAG, "Loading or generating key pair");
            if (keyFile.exists() && pubKeyFile.exists() && certFile.exists()) {
                Log.i(TAG, "Loading existing key pair and certificate");
                try {
                    // Load private key
                    byte[] privateKeyBytes = readFileBytes(keyFile);
                    PKCS8EncodedKeySpec privateSpec = new PKCS8EncodedKeySpec(privateKeyBytes);
                    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
                    privateKey = keyFactory.generatePrivate(privateSpec);
                    Log.i(TAG, "Private key loaded");

                    // Load public key
                    byte[] publicKeyBytes = readFileBytes(pubKeyFile);
                    X509EncodedKeySpec publicSpec = new X509EncodedKeySpec(publicKeyBytes);
                    publicKey = keyFactory.generatePublic(publicSpec);
                    Log.i(TAG, "Public key loaded");

                    // Load certificate
                    byte[] certBytes = readFileBytes(certFile);
                    CertificateFactory cf = CertificateFactory.getInstance("X.509");
                    certificate = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(certBytes));
                    Log.i(TAG, "Certificate loaded");

                } catch (Exception e) {
                    Log.e(TAG, "Failed to load existing keys, generating new ones", e);
                    generateNewKeyPairAndCert();
                }

            } else {
                Log.i(TAG, "No existing keys found, generating new ones");
                generateNewKeyPairAndCert();
            }
        }

        private byte[] readFileBytes(File file) throws IOException {
            byte[] bytes = new byte[(int) file.length()];


逻辑是对的：如果文件存在就加载，不存在才生成。

**问题可能是：配对用的密钥和连接用的密钥不是同一个！**

让我看配对逻辑在哪里：

echo "=== 搜索配对相关代码 ==="
grep -rn "pair\|Pair" ~/adb-auto-enable-mod/app/src/main/java/ | grep -v ".class" | head -30


Output:
=== 搜索配对相关代码 ===
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbHelper.java:29:import java.security.KeyPair;
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbHelper.java:30:import java.security.KeyPairGenerator;
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbHelper.java:63:    public boolean pair(String host, int port, String code) {
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbHelper.java:66:            Log.i(TAG, "Pairing with " + host + ":" + port + " using code: " + code);
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbHelper.java:68:            manager.pair(host, port, code);
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbHelper.java:69:            Log.i(TAG, "Pairing successful!");
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbHelper.java:72:            Log.e(TAG, "Pairing failed", e);
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbHelper.java:79:                    Log.e(TAG, "Error closing manager after pair", e);
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbHelper.java:225:            loadOrGenerateKeyPair();
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbHelper.java:229:        private void loadOrGenerateKeyPair() throws Exception {
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbHelper.java:230:            Log.i(TAG, "Loading or generating key pair");
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbHelper.java:232:                Log.i(TAG, "Loading existing key pair and certificate");
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbHelper.java:255:                    generateNewKeyPairAndCert();
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbHelper.java:260:                generateNewKeyPairAndCert();
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbHelper.java:281:        private void generateNewKeyPairAndCert() throws Exception {
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbHelper.java:282:            Log.i(TAG, "Generating new RSA key pair");
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbHelper.java:283:            KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbHelper.java:285:            KeyPair keyPair = keyGen.generateKeyPair();
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbHelper.java:286:            privateKey = keyPair.getPrivate();
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbHelper.java:287:            publicKey = keyPair.getPublic();
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbHelper.java:288:            Log.i(TAG, "Key pair generated");
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbHelper.java:292:            certificate = generateSelfSignedCertificate(keyPair);
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbHelper.java:303:        private X509Certificate generateSelfSignedCertificate(KeyPair keyPair) throws Exception {
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbHelper.java:308:            SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded());
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbHelper.java:320:                    .build(keyPair.getPrivate());
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/WebServer.java:45:        if (uri.equals("/api/pair") && method == Method.POST) {
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/WebServer.java:46:            return handlePairing(session);
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/WebServer.java:62:    private Response handlePairing(IHTTPSession session) {
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/WebServer.java:74:            Log.i(TAG, "Web API: Received pairing request - port: " + portStr + ", code: " + code);
/home/ubuntu/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/WebServer.java:82:            Log.i(TAG, "Web API: Pairing on port " + port + " with code " + code);


看 pair 方法：

sed -n '63,85p' ~/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbHelper.java


Output:
public boolean pair(String host, int port, String code) {
        SimpleAdbManager manager = null;
        try {
            Log.i(TAG, "Pairing with " + host + ":" + port + " using code: " + code);
            manager = new SimpleAdbManager(context);
            manager.pair(host, port, code);
            Log.i(TAG, "Pairing successful!");
            return true;
        } catch (Exception e) {
            Log.e(TAG, "Pairing failed", e);
            return false;
        } finally {
            if (manager != null) {
                try {
                    manager.close();
                } catch (Exception e) {
                    Log.e(TAG, "Error closing manager after pair", e);
                }
            }
        }
    }

    public boolean connect(String host, int port) {


**发现问题了！**

每次调用 `pair()` 或 `connect()` 都会 `new SimpleAdbManager(context)`，而 SimpleAdbManager 构造函数里会调用 `loadOrGenerateKeyPair()`。

如果第一次配对时文件不存在，会生成新密钥并保存。但问题是：**配对成功后，密钥文件真的保存了吗？**

让我检查 `generateNewKeyPairAndCert` 是否正确保存：

sed -n '281,302p' ~/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/AdbHelper.java


Output:
private void generateNewKeyPairAndCert() throws Exception {
            Log.i(TAG, "Generating new RSA key pair");
            KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
            keyGen.initialize(2048, new SecureRandom());
            KeyPair keyPair = keyGen.generateKeyPair();
            privateKey = keyPair.getPrivate();
            publicKey = keyPair.getPublic();
            Log.i(TAG, "Key pair generated");

            // Generate self-signed certificate
            Log.i(TAG, "Generating self-signed certificate");
            certificate = generateSelfSignedCertificate(keyPair);
            Log.i(TAG, "Certificate generated");

            // Save keys
            Log.i(TAG, "Saving keys to files");
            writeFileBytes(keyFile, privateKey.getEncoded());
            writeFileBytes(pubKeyFile, publicKey.getEncoded());
            writeFileBytes(certFile, certificate.getEncoded());
            Log.i(TAG, "Keys and certificate saved successfully");
        }


保存逻辑看起来没问题。

**等等，我想到了！** 问题可能是：

Android 的 `context.getFilesDir()` 在 **Direct Boot** 模式下（解锁前）是不可访问的！

App 设置了 `directBootAware="true"`，意味着它在用户解锁屏幕前就启动了。但此时 `getFilesDir()` 返回的是加密存储，无法读取！

需要用 `createDeviceProtectedStorageContext().getFilesDir()` 来存储密钥，这样在 Direct Boot 模式下也能访问。

**这就是为什么重启后配对丢失 —— 密钥文件在解锁前读不到，所以生成了新密钥！**

要修复吗？


[System note: Your previous turn was interrupted before you could process the last tool result(s). The conversation history contains tool outputs you haven't responded to yet. Please finish processing those results and summarize what was accomplished, then address the user's new message below.]

步，我让去看新版app，现在已经配对了，你判断是错误的


Explanation: 用户说新版 App 现在已经配对了，让我重新检查状态。

用户说新版 App 现在已经配对了，让我重新检查状态。


Output: {"lastStatus":"Scanning for ADB port...","lastPort":5555,"isPaired":false,"hasPermission":true,"adb5555Available":false}


`isPaired: false`，但你说已经配对了。

让我看看 `isPaired` 的判断逻辑是不是有问题：

sed -n '135,165p' ~/adb-auto-enable-mod/app/src/main/java/com/tpn/adbautoenable/WebServer.java


Output:
private Response handleStatus() {
        Log.d(TAG, "handleStatus() called - creating socket for port check");
        SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
        String lastStatus = prefs.getString("last_status", "Not run yet");
        int lastPort = prefs.getInt("last_port", -1);
        boolean isPaired = prefs.getBoolean("is_paired", false);

        Log.d(TAG, "handleStatus() - calling checkPort5555()");
        boolean adb5555Available = checkPort5555();

        // Cache permission check - only do it once
        if (permissionCached == null) {
            Log.d(TAG, "handleStatus() - checking WRITE_SECURE_SETTINGS permission (cached)");
            try {
                Settings.Global.putInt(context.getContentResolver(), "adb_wifi_enabled", 1);
                permissionCached = true;
                Log.d(TAG, "handleStatus() - permission check SUCCESS");
            } catch (SecurityException e) {
                permissionCached = false;
                Log.d(TAG, "handleStatus() - permission check FAILED");
            }
        }

        String json = String.format(Locale.US,
                "{\"lastStatus\":\"%s\",\"lastPort\":%d,\"isPaired\":%b,\"hasPermission\":%b,\"adb5555Available\":%b}",
                lastStatus, lastPort, isPaired, permissionCached, adb5555Available
        );
        Log.d(TAG, "handleStatus() completed");
        return newFixedLengthResponse(Response.Status.OK, "application/json", json);
    }


`isPaired` 只是读 SharedPreferences 里的 `is_paired` 标志，不是真正检查密钥是否有效。

**问题是：** SharedPreferences 存在普通存储里，重启后在 Direct Boot 模式下读不到，所以 `is_paired` 变成了默认值 `false`。

但实际密钥文件可能还在。让我检查密钥文件是否存在：

ssh 100.114.227.26 -p 8022 "
echo '=== 检查新版 App 密钥文件 ==='
ls -la /data/data/com.tpn.adbautoenable.mod/files/ 2>/dev/null || echo '无权限直接访问'

echo ''
echo '=== 通过 App 日志检查 ==='
logcat -d | grep -i 'key\|pair\|load\|generat' | grep ADBAutoEnable | tail -20
"


Output:
=== 检查新版 App 密钥文件 ===
无权限直接访问

=== 通过 App 日志检查 ===


日志被清了。让我触发一次连接看日志：

ssh 100.114.227.26 -p 8022 "
logcat -c
curl -s 'http://127.0.0.1:9094/api/test'
sleep 10
logcat -d | grep -i ADBAutoEnable | head -40
"


Output: {"success":true,"message":"Boot test started. Check logs below for progress."}


没日志输出。让我直接看 logcat：

ssh 100.114.227.26 -p 8022 "logcat -d -s ADBAutoEnable:* 2>/dev/null | tail -50"


ssh 100.114.227.26 -p 8022 "logcat -d 2>/dev/null | grep -E 'ADB|key|Key|pair|Pair|load|Load|generat' | tail -30"


Output:
05-18 14:36:31.832 19986 19986 W sh      : type=1400 audit(0.0:190753): avc:  granted  { execute } for  path="/data/data/com.termux/files/usr/lib/libtermux-exec-ld-preload.so" dev="dm-76" ino=2312286 scontext=u:r:untrusted_app_27:s0:c163,c257,c512,c768 tcontext=u:object_r:app_data_file:s0:c163,c257,c512,c768 tclass=file app=com.termux.api
05-18 14:36:32.848 20001 20001 W sh      : type=1400 audit(0.0:190764): avc:  granted  { execute } for  path="/data/data/com.termux/files/usr/lib/libtermux-exec-ld-preload.so" dev="dm-76" ino=2312286 scontext=u:r:untrusted_app_27:s0:c163,c257,c512,c768 tcontext=u:object_r:app_data_file:s0:c163,c257,c512,c768 tclass=file app=com.termux.api
05-18 14:36:33.864 20018 20018 W sh      : type=1400 audit(0.0:190775): avc:  granted  { execute } for  path="/data/data/com.termux/files/usr/lib/libtermux-exec-ld-preload.so" dev="dm-76" ino=2312286 scontext=u:r:untrusted_app_27:s0:c163,c257,c512,c768 tcontext=u:object_r:app_data_file:s0:c163,c257,c512,c768 tclass=file app=com.termux.api
05-18 14:36:34.880 20031 20031 W sh      : type=1400 audit(0.0:190786): avc:  granted  { execute } for  path="/data/data/com.termux/files/usr/lib/libtermux-exec-ld-preload.so" dev="dm-76" ino=2312286 scontext=u:r:untrusted_app_27:s0:c163,c257,c512,c768 tcontext=u:object_r:app_data_file:s0:c163,c257,c512,c768 tclass=file app=com.termux.api
05-18 14:36:35.896 20045 20045 W sh      : type=1400 audit(0.0:190797): avc:  granted  { execute } for  path="/data/data/com.termux/files/usr/lib/libtermux-exec-ld-preload.so" dev="dm-76" ino=2312286 scontext=u:r:untrusted_app_27:s0:c163,c257,c512,c768 tcontext=u:object_r:app_data_file:s0:c163,c257,c512,c768 tclass=file app=com.termux.api
05-18 14:36:36.912 20098 20098 W sh      : type=1400 audit(0.0:190808): avc:  granted  { execute } for  path="/data/data/com.termux/files/usr/lib/libtermux-exec-ld-preload.so" dev="dm-76" ino=2312286 scontext=u:r:untrusted_app_27:s0:c163,c257,c512,c768 tcontext=u:object_r:app_data_file:s0:c163,c257,c512,c768 tclass=file app=com.termux.api
05-18 14:36:37.928 20133 20133 W sh      : type=1400 audit(0.0:190853): avc:  granted  { execute } for  path="/data/data/com.termux/files/usr/lib/libtermux-exec-ld-preload.so" dev="dm-76" ino=2312286 scontext=u:r:untrusted_app_27:s0:c163,c257,c512,c768 tcontext=u:object_r:app_data_file:s0:c163,c257,c512,c768 tclass=file app=com.termux.api
05-18 14:36:38.948 20166 20166 W sh      : type=1400 audit(0.0:190947): avc:  granted  { execute } for  path="/data/data/com.termux/files/usr/lib/libtermux-exec-ld-preload.so" dev="dm-76" ino=2312286 scontext=u:r:untrusted_app_27:s0:c163,c257,c512,c768 tcontext=u:object_r:app_data_file:s0:c163,c257,c512,c768 tclass=file app=com.termux.api
05-18 14:36:39.968 20190 20190 W sh      : type=1400 audit(0.0:190958): avc:  granted  { execute } for  path="/data/data/com.termux/files/usr/lib/libtermux-exec-ld-preload.so" dev="dm-76" ino=2312286 scontext=u:r:untrusted_app_27:s0:c163,c257,c512,c768 tcontext=u:object_r:app_data_file:s0:c163,c257,c512,c768 tclass=file app=com.termux.api
05-18 14:36:40.988 20300 20300 W sh      : type=1400 audit(0.0:190969): avc:  granted  { execute } for  path="/data/data/com.termux/files/usr/lib/libtermux-exec-ld-preload.so" dev="dm-76" ino=2312286 scontext=u:r:untrusted_app_27:s0:c163,c257,c512,c768 tcontext=u:object_r:app_data_file:s0:c163,c257,c512,c768 tclass=file app=com.termux.api
05-18 14:36:42.000 20307 20307 W sh      : type=1400 audit(0.0:190980): avc:  granted  { execute } for  path="/data/data/com.termux/files/usr/lib/libtermux-exec-ld-preload.so" dev="dm-76" ino=2312286 scontext=u:r:untrusted_app_27:s0:c163,c257,c512,c768 tcontext=u:object_r:app_data_file:s0:c163,c257,c512,c768 tclass=file app=com.termux.api
05-18 14:36:43.016 20316 20316 W sh      : type=1400 audit(0.0:190991): avc:  granted  { execute } for  path="/data/data/com.termux/files/usr/lib/libtermux-exec-ld-preload.so" dev="dm-76" ino=2312286 scontext=u:r:untrusted_app_27:s0:c163,c257,c512,c768 tcontext=u:object_r:app_data_file:s0:c163,c257,c512,c768 tclass=file app=com.termux.api
05-18 14:36:44.036 20320 20320 W sh      : type=1400 audit(0.0:191002): avc:  granted  { execute } for  path="/data/data/com.termux/files/usr/lib/libtermux-exec-ld-preload.so" dev="dm-76" ino=2312286 scontext=u:r:untrusted_app_27:s0:c163,c257,c512,c768 tcontext=u:object_r:app_data_file:s0:c163,c257,c512,c768 tclass=file app=com.termux.api
05-18 14:36:45.032 20332 20332 W coreutils: type=1400 audit(0.0:191093): avc:  granted  { execute } for  path="/data/data/com.termux/files/usr/lib/libtermux-exec-ld-preload.so" dev="dm-76" ino=2312286 scontext=u:r:untrusted_app_27:s0:c163,c257,c512,c768 tcontext=u:object_r:app_data_file:s0:c163,c257,c512,c768 tclass=file app=com.termux.api


没有 App 的日志。你说已经配对了，让我直接测试连接能不能用：


Output:
=== 扫描开放端口 ===

=== 扫描 30000-50000 (采样) ===


没有任何端口开放。**无线调试根本没启动！**

ssh 100.114.227.26 -p 8022 "
echo '=== 检查无线调试状态 ==='
settings get global adb_wifi_enabled
"


Output:
=== 检查无线调试状态 ===

Exception occurred while executing 'get':
java.lang.SecurityException: Permission Denial: getCurrentUser() from pid=20598, uid=10419 requires android.permission.INTERACT_ACROSS_USERS
	at com.android.server.am.UserController.checkGetCurrentUserPermissions(UserController.java:3365)
	at com.android.server.am.UserController.getCurrentUser(UserController.java:3370)
	at com.android.server.am.ActivityManagerService.getCurrentUser(ActivityManagerService.java:17671)
	at com.android.providers.settings.SettingsService$MyShellCommand.onCommand(SourceFile:240)
	at com.android.modules.utils.BasicShellCommandHandler.exec(BasicShellCommandHandler.java:97)
	at android.os.ShellCommand.exec(ShellCommand.java:38)
	at com.android.providers.settings.SettingsService.onShellCommand(SourceFile:54)
	at android.os.Binder.shellCommand(Binder.java:1164)
	at android.os.Binder.onTransact(Binder.java:966)
	at android.os.Binder.execTransactInternal(Binder.java:1444)
	at android.os.Binder.execTransact(Binder.java:1378)


Termux 没权限查。**问题是无线调试没开！**

你需要手动打开无线调试，然后给我配对码和端口。


你说得对。让我检查这个日志是哪个 App 产生的：

Explanation:
用户说禁止一直重新编译，要先检查问题。

日志显示：
- `Still waiting for WiFi... (10s)` - 这是原版的日志
- `mDNS discovery timed out` - 这是原版的逻辑

但我检查源码，waitForWifiConnection 已经被修改了，应该输出 "Skipping WiFi check"。

问题可能是：
1. 日志来自原版 App (com.tpn.adbautoenable)，不是新版 (com.tpn.adbautoenable.mod)
2. 或者安装的确实不是最新的

让我检查日志里的进程 ID 对应哪个 App。

ssh 100.114.227.26 -p 8022 "
echo '=== 检查进程 10902 是哪个 App ==='
ps -p 10902 -o pid,name,args 2>/dev/null || ps aux | grep 10902

echo ''
echo '=== 两个 App 的进程 ==='
ps aux | grep adbautoenable | grep -v grep
"


Output:
=== 检查进程 10902 是哪个 App ===
u0_a419  22305  2.0  0.0 2133200 4912 ?        S<s   1970   0:00 /data/data/com.termux/files/usr/bin/bash -c  echo '=== ???????????? 10902 ????????? App ===' ps -p 10902 -o pid,name,args 2>/dev/null || ps aux | grep 10902  echo '' echo '=== ?????? App ????????? ===' ps aux | grep adbautoenable | grep -v grep
u0_a419  22315  0.0  0.0 2131276 4016 ?        S<    1970   0:00 grep 10902

=== 两个 App 的进程 ===


进程 10902 已经不存在了。让我看日志里的包名：

ssh 100.114.227.26 -p 8022 "
echo '=== 查看完整日志确认是哪个 App ==='
logcat -d | grep -E 'ADBAutoEnable|adbautoenable' | tail -30
"


Output: === 查看完整日志确认是哪个 App ===


日志被清了。你刚才看到的日志 `Still waiting for WiFi` 是**原版 App (com.tpn.adbautoenable)** 的行为，不是新版的。

因为：
1. 新版源码里 `waitForWifiConnection` 已经改成 `Skipping WiFi check`
2. 原版没改，还是 `Still waiting for WiFi`

**问题是：开机时原版 App 也在运行，它的日志混在一起了。**

让我确认两个 App 的状态：


Output:
=== 原版 App 状态 (9093) ===
{"lastStatus":"Enabling wireless debugging...","lastPort":44587,"isPaired":true,"hasPermission":true,"adb5555Available":false}
=== 新版 App 状态 (9094) ===
{"lastStatus":"Scanning for ADB port...","lastPort":5555,"isPaired":false,"hasPermission":true,"adb5555Available":false}


**确认了：**
- **原版 (9093)**：`isPaired: true`，`lastStatus: "Enabling wireless debugging..."`
- **新版 (9094)**：`isPaired: false`，`lastStatus: "Scanning for ADB port..."`

那个 `Still waiting for WiFi` 日志是**原版 App** 产生的。

**新版的问题是 `isPaired: false`** — 配对信息丢失了。这才是真正的问题。

你之前配对新版成功了，但重启后 `isPaired` 变成 `false`。这说明 SharedPreferences 里的 `is_paired` 标志在重启后丢失了，可能是 Direct Boot 模式下读不到。