Overview
Client、Server和Daemon
PC端有一个ADB.exe,它既是Client,也是Server,而Daemon则是存在于手机端,Daemon需要在开发者模式中手动开启。
Client为我们使用ADB功能提供API,我们通过Client来执行ADB操作,但它只是请求的发起者而非执行者,具体执行请求是在Server和Daemon中完成。Server在后台运行,它的作用是作为沟通Client与Daemon的桥梁,由于Client与Daemon并未直接建立连接,Client与Daemon的交互由Server代为进行,Server也可以将Client指定的Daemon交给Client直接操作。
Service
ADB的具体功能即Service分两种:Host Service和Local Service。Host Service由Server完成,例如adb devices命令就是Client向Server发起的请求,Server来查询连接到Server的Daemon数量和信息后返回给Client。Local Service由Daemon完成,例如adb shell。
启动流程
使用adb start-server会在PC端启动ADB server并监听5037端口等待Client连接。此外,执行adb shell
或adb devices
等部分命令会默认向localhost:5037进行请求,如果连接超时则会自动在5037端口启动Server。另外,在启动Server以及使用Client时,我们可以指定IP和Port,实现远程请求。
首先在一台机器上以1234端口启动ADB Server
代码语言:javascript复制adb -a -P 1234 nodaemon server
然后在另一台机器上用Client指定ip和端口进行连接
代码语言:javascript复制adb -H 10.176.36.22 -P 1234 devices
通信协议
Server启动后Client可以通过TCP向Server发起请求,格式如下
- 开头为4位16进制字符串头部,用于描述payload长度,长度不包括头部部分
- Payload的具体内容
比如查询adb的版本号,我们首先使用adb start-server
启动Server,然后使用socket连接localhost:5037
,再使用socket发送000Chost:version
,随后Server会返回OKAY
或者FAIL
,如果为OKAY
则会继续返回具体的版本信息。其中000C
即为头部,host:version
即为Payload,000C
代表Payload的长度为12。其中Payload的前缀host:
代表将执行Host Service中的操作。对于Local Service在下文中会提到。在接收到OKAY
消息以后,Client和Server的连接不会断开,可以复用该连接继续进行请求,但是某些特定的命令会在接收到OKAY
后改变连接的状态,比如host:transport:<serialnumber>
会将连接转移到指定设备的Daemon中,来直接与其进行通信。
misc
不难想象会存在Client和Server版本不匹配的情况,adb默认的操作是将server关闭并从adb.exe启动自己版本的server
API
参考文档
ADB Protocol
Python实现(部分功能)
代码语言:javascript复制import socket
class ADB:
OKAY = b"OKAY"
FAIL = b"FAIL"
_socket = None
def build(self,payload:str) -> bytes:
header = hex(len(payload.encode()))
header = str(header)[2:].rjust(4,"0")
return (header payload).encode()
def send(self,msg:str) -> str:
_msg = self.build(msg)
self._socket.send(_msg)
isOK = self._socket.recv(0x4)
if isOK != self.OKAY:
return False
return True
def recv(self,raw=False):
if raw:
return self._socket.recv(0x100)
msglen = self._socket.recv(0x4)
msglen = eval("0x" msglen.decode())
msg = self._socket.recv(msglen).decode()
return msg
def connectServer(self,ip="localhost",port=5037,timeout=3):
self._socket = socket.create_connection((ip,5037),timeout=timeout)
def getVersion(self):
self.send("host:version")
return self.recv()
if __name__ == "__main__":
adb = ADB()
adb.connectServer()
print(adb.getVersion())
# print(adb.recv(True))
# print(adb.recv(True))