android adb 概述

android adb的代码分为两部分:

kernel层的代码在如下路径:

drivers/usb/gadget/f_adb.c

drivers/usb/gadget/android.c

他吐给上层应用的是如下的设备节点:/dev/android_adb

应用层的代码在如下路径:

system/core/adb目录

针对device,该目录编译的输出是adbd

 
 
控制台上手动启动平板adb的功能的方法如下:
 
step1:在init.rc中申明adbd服务
  1. <span style="font-size:18px;">service adbd /sbin/adbd
  2. class core
  3. #    socket adbd stream 660 system system
  4. disabled
  5. #    seclabel u:r:adbd:s0</span>

step2:init.rc中通过属性变量来触发usb adb功能

  1. on property:sys.usb.config=adb
  2. write /sys/class/android_usb/android0/enable 0
  3. write /sys/class/android_usb/android0/idVendor 18d1
  4. write /sys/class/android_usb/android0/idProduct D002
  5. write /sys/class/android_usb/android0/functions ${sys.usb.config}
  6. write /sys/class/android_usb/android0/enable 1
  7. start adbd
  8. setprop sys.usb.state ${sys.usb.config}
 
step3: init.rc中disable adb
  1. <span style="font-size:18px;">on property:sys.usb.config=none
  2. stop adbd
  3. write /sys/class/android_usb/android0/enable 0
  4. write /sys/class/android_usb/android0/bDeviceClass 0
  5. setprop sys.usb.state ${sys.usb.config}</span>
step4:在控制台上执行命令setprop sys.usb.config adb 将使能adb功能
         在控制台上执行命令setprop sys.usb.confignone 将关闭adb功能    
 
以上默认是通过usb来实现adb的功能,其实也可以通过网络来实现adb的功能;方法如下:
device端:

step1:  setprop service.adb.tcp.port 5555

step2:  setprop sys.usb.confignone

setprop sys.usb.config adb

step3: 通过执行netstat -tna命令确认5555端口被监听。#以上操作如下图
 
pc端:

step1:  adb kill-server

step2: adb connect 10.1.32.4  #这里的地址就是平板的地址

step3: adb devices

step3: adb shell  #以上操作如下图

adb 出问题时的调试方法:

方法一:通过bus hound工具来抓pc跟device之间的usb 包的通讯。前提是需要对usb adb的命令包格式很熟悉。

方法二: 由属性变量:persist.adb.trace_mask来控制adb的log输出级别。其输出的log信息将被重定向到/data/adb/目录下的文件中。

具体源代码,请查看:/system/core/adb/adb.c(start_device_log函数), system/core/adb/adb.h(adb_trace_mask,AdbTrace)

方法三:由于方法二有一定的局限性,他是将printf的输出重定向到/data/adb/xxxx.txt文件中,这样很多的后台服务的打印信息是打印不出来的。

因为他的输入,输出被重定向到pty/pts或是pipe或是socket pair上了。所以我们的方法就是修改/system/core/adb/adb.h文件中的#  define  D(...) 宏,使其的打印输出到logcat中。方法如下:
  1. /* you must define TRACE_TAG before using this macro */
  2. define  D(...)                                      \
  3. do {                                           \
  4. if (ADB_TRACING) {                         \
  5. int save_errno = errno;                \
  6. adb_mutex_lock(&D_lock);               \
  7. __android_log_print(3,"adb", "%s::%s():",           \
  8. __FILE__, __FUNCTION__);       \
  9. errno = save_errno;                    \
  10. __android_log_print(3,"adb", __VA_ARGS__ );         \
  11. fflush(stderr);                        \
  12. adb_mutex_unlock(&D_lock);             \
  13. errno = save_errno;                    \
  14. }                                           \
  15. } while (0)

上面的__android_log_print函数需要依赖头文件#include <android/log.h>和动态库:liblog ,动态库的链接方法:

LOCAL_STATIC_LIBRARIES +=  liblog    #交叉编译时使用

LOCAL_LDLIBS += -llog  #编译主机端的adb时使用

为了查看更多地log,可以将persist.adb.trace_mask设置为0x3ff。
一下将从几个专题来研究devices端的adbd后台的工作内容:
专题一:#######adb 从pc端发送命令到device端的流程:#############
output_thread(system/core/adb/transport.c)

get_apacket(分配一个apacket包所需的内存)

t->read_from_remote(p, t)

write_packet(t->fd, t->serial, &p) //注意该函数,真正只是发送包的开始地址,并不是发送整个包的内容数据

|

|t->fd对应的pipe另一端为: t->transport_socket ,而该句柄的接收处理函数为: transport_socket_events

|
\|/

transport_socket_events

read_packet(fd, t->serial, &p)) //fd即为t->transport_socket

handle_packet(p, (atransport *) _t); //该函数根据从pc端发送过来的命令,做相应的处理的。

注意:
read_from_remote/write_to_remote  对应从/dev/android_adb读取和写入数据
 
 
专题二:###############adb 从device端发送响应到pc端的流程:#########
send_packet(apacket *p, atransport *t)//函数用来向pc端发送数据(通过usb或以太网)

write_packet(t->transport_socket, t->serial, &p)

|

 |t->fd对应的pipe另一端为: t->transport_socket ,该socket对是在 transport_registration_func 函数中申明  |的。
\|/

input_thread

read_packet(t->fd, t->serial, &p)

t->write_to_remote(p, t);

|

|

|对应于usb而言

\|/

remote_write //发送命令和数据到pc端

usb_write(t->usb, &p->msg, sizeof(amessage)) // 发送命令

usb_write(t->usb, &p->data, size) //发送数据
 
专题三:pc跟device之间传输adb数据包的格式
他们之间的数据传输单元称为:struct apacket,具体结构如下:

  1. <span style="font-size:18px;">      struct apacket
  2. {
  3. apacket *next;   //adb package队列,可以对还未来得及处理的adb数据包进行入队列
  4. unsigned len;
  5. unsigned char *ptr;
  6. amessage msg;     //adb数据包对应的命令头,对应一个IN或OUT传输
  7. unsigned char data[MAX_PAYLOAD]; //命令包对应的数据部分,对应一个IN或OUT传输
  8. };</span>

一个 apacket 包传输分为两个阶段:

第一个阶段是:命令传输阶段(required),存储在 apacket.msg 中

第二个阶段是:数据传输阶段(optional),存储在 apacket.data 中

adb支持的命令如下:

  1. <span style="font-size:18px;">          #define A_SYNC 0x434e5953   //该命令用于adb push/pull命令
  2. #define A_CNXN 0x4e584e43   //该命令用于pc和device进行adb连接时使用,该命令带有数据阶段
  3. #define A_OPEN 0x4e45504f   //用于在devices端开启一个服务,譬如adb shell,adb logcat,adb remount等shell命令
  4. #define A_OKAY 0x59414b4f   //表示接收ok,该命令没有数据阶段
  5. #define A_CLSE 0x45534c43   //关闭对应的连接,关闭和打开都是双向的。
  6. #define A_WRTE 0x45545257   //该命令用来向pc端或是device端发送数据,该命令一般都有数据阶段
  7. #define A_AUTH 0x48545541</span>
主题四:device端对收到的pc端来的命令或数据的处理函数:handle_packet
  1. <span style="font-size:18px;">void handle_packet(apacket *p, atransport *t)//system/core/adb/adb.c
  2. {
  3. asocket *s;
  4. D("handle_packet() %c%c%c%c\n", ((char*) (&(p->msg.command)))[0],
  5. ((char*) (&(p->msg.command)))[1],
  6. ((char*) (&(p->msg.command)))[2],
  7. ((char*) (&(p->msg.command)))[3]);
  8. print_packet("recv", p);
  9. switch(p->msg.command){
  10. case A_SYNC:           //在adb push和pull命令时使用。
  11. if(p->msg.arg0){
  12. send_packet(p, t);
  13. if(HOST) send_connect(t);
  14. } else {
  15. t->connection_state = CS_OFFLINE;
  16. handle_offline(t);
  17. send_packet(p, t);
  18. }
  19. return;
  20. case A_CNXN: /* CONNECT(version, maxdata, "system-id-string") */ //在adb连接过程时使用。
  21. /* XXX verify version, etc */       //首先由pc发送一个A_CNXN命令和对应的数据,然后device收到后,会回复一个
  22. //A_CNXN命令并带上数据阶段。
  23. if(t->connection_state != CS_OFFLINE) {
  24. t->connection_state = CS_OFFLINE;
  25. handle_offline(t);
  26. }
  27. parse_banner((char*) p->data, t);
  28. if (HOST || !auth_enabled) {
  29. handle_online(t);
  30. if(!HOST) send_connect(t);
  31. } else {
  32. send_auth_request(t);
  33. }
  34. break;
  35. case A_AUTH:
  36. if (p->msg.arg0 == ADB_AUTH_TOKEN) {
  37. t->connection_state = CS_UNAUTHORIZED;
  38. t->key = adb_auth_nextkey(t->key);
  39. if (t->key) {
  40. send_auth_response(p->data, p->msg.data_length, t);
  41. } else {
  42. /* No more private keys to try, send the public key */
  43. send_auth_publickey(t);
  44. }
  45. } else if (p->msg.arg0 == ADB_AUTH_SIGNATURE) {
  46. if (adb_auth_verify(t->token, p->data, p->msg.data_length)) {
  47. adb_auth_verified(t);
  48. t->failed_auth_attempts = 0;
  49. } else {
  50. if (t->failed_auth_attempts++ > 10)
  51. adb_sleep_ms(1000);
  52. send_auth_request(t);
  53. }
  54. } else if (p->msg.arg0 == ADB_AUTH_RSAPUBLICKEY) {
  55. adb_auth_confirm_key(p->data, p->msg.data_length, t);
  56. }
  57. break;
  58. case A_OPEN: /* OPEN(local-id, 0, "destination") */  执行一个adb命令时,首先是发送的这个命令
  59. if (t->online) {
  60. char *name = (char*) p->data;  //命令对应的数据,该数据字段一般包含所要执行的shell命令
  61. name[p->msg.data_length > 0 ? p->msg.data_length - 1 : 0] = 0;
  62. D("open usb\n");
  63. s = create_local_service_socket(name); // 根据命令的名字执行相应的后台服务线程,通过socket对或是pty/pts来实现本地socket跟
  64. if(s == 0) {                         //后台服务线程之间的通讯,本地socket用于跟对应命令的后台服务线程通讯
  65. send_close(0, p->msg.arg0, t);
  66. } else {
  67. s->peer = create_remote_socket(p->msg.arg0, t); //对端socket即用于实现跟pc端的交互,主要是向pc发送数据
  68. s->peer->peer = s;
  69. send_ready(s->id, s->peer->id, t);
  70. s->ready(s);
  71. }
  72. }
  73. break;
  74. case A_OKAY: /* READY(local-id, remote-id, "") */
  75. if (t->online) {
  76. if((s = find_local_socket(p->msg.arg1))) {
  77. if(s->peer == 0) {
  78. s->peer = create_remote_socket(p->msg.arg0, t);
  79. s->peer->peer = s;
  80. }
  81. s->ready(s);
  82. }
  83. }
  84. break;
  85. case A_CLSE: /* CLOSE(local-id, remote-id, "") */
  86. if (t->online) {
  87. if((s = find_local_socket(p->msg.arg1))) {
  88. s->close(s);  //关闭时双向的,现实pc端发送关闭命令,然后是device端回复关闭命令,close 对应 local_socket_close
  89. }
  90. }
  91. break;
  92. case A_WRTE:    // 对应pc端给device发送数据,分为两个阶段:一个阶段是命令:WRTE(包含在 apacket.msg中);另一个阶段是数据(包含在 apacket.data中)
  93. if (t->online) {
  94. if((s = find_local_socket(p->msg.arg1))) {
  95. unsigned rid = p->msg.arg0;
  96. p->len = p->msg.data_length;
  97. if(s->enqueue(s, p) == 0) {  //enqueue 对应的函数是 local_socket_enqueue
  98. D("Enqueue the socket\n");
  99. send_ready(s->id, rid, t);
  100. }
  101. return;
  102. }
  103. }
  104. break;
  105. default:
  106. printf("handle_packet: what is %08x?!\n", p->msg.command);
  107. }
  108. put_apacket(p);
  109. }</span>

上面函数中的remote_socket_enqueue函数,用于向pc端发送命令和数据

  1. <span style="font-size:18px;">static int remote_socket_enqueue(asocket *s, apacket *p)
  2. {
  3. D("entered remote_socket_enqueue RS(%d) WRITE fd=%d peer.fd=%d\n",
  4. s->id, s->fd, s->peer->fd);
  5. p->msg.command = A_WRTE;      //apackge包对应的命令
  6. p->msg.arg0 = s->peer->id;
  7. p->msg.arg1 = s->id;
  8. p->msg.data_length = p->len;
  9. send_packet(p, s->transport);
  10. return 1;
  11. }</span>

上面函数create_local_service_socket的实现如下:

  1. <span style="font-size:18px;">asocket *create_local_service_socket(const char *name)
  2. {
  3. asocket *s;
  4. int fd;
  5. #if !ADB_HOST
  6. if (!strcmp(name,"jdwp")) {
  7. return create_jdwp_service_socket();
  8. }
  9. if (!strcmp(name,"track-jdwp")) {
  10. return create_jdwp_tracker_service_socket();
  11. }
  12. #endif
  13. D("begin service_to_fd:%s\n",name);
  14. fd = service_to_fd(name);//该函数的功能就是根据name来创建相应的后台服务,并创建一个socket pair或是pty/pts管道对,返回的fd对应该管道的一端;
  15. if(fd < 0) return 0;     //该管道的另一端,将dup重定向到该后台服务的输入输出句柄。
  16. s = create_local_socket(fd);//安装本地句柄,即上面返回的fd句柄的输入和输出处理函数:local_socket_event_func
  17. D("LS(%d): bound to '%s' via %d\n", s->id, name, fd);
  18. #if !ADB_HOST
  19. if ((!strncmp(name, "root:", 5) && getuid() != 0)
  20. || !strncmp(name, "usb:", 4)
  21. || !strncmp(name, "tcpip:", 6)) {
  22. D("LS(%d): enabling exit_on_close\n", s->id);
  23. s->exit_on_close = 1;
  24. }
  25. #endif
  26. return s;
  27. }</span>

local_socket_event_func函数展开如下:该函数是handle_packet与对应的后台服务之间的一个中转函数,他的任务包括:

任务一:将pc端发送过来的命令和数据报(统称为adb package)通过local_socket_enqueue函数放置在struct

apacket的next队列上,并触发如下函数的FDE_WRITE分支的处理,该分支将从apacket队列上取package,并将它写到管道另一端的后台服务上,作为后台服务的输入。

任务二:在要读出后台服务的输出结果时,通过调用函数local_socket_ready来触发如下函数的FDE_READ

分支,该分支将通过管道,读取后台服务的输出数据,并调用 s->peer->enqueue(即remote_socket_enqueue函数)将后台服务的输出发送到远端,即pc端上。

  1. <span style="font-size:18px;">static void local_socket_event_func(int fd, unsigned ev, void *_s)
  2. {
  3. asocket *s = _s;
  4. D("LS(%d): event_func(fd=%d(==%d), ev=%04x)\n", s->id, s->fd, fd, ev);
  5. /* put the FDE_WRITE processing before the FDE_READ
  6. ** in order to simplify the code.
  7. */
  8. if(ev & FDE_WRITE){
  9. apacket *p;
  10. while((p = s->pkt_first) != 0) {
  11. while(p->len > 0) {
  12. int r = adb_write(fd, p->ptr, p->len);//将pc段发送来的命令和数据写到后台服务
  13. if(r > 0) {
  14. p->ptr += r;
  15. p->len -= r;
  16. continue;
  17. }
  18. if(r < 0) {
  19. /* returning here is ok because FDE_READ will
  20. ** be processed in the next iteration loop
  21. */
  22. if(errno == EAGAIN) return;
  23. if(errno == EINTR) continue;
  24. }
  25. D(" closing after write because r=%d and errno is %d\n", r, errno);
  26. s->close(s);
  27. return;
  28. }
  29. if(p->len == 0) {
  30. s->pkt_first = p->next;//取队列中的下一个apackage包
  31. if(s->pkt_first == 0) s->pkt_last = 0;
  32. put_apacket(p);
  33. }
  34. }
  35. /* if we sent the last packet of a closing socket,
  36. ** we can now destroy it.
  37. */
  38. if (s->closing) {
  39. D(" closing because 'closing' is set after write\n");
  40. s->close(s);
  41. return;
  42. }
  43. /* no more packets queued, so we can ignore
  44. ** writable events again and tell our peer
  45. ** to resume writing
  46. */
  47. fdevent_del(&s->fde, FDE_WRITE);
  48. s->peer->ready(s->peer);
  49. }
  50. if(ev & FDE_READ){
  51. apacket *p = get_apacket();
  52. unsigned char *x = p->data;
  53. size_t avail = MAX_PAYLOAD;
  54. int r;
  55. int is_eof = 0;
  56. while(avail > 0) {
  57. r = adb_read(fd, x, avail);//读取后台服务的输出
  58. D("LS(%d): post adb_read(fd=%d,...) r=%d (errno=%d) avail=%d\n", s->id, s->fd, r, r<0?errno:0, avail);
  59. if(r > 0) {
  60. avail -= r;
  61. x += r;
  62. continue;
  63. }
  64. if(r < 0) {
  65. if(errno == EAGAIN) break;
  66. if(errno == EINTR) continue;
  67. }
  68. /* r = 0 or unhandled error */
  69. is_eof = 1;
  70. break;
  71. }
  72. D("LS(%d): fd=%d post avail loop. r=%d is_eof=%d forced_eof=%d\n",
  73. s->id, s->fd, r, is_eof, s->fde.force_eof);
  74. if((avail == MAX_PAYLOAD) || (s->peer == 0)) {
  75. put_apacket(p);
  76. } else {
  77. p->len = MAX_PAYLOAD - avail;
  78. <span style="white-space:pre">      </span>//以下将后台服务的输出发送到远端pc上。
  79. r = s->peer->enqueue(s->peer, p);//remote_socket_enqueue,该分支在handle_packet函数中的被create_remote_socket初始化
  80. D("LS(%d): fd=%d post peer->enqueue(). r=%d\n", s->id, s->fd, r);
  81. if(r < 0) {
  82. /* error return means they closed us as a side-effect
  83. ** and we must return immediately.
  84. **
  85. ** note that if we still have buffered packets, the
  86. ** socket will be placed on the closing socket list.
  87. ** this handler function will be called again
  88. ** to process FDE_WRITE events.
  89. */
  90. return;
  91. }
  92. if(r > 0) {
  93. /* if the remote cannot accept further events,
  94. ** we disable notification of READs.  They'll
  95. ** be enabled again when we get a call to ready()
  96. */
  97. fdevent_del(&s->fde, FDE_READ);
  98. }
  99. }
  100. /* Don't allow a forced eof if data is still there */
  101. if((s->fde.force_eof && !r) || is_eof) {
  102. D(" closing because is_eof=%d r=%d s->fde.force_eof=%d\n", is_eof, r, s->fde.force_eof);
  103. s->close(s);
  104. }
  105. }
  106. if(ev & FDE_ERROR){
  107. /* this should be caught be the next read or write
  108. ** catching it here means we may skip the last few
  109. ** bytes of readable data.
  110. */
  111. //        s->close(s);
  112. D("LS(%d): FDE_ERROR (fd=%d)\n", s->id, s->fd);
  113. return;
  114. }
  115. }</span>

注意:

system/core/adb/services.c对应各种命令的后台服务
system/core/adb/commandline.c包含各种后台服务的具体实现。
 
 
主题五:usb adb的bus hound分析
情景1:adb的链接过程
2.1.0  
3.1.0
4.1.0 
5.1.0
以上是通用的枚举过程,即取得描述符的内容。
6.1.0  set_config
7.1.0  取得描述符
至此完成设备的枚举,开始adb的连接过程。
8.1.0 发送adb命令:A_CNXN  为out传输
9.1.0 发送adb命令A_CNXN所带的数据 为out传输
10.1.0 由device端发送adb命令A_CNXN到host pc段。IN传输
11.1.0 为adb命令A_CNXN所带的数据,IN传输
12.1.0为adb命令A_OPEN
13.1.0为adb命令A_OPEN所带的数据
14.1.0为以上命令的响应:A_OKAY
15.1.0为adb命令A_WRTE
16.1.0为adb命令A_WRTE所带的数据
17.1.0为以上命令的响应
21.1.0位adb命令A_CLSE,没有数据阶段 IN传输
22.1.0为adb命令A_CLSE,没有数据阶段,关闭时双向的。OUT传输
以上即为adb连接过程的usb封包的分析
 
情景2:adb logcat过程

1.1.0对应adb命令A_OPEN
2.1.0对应adb命令A_OPEN所带的数据,这个数据中有如下命令:exec logcat
3.1.0为以上命令的响应,IN传输

4.1.0对应adb命令A_WRTE
5.1.0对应adb命令A_WRTE的数据 IN传输,打开设备节点/dev/log/main
6.1.0为以上命令的响应 OUT传输

7.1.0对应adb命令A_WRTE
8.1.0对应adb命令A_WRTE的数据,即为logcat的输出内容 IN传输
10.1.0为以上命令的响应,OUT传输

再往下,即为上面命令的重复,以不断输出logcat的log信息

 
情景3:adb pull过程
 
1.1.0对应adb命令A_OPEN
2.1.0对应adb命令A_OPEN所带的数据,这个数据中有如下命令:sync,
     该sync命令在service_to_fd函数中,被file_sync_service函数所处理
3.1.0为以上命令的响应,IN传输

4.1.0对应adb命令A_WRTE
5.1.0对应adb命令A_WRTE的数据 IN传输,数据内容为:STAT,pc用该命令来查询该文件是否存在,已经是有权限
6.1.0为以上命令的响应 OUT传输

13.1.0 对应adb命令A_WRTE  OUT传输
14.1.0 对应adb命令A_WRTE的数据,该数据包含一个命令:RECV,表示pc从device段接收文件
15.1.0 为以上命令的响应

16.1.0 对应adb命令A_WRTE OUT传输
17.1.0 对应adb命令A_WRTE的数据,表示要pull的是哪个文件
18.1.0 为以上命令的响应

19.1.0 对应adb命令A_WRTE IN传输
20.1.0 对应adb命令A_WRTE的数据:DATA,表示开始数据传输,并且指定下一个命令需要传输的数据大小
21.1.0 为以上命令的响应

22.1.0 对应adb命令A_WRTE IN传输
23.1.0 对应adb命令A_WRTE的数据: 包含文件的具体内容,长度就是上面命令指定的长度
24.1.0 为以上命令的响应

重复19到24这个过程,直到文件被传输完成。

 
情景4:adb push过程
 
2.1.0对应adb命令A_OPEN所带的数据,这个数据中有如下命令:shell:
3.1.0为以上命令的响应,IN传输

4.1.0对应adb命令A_WRTE IN传输
5.1.0对应adb命令A_WRTE的数据 ,数据内容为:#,即为显示在pc上的cmd窗口上的#号。
6.1.0为以上命令的响应 OUT传输

android usb adb流程[转]的更多相关文章

  1. Android USB ADB ATUH 验证包验证流程

    #define ADB_AUTH_TOKEN 1 #define ADB_AUTH_SIGNATURE 2 #define ADB_AUTH_RSAPUBLICKEY 3   1. 连接USB,手机发 ...

  2. Android 4.4 Kitkat 使能 USB adb 功能

    背景 在 Linux-3.8 以后,Android 的内核分支,便去掉了 f_adb,改使用 USB function FS,在用户空间实现 USB adb 功能.这篇文章依据原作者的 Google+ ...

  3. android apk 的root 权限和USB adb 权限的差别

    USB adb 权限是指,当adb 连接手机时,手机中的守护进程adbd 的权限为root 权限,从而它的子进程也具有root 权限.通常假设adb shell 看到是: Android 4.0 以后 ...

  4. Android USB驱动源码分析(-)

    Android USB驱动中,上层应用协议里最重要的一个文件是android/kernel/drivers/usb/gadget/android.c.这个文件实现USB的上层应用协议. 首先包含了一些 ...

  5. Android 工具-adb

    Android 工具-adb 版权声明:本文为博主原创文章,未经博主允许不得转载. Android 开发中, adb 是开发者经常使用的工具,是 Android 开发者必须掌握的. Android D ...

  6. Android 神兵利器—— Adb 常用命令

    总结的Android工具类文章: Android 神兵利器-- Adb 常用命令 Android 神兵利器-- Git 常用命令 Adb的全称为Android Debug Bridge,是管理andr ...

  7. I.MX6 默认打开 USB adb

    /***************************************************************************** * I.MX6 默认打开 USB adb ...

  8. 【Android】ADB常用指令与logcat日志(转)

    ADB命令简介 ADB是一个功能强大的命令行工具.通过它可以直接和模拟器或真机进行交互.它是一个具有客户端和服务器端的程序. 它主要由三个部分组成: 客户端,它运行在你的开发机上,你可以通过执行adb ...

  9. Android USB Host与HID通讯 (一)

    去年9月份来到现在的公司,接到新公司的第一个项目就是Android USB Host与HID通讯,当时也什么都不懂,就拿着google的api 开发指南 (http://developer.andro ...

随机推荐

  1. 数据库sql命令

    本文为转载,原文地址:http://www.cnblogs.com/cangqiongbingchen/p/4530333.html 1.说明:创建数据库CREATE DATABASE databas ...

  2. HDU 4436 str2int(后缀自动机)(2012 Asia Tianjin Regional Contest)

    Problem Description In this problem, you are given several strings that contain only digits from '0' ...

  3. 用Electron开发桌面应用app的相关文献集锦

    1. 超棒的发声器(项目实战) 原文点此链接 2. Electron中文文档 原文点此链接

  4. mysql 5.7 Access denied for user 'root'@'localhost' solution

    sudo vim /etc/mysql/debian.cnf # Automatically generated for Debian scripts. DO NOT TOUCH! [client] ...

  5. To Chromium之VS调试追踪

    启动的code: for(;;){...WaitForWork()}base.dll!base::MessagePumpForUI::DoRunLoop                         ...

  6. PHP与webserver【简书看到的】

    很久以前,人们造出来一个机器人,它的英文名字叫web server,中文名叫网页服务器.(为了简写,下文称web server为server) server的工作很简单,就是做内容的分发. 初期的se ...

  7. Android Service的分类详解

    按照启动方式分类 谷歌官网对Service的分类 Service根据启动方式分为两类:Started和Bound.其中,Started()是通过startService()来启动,主要用于程序内部使用 ...

  8. 魔法物品(magic.pas/c/cpp)

    有两种类型的物品:普通物品和魔法物品.普通物品没有魔法属性,而魔法物品拥有一些魔法属性.每种普通物品有一个价值P,但每种魔法物品有两种价值:鉴定前的价值P.和鉴定后的价值P2(当然,P2总是大于P.) ...

  9. [NOIP2018 TG D2T2]填数游戏

    题目大意:$NOIP2018\;TG\;D2T2$ 题解:在skip2004的博客基础上修改的,也是暴搜. 说明一下把vector改成数组并不可以通过此题,记录. 结论:在$m>n+1$时答案为 ...

  10. [CF1019A]Elections

    题目大意:有$n$个人,$m$个政党,每个人都想投一个政党,但可以用一定的钱让他选你想让他选的政党. 现在要$1$号政党获胜,获胜的条件是:票数严格大于其他所有政党.求最小代价 题解:暴力枚举其他政党 ...