幽沉谢世事,俯默窥唐虞。这篇文章主要讲述android adb 流程原理代码分析相关的知识,希望能为你提供帮助。
由于要用到adb的知识,但是对adb啥也不了解,看了下android的代码,adb的源码在system/core/adb下面,然后网上搜下了资料,发现很多大神的源码分析,瞬间信心爆棚,把大神写的博客都浏览了一遍,然后手动运行了下adb命令,顺便跟踪了下过程,发现原来还是很好的理解,源码的各种线程创建,函数回调,对于我这种基础不咋好的,,还是看的晕晕呼呼,现在把我自己的理解给大家分享,有理解错误的还请多多指正
文章图片
这样是不是容易理解多了呢,经过这样的数据发送,我们就通过adb push命令把本地的profile文件推送到远程设备的根目录了。哇..... 原来这么简单,一个profile文件就传输了。流程理解了,我们再来看代码,现在结果你知道了,流程你也懂了,再来看源码,是不是容易理解了呢。
(5)同样,我们看代码也是逆向的看,这样利于我们理解,不会被源码看到晕乎乎,上面流程懂了,知道了每次是以apacket的格式发送的,我们先来研究这个apacket的接收与发送函数。
接收函数handle_packet
void handle_packet(apacket *p, atransport *t)
{
asocket *s;
D("handle_packet() %c%c%c%c
", ((char*) (&
(p->
msg.command)))[0],
((char*) (&
(p->
msg.command)))[1],
((char*) (&
(p->
msg.command)))[2],
((char*) (&
(p->
msg.command)))[3]);
print_packet("recv", p);
switch(p->
msg.command){
case A_SYNC:
if(p->
msg.arg0){
send_packet(p, t);
if(HOST) send_connect(t);
} else {
t->
connection_state = CS_OFFLINE;
handle_offline(t);
send_packet(p, t);
}
return;
case A_CNXN: /* CONNECT(version, maxdata, "system-id-string") */
/* XXX verify version, etc */
if(t->
connection_state != CS_OFFLINE) {
t->
connection_state = CS_OFFLINE;
handle_offline(t);
}parse_banner((char*) p->
data, t);
if (HOST || !auth_enabled) {
handle_online(t);
if(!HOST) send_connect(t);
} else {
send_auth_request(t);
}
break;
case A_AUTH:
if (p->
msg.arg0 == ADB_AUTH_TOKEN) {
t->
key = adb_auth_nextkey(t->
key);
if (t->
key) {
send_auth_response(p->
data, p->
msg.data_length, t);
} else {
/* No more private keys to try, send the public key */
send_auth_publickey(t);
}
} else if (p->
msg.arg0 == ADB_AUTH_SIGNATURE) {
if (adb_auth_verify(t->
token, p->
data, p->
msg.data_length)) {
adb_auth_verified(t);
t->
failed_auth_attempts = 0;
} else {
if (t->
failed_auth_attempts++ >
10)
adb_sleep_ms(1000);
send_auth_request(t);
}
} else if (p->
msg.arg0 == ADB_AUTH_RSAPUBLICKEY) {
adb_auth_confirm_key(p->
data, p->
msg.data_length, t);
}
break;
case A_OPEN: /* OPEN(local-id, 0, "destination") */
if (t->
online) {
char *name = (char*) p->
data;
name[p->
msg.data_length >
0 ? p->
msg.data_length - 1 : 0] = 0;
s = create_local_service_socket(name);
if(s == 0) {
send_close(0, p->
msg.arg0, t);
} else {
s->
peer = create_remote_socket(p->
msg.arg0, t);
s->
peer->
peer = s;
send_ready(s->
id, s->
peer->
id, t);
s->
ready(s);
}
}
break;
case A_OKAY: /* READY(local-id, remote-id, "") */
if (t->
online) {
if((s = find_local_socket(p->
msg.arg1))) {
if(s->
peer == 0) {
s->
peer = create_remote_socket(p->
msg.arg0, t);
s->
peer->
peer = s;
}
s->
ready(s);
}
}
break;
case A_CLSE: /* CLOSE(local-id, remote-id, "") */
if (t->
online) {
if((s = find_local_socket(p->
msg.arg1))) {
s->
close(s);
}
}
break;
case A_WRTE:
if (t->
online) {
if((s = find_local_socket(p->
msg.arg1))) {
unsigned rid = p->
msg.arg0;
p->
len = p->
msg.data_length;
if(s->
enqueue(s, p) == 0) {
D("Enqueue the socket
");
send_ready(s->
id, rid, t);
}
return;
}
}
break;
default:
printf("handle_packet: what is %08x?!
", p->
msg.command);
}put_apacket(p);
}
哇,这个函数好像不复杂,一个函数,然后解析apacket *p数据,根据msg.command的命令值, 然后对应不同的case,有着不同的响应。事实上也就是这样,这个函数主要就是根据不同的消息类型,来处理这个apacket的数据。
上面不是有adb push命令吗,我们根据这个流程,看看handle_packet是否是跟我们预期的响应流程一样。
(5.1)OPEN响应
recv: OPEN 00141028 00000000 0006 "sync:."
send: OKAY 0000003e 00141028 0000 ""
接收到了OPEN的消息,然后附带了一个sync的数据,我们看看是如何响应的。
case A_OPEN: /* OPEN(local-id, 0, "destination") */
if (t->
online) {
char *name = (char*) p->
data;
name[p->
msg.data_length >
0 ? p->
msg.data_length - 1 : 0] = 0;
s = create_local_service_socket(name);
if(s == 0) {
send_close(0, p->
msg.arg0, t);
} else {
s->
peer = create_remote_socket(p->
msg.arg0, t);
s->
peer->
peer = s;
send_ready(s->
id, s->
peer->
id, t);
s->
ready(s);
}
}
break;
调用create_local_service_socket(“sync”);
fd = service_to_fd(name);
//创建本地socket,并为这个socket创建数据处理线程file_sync_service
ret = create_service_thread(file_sync_service, NULL);
//把这个本地socket关联到结构asocket *s
s = create_local_socket(fd);
调用create_remote_socket(p-> msg.arg0, t); //把远程的socket也与这个结构体asocket 关联。
如上两个函数调用,主要是初始化本地的socket对,本地socket用来跟后台服务线程之间的通信,以及跟对应命令的后台服务线程通信。初始化adb通信的环境。其中asocket *s为本地socket与远程socket的一个关联结构体,其中s保存的是本地socket的信息,s-> peer保存的是远程socket相关的信息。
send_ready(s-> id, s-> peer-> id, t); 然后发送OKAY给PC端。
static void send_ready(unsigned local, unsigned remote, atransport *t)
{
D("Calling send_ready
");
apacket *p = get_apacket();
p->
msg.command = A_OKAY;
p->
msg.arg0 = local;
p->
msg.arg1 = remote;
send_packet(p, t);
}
这个与我们看到的流程相符合。接收到OPEN的消息,初始化一些状态,然后返回一个OKAY的状态
(5.2)WRITE响应
recv: WRTE 00141028 0000003e 0009 "STAT..../"
send: OKAY 0000003e 00141028 0000 ""
send: WRTE 0000003e 00141028 0010 "STAT.A......[oHZ"
接收到了WRITE的消息,顺带了一个查询STAT的数据,我们看看是如何响应的:
case A_WRTE:
if (t->
online) {
if((s = find_local_socket(p->
msg.arg1))) {
unsigned rid = p->
msg.arg0;
p->
len = p->
msg.data_length;
if(s->
enqueue(s, p) == 0) {
D("Enqueue the socket
");
send_ready(s->
id, rid, t);
}
return;
}
}
break;
先通过参数p-> msg.arg1找到我们在OPEN的时候建立的结构体信息asocket *s, 然后处理本地socket队列中的数据(s为本地,s-> peer为远程)
s-> enqueue(s, p)即为之前 关联的函数local_socket_enqueue,其在create_local_socket(fd); 的时候设置。
static int local_socket_enqueue(asocket *s, apacket *p)
{
D("LS(%d): enqueue %d
", s->
id, p->
len);
p->
ptr = p->
data;
/* if there is already data queue‘d, we will receive
** events when it‘s time to write.just add this to
** the tail
*/
if(s->
pkt_first) {
goto enqueue;
}/* write as much as we can, until we
** would block or there is an error/eof
*/
while(p->
len >
0) {
int r = adb_write(s->
fd, p->
ptr, p->
len);
if(r >
0) {
p->
len -= r;
p->
ptr += r;
continue;
}
if((r == 0) || (errno != EAGAIN)) {
D( "LS(%d): not ready, errno=%d: %s
", s->
id, errno, strerror(errno) );
s->
close(s);
return 1;
/* not ready (error) */
} else {
break;
}
}if(p->
len == 0) {
put_apacket(p);
return 0;
/* ready for more data */
}enqueue:
p->
next = 0;
if(s->
pkt_first) {
s->
pkt_last->
next = p;
} else {
s->
pkt_first = p;
}
s->
pkt_last = p;
/* make sure we are notified when we can drain the queue */
fdevent_add(&
s->
fde, FDE_WRITE);
return 1;
/* not ready (backlog) */
}
我们通过adb_write(s-> fd, p-> ptr, p-> len)把要处理的数据,写入到本地socket对应的fd中,等待处理。
然后调用send_ready(s-> id, rid, t); 返回一个OKAY的状态
我们把待处理的数据adb_write之后,又是在哪里处理的呢,我们之前在创建本地socket的时候,就创建了一个线程,对应的处理socket数据的函数file_sync_service。
我们来看看file_sync_service函数是如何处理的
void file_sync_service(int fd, void *cookie)
{
syncmsg msg;
char name[1025];
unsigned namelen;
char *buffer = malloc(SYNC_DATA_MAX);
if(buffer == 0) goto fail;
for(;
;
) {
D("sync: waiting for command
");
if(readx(fd, &
msg.req, sizeof(msg.req))) {
fail_message(fd, "command read failure");
break;
}
namelen = ltohl(msg.req.namelen);
if(namelen >
1024) {
fail_message(fd, "invalid namelen");
break;
}
if(readx(fd, name, namelen)) {
fail_message(fd, "filename read failure");
break;
}
name[namelen] = 0;
msg.req.namelen = 0;
D("sync: ‘%s‘ ‘%s‘
", (char*) &
msg.req, name);
switch(msg.req.id) {
case ID_STAT:
if(do_stat(fd, name)) goto fail;
break;
case ID_LIST:
if(do_list(fd, name)) goto fail;
break;
case ID_SEND:
if(do_send(fd, name, buffer)) goto fail;
break;
case ID_RECV:
if(do_recv(fd, name, buffer)) goto fail;
break;
case ID_QUIT:
goto fail;
default:
fail_message(fd, "unknown command");
goto fail;
}
}fail:
if(buffer != 0) free(buffer);
D("sync: done
");
adb_close(fd);
}
原来在这里处理的数据,终于找到你, 我们收到的消息是查看路径是否存在,这里对应的就是ID_STAT,还有其他的消息处理,比如ID_SEND,ID_RECV,ID_QUIT,望文生义,我们就不具体解释了。我们还是看看ID_STAT对应的处理吧do_stat(fd, name)。
static int do_stat(int s, const char *path)
{
syncmsg msg;
struct stat st;
msg.stat.id = ID_STAT;
if(lstat(path, &
st)) {
msg.stat.mode = 0;
msg.stat.size = 0;
msg.stat.time = 0;
} else {
msg.stat.mode = htoll(st.st_mode);
msg.stat.size = htoll(st.st_size);
msg.stat.time = htoll(st.st_mtime);
}return writex(s, &
msg.stat, sizeof(msg.stat));
}
这里就是判断路径是否存在的逻辑了,这个就是我们想要的,我们把判断的结果存储在msg.stat, 然后把对应的结果写回去writex。
我们把检测的状态writex之后,但是这个数据还没有发送回PC端啊,是在哪里发送回去的呢,我们继续跟踪 我们在create_local_socket创建本地socket的时候,顺便还注册了一个回调函数local_socket_event_func
static void local_socket_event_func(int fd, unsigned ev, void *_s)
{
asocket *s = _s;
D("LS(%d): event_func(fd=%d(==%d), ev=%04x)
", s->
id, s->
fd, fd, ev);
/* put the FDE_WRITE processing before the FDE_READ
** in order to simplify the code.
*/
if(ev &
FDE_WRITE){
apacket *p;
while((p = s->
pkt_first) != 0) {
while(p->
len >
0) {
int r = adb_write(fd, p->
ptr, p->
len);
if(r >
0) {
p->
ptr += r;
p->
len -= r;
continue;
}
if(r <
0) {
/* returning here is ok because FDE_READ will
** be processed in the next iteration loop
*/
if(errno == EAGAIN) return;
if(errno == EINTR) continue;
}
D(" closing after write because r=%d and errno is %d
", r, errno);
s->
close(s);
return;
}if(p->
len == 0) {
s->
pkt_first = p->
next;
if(s->
pkt_first == 0) s->
pkt_last = 0;
put_apacket(p);
}
}/* if we sent the last packet of a closing socket,
** we can now destroy it.
*/
if (s->
closing) {
D(" closing because ‘closing‘ is set after write
");
s->
close(s);
return;
}/* no more packets queued, so we can ignore
** writable events again and tell our peer
** to resume writing
*/
fdevent_del(&
s->
fde, FDE_WRITE);
s->
peer->
ready(s->
peer);
}if(ev &
FDE_READ){
apacket *p = get_apacket();
unsigned char *x = p->
data;
size_t avail = MAX_PAYLOAD;
int r;
int is_eof = 0;
while(avail >
0) {
r = adb_read(fd, x, avail);
D("LS(%d): post adb_read(fd=%d,...) r=%d (errno=%d) avail=%d
", s->
id, s->
fd, r, r<
0?errno:0, avail);
if(r >
0) {
avail -= r;
x += r;
continue;
}
if(r <
0) {
if(errno == EAGAIN) break;
if(errno == EINTR) continue;
}/* r = 0 or unhandled error */
is_eof = 1;
break;
}
D("LS(%d): fd=%d post avail loop. r=%d is_eof=%d forced_eof=%d
",
s->
id, s->
fd, r, is_eof, s->
fde.force_eof);
if((avail == MAX_PAYLOAD) || (s->
peer == 0)) {
put_apacket(p);
} else {
p->
len = MAX_PAYLOAD - avail;
r = s->
peer->
enqueue(s->
peer, p);
D("LS(%d): fd=%d post peer->
enqueue(). r=%d
", s->
id, s->
fd, r);
if(r <
0) {
/* error return means they closed us as a side-effect
** and we must return immediately.
**
** note that if we still have buffered packets, the
** socket will be placed on the closing socket list.
** this handler function will be called again
** to process FDE_WRITE events.
*/
return;
}if(r >
0) {
/* if the remote cannot accept further events,
** we disable notification of READs.They‘ll
** be enabled again when we get a call to ready()
*/
fdevent_del(&
s->
fde, FDE_READ);
}
}
/* Don‘t allow a forced eof if data is still there */
if((s->
fde.force_eof &
&
!r) || is_eof) {
D(" closing because is_eof=%d r=%d s->
fde.force_eof=%d
", is_eof, r, s->
fde.force_eof);
s->
close(s);
}
}if(ev &
FDE_ERROR){
/* this should be caught be the next read or write
** catching it here means we may skip the last few
** bytes of readable data.
*/
//s->
close(s);
D("LS(%d): FDE_ERROR (fd=%d)
", s->
id, s->
fd);
return;
}
}
这个函数内容就比较多了,我们看后面if(ev & FDE_READ)部分:
adb_read(fd, x, avail); 把数据读出来,然后调用r = s-> peer-> enqueue(s-> peer, p); ,即把数据发送给远程socket的队列处理。(s-> speer即远程端,之前已经说明)
s-> peer-> enqueue函数即remote_socket_enqueue:
static int remote_socket_enqueue(asocket *s, apacket *p)
{
D("entered remote_socket_enqueue RS(%d) WRITE fd=%d peer.fd=%d
",
s->
id, s->
fd, s->
peer->
fd);
p->
msg.command = A_WRTE;
p->
msg.arg0 = s->
peer->
id;
p->
msg.arg1 = s->
id;
p->
msg.data_length = p->
len;
send_packet(p, s->
transport);
return 1;
}
这样我们就把STAT的结果,通过WRITE返回给了PC端
这个与我们看到的流程也是相符的,接收到WRITE(STAT)的消息,先返回一个OKAY的状态,在返回WRITE(STAT)的结果。
我们可以观察之前的数据接收及发送流程,可以发现每次一个WRITE消息,后面都是返回一个OKAY WRITE消息。
贴了这么多的代码,是不是有点晕了,再贴就真的看不下去了,我们下面重新来理一理思路。
1. adb其实就是个socket通信,数据发过来发过去。
2. adb每次都是发送的一个数据包,数据结构是struct apacket,其中包含msg消息部分,及data数据部分。
3. 从PC跟device通信的过程,有一条协议流程,通过不断的数据交互发送,实现数据文件传递。
4. 我们可以定义 #define DEBUG_PACKETS 1 这样可以看到socket通信的数据发送过程。
5. socket数据建立传输过程,会创建socket,创建事件监听线程,注册回调响应函数,乱七八糟的......
【android adb 流程原理代码分析】 6. 然后就是一系列的代码流程了,头晕了,我们下节再来详细理一理这个,为啥要按照你说的这个流程走,为啥你说走到这里,调用这个函数,凭啥相信你,下节见......
推荐阅读
- 安卓抽屉菜单(侧滑菜单)全屏设置
- 安卓真机或者模拟器运行安装应用时提示 Failure [INSTALL_FAILED_NO_MATCHING_ABIS: Failed to extract native libraries, res
- 安卓打印长日志
- [Java]基本資料包裝類別 Wrapper Classes
- Android 摇一摇监听实现
- android studio 环境配置及使用时遇到的问题
- Android架构篇--MVP模式的介绍篇
- Android 动态渐变按钮
- android修改getprop读取到的ro.build.fingerprint属性