如何在Android Java中实现无需桌面ADB服务器的ADB环回功能?
实现无需桌面ADB服务器的本地ADB操作(Android Java)
一、核心原理
ADB无线调试支持设备通过本地环回地址与自身的ADB守护进程通信。要实现无桌面服务器的操作,核心逻辑是:
- 生成符合ADB规范的自签名密钥对,完成设备与自身的配对
- 应用内部建立Socket连接到本地ADB服务(默认端口5555)
- 按照ADB协议封装命令发送与响应解析逻辑
二、关键步骤与代码实现
1. 生成ADB配对密钥对
ADB配对需要adbkey(私钥)和adbkey.pub(公钥),可在应用内部生成:
import java.io.File; import java.io.FileOutputStream; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.PrivateKey; import java.security.PublicKey; import java.security.spec.RSAKeyGenParameterSpec; public class AdbKeyGenerator { private static final String ALGORITHM = "RSA"; private static final int KEY_SIZE = 2048; public static void generateKeys(File keyDir) throws Exception { keyDir.mkdirs(); KeyPairGenerator keyGen = KeyPairGenerator.getInstance(ALGORITHM); keyGen.initialize(new RSAKeyGenParameterSpec(KEY_SIZE, RSAKeyGenParameterSpec.F4)); KeyPair keyPair = keyGen.generateKeyPair(); // 保存私钥到adbkey PrivateKey privateKey = keyPair.getPrivate(); try (FileOutputStream fos = new FileOutputStream(new File(keyDir, "adbkey"))) { fos.write(privateKey.getEncoded()); } // 保存公钥到adbkey.pub(ADB要求格式:ssh-rsa <base64编码> user@host) PublicKey publicKey = keyPair.getPublic(); String pubContent = "ssh-rsa " + android.util.Base64.encodeToString(publicKey.getEncoded(), android.util.Base64.DEFAULT) + " android@local"; try (FileOutputStream fos = new FileOutputStream(new File(keyDir, "adbkey.pub"))) { fos.write(pubContent.getBytes()); } } }
2. 完成本地ADB配对
需要将生成的密钥对放到ADB认可的系统目录(通常为/data/misc/adb),这一步需要ROOT权限;若无ROOT,可引导用户手动开启无线调试并完成本地配对:
public class AdbPairingHelper { public static boolean pairWithLocalAdb(File keyDir) { String copyCmd = String.format( "cp %s/adbkey* /data/misc/adb/ && chmod 600 /data/misc/adb/adbkey* && chown adb:adb /data/misc/adb/adbkey*", keyDir.getAbsolutePath() ); try { Process suProcess = Runtime.getRuntime().exec("su"); try (OutputStream os = suProcess.getOutputStream()) { os.write((copyCmd + "\n").getBytes()); os.flush(); } return suProcess.waitFor() == 0; } catch (Exception e) { e.printStackTrace(); return false; } } }
3. 连接本地ADB服务并执行命令
ADB基于自定义TCP协议,通过Socket连接到localhost:5555后,按照协议格式发送命令:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.Socket; public class LocalAdbClient { private Socket adbSocket; private OutputStream outputStream; private BufferedReader inputReader; public boolean connect() throws IOException { adbSocket = new Socket("localhost", 5555); outputStream = adbSocket.getOutputStream(); inputReader = new BufferedReader(new InputStreamReader(adbSocket.getInputStream())); // 发送ADB握手标识(固定为"ADB0") outputStream.write(new byte[]{0x41, 0x44, 0x42, 0x30}); outputStream.flush(); // 验证服务器响应 byte[] respBuffer = new byte[4]; inputReader.read(respBuffer); return new String(respBuffer).equals("OKAY"); } public String executeCommand(String command) throws IOException { if (!adbSocket.isConnected()) { throw new IOException("ADB socket not connected"); } // ADB命令格式:4字节十六进制长度 + 命令内容 String cmdWithNewLine = command + "\n"; String lengthHex = String.format("%04x", cmdWithNewLine.length()); outputStream.write(lengthHex.getBytes()); outputStream.write(cmdWithNewLine.getBytes()); outputStream.flush(); // 解析响应 StringBuilder response = new StringBuilder(); String line; while ((line = inputReader.readLine()) != null) { if (line.startsWith("OKAY")) { continue; } if (line.startsWith("FAIL")) { throw new IOException("Command failed: " + line.substring(4)); } response.append(line).append("\n"); } return response.toString().trim(); } public void disconnect() throws IOException { if (adbSocket != null) { adbSocket.close(); } } }
4. 整合使用示例
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); File keyDir = new File(getFilesDir(), "adb_keys"); try { // 生成密钥对 AdbKeyGenerator.generateKeys(keyDir); // 完成配对(需ROOT) if (AdbPairingHelper.pairWithLocalAdb(keyDir)) { LocalAdbClient adbClient = new LocalAdbClient(); if (adbClient.connect()) { // 执行示例命令:获取窗口信息 String result = adbClient.executeCommand("shell dumpsys window"); Log.d("ADB_OUTPUT", result); adbClient.disconnect(); } } } catch (Exception e) { e.printStackTrace(); } } }
三、注意事项
- 权限要求:复制密钥到系统目录需要ROOT权限;无ROOT时,需引导用户手动开启「无线调试」并完成本地配对(路径:设置 -> 开发者选项 -> 无线调试 -> 配对设备)。
- 协议兼容性:不同Android版本的ADB守护进程行为可能有差异,需多设备测试;复杂操作(如文件传输)需扩展协议解析逻辑。
- 权限申请:应用需声明
INTERNET权限(即使是本地连接)。 - 安全性:生成的密钥对需妥善存储,避免泄露。
内容的提问来源于stack exchange,提问作者Adam Taylor
相关产品推荐
相关产品推荐

