Browsed by
作者:koffuxu

Ubuntu环境使用Gradle编译打包APK

Ubuntu环境使用Gradle编译打包APK

参考:

1,Prepare

  • gradle:
不需要单独下载,在某个项目中执行./gradlew会下载项目需要的gradle版本,最终会下载到.gradle/dist/下面,根据ZT Android APP环境规范,下载3.3-all即可。
  • JDK:单独安装Java7
apt-get install openjdk-7-jdk
  • 安装JAVA 8
下载
scp -P 10622 jdk-8u131-linux-x64.tar.gz xugangfeng@aly.zerotech.top:~/misc/
  • android-sdk:
新的sdk都是打包在android studio工具里面,但我们又不需要。我们可以先通过下载一个老的版本sdk,然后通过这个sdk里面的工具android命令下载新的sdk版本和build tools。
 tar xf android-sdk_r24.4.1-linux.tgz
完成之后,就可以使用android命令了
列出远程可以下载的sdk和tools版本
android list sdk –all
  1. 9- Android SDK Build-tools, revision 24.0.2
  2. 10- Android SDK Build-tools, revision 24.0.1
  3. ...
  4. 37- SDK Platform Android 7.0, API 24, revision 2
  5. 38- SDK Platform Android 6.0, API 23, revision 3
  6. 39- SDK Platform Android 5.1.1, API 22, revision 2
  7. 40- SDK Platform Android 5.0.1, API 21, revision 2
下载对应平台的,我下载build-tools 24.0.2和 sdk版本为22.
android update sdk -a -u -t 1,2,6,7,36,42,37
  • 更新环境变量
  1. #android sdk
  2. ANDROID_HOME=/home/andbase/workspace/misc/android-sdk-linux
  3. export ANDROID_HOME
  4. GRADLE_HOME=/home/andbase/.gradle/wrapper/dists/gradle-3.3-all/55gk2rcmfc6p2dg9u9ohc3hw9/gradle-3.3/
  5. export PATH=$PATH:$ANDROID_HOME/tools:$ANDROID_HOME/tools/bin:$GRADLE_HOME/bin

2,Ready Go

  • 进入项目目录,Gradle版本确认,确认gradle版本为3.3
  1. andbase@vm-10-142-132-78:ZerotechDemo$ ./gradlew -v
  2. ------------------------------------------------------------
  3. Gradle 3.3
  4. ------------------------------------------------------------
  5. Build time: 2017-01-03 15:31:04 UTC
  6. Revision: 075893a3d0798c0c1f322899b41ceca82e4e134b
  7. Groovy: 2.4.7
  8. Ant: Apache Ant(TM) version 1.9.6 compiled on June 29 2015
  9. JVM: 1.7.0_85 (Oracle Corporation 24.85-b03)
  10. OS: Linux 3.16.0-30-generic amd64
  • 配置好java
根据sdk版本,选择java, update-alternatives –config java/javac
  • 设置Liecense
如果不设置,否则会有这样的错误
  1. * What went wrong:
  2. A problem occurred configuring project ':app'.
  3. > You have not accepted the license agreements of the following SDK components:
  4. [Android SDK Platform-Tools].
  5. Before building your project, you need to accept the license agreements and complete the installation of the missing components using the Android Studio SDK Manager.
  6. Alternatively, to learn how to transfer the license agreements from one workstation to another, go to http://d.android.com/r/studio-ui/export-licenses.html
android list sdk –all   列出当前的platform-tools
2- Android SDK Platform-tools, revision 26
android update sdk -a -u -t 1,2
November 20, 2015
Do you accept the license ‘android-sdk-license-c81a61d9′ [y/n]: y
另外,外部依赖包constraint-layout需要使用sdkmanager命令来添加License确认
sdkmanager “extras;m2repository;com;android;support;constraint;constraint-layout-solver;1.0.2″
  • lint检查错误

在编译脚本文件app/build.gradle中的android切点添加参数

    lintOptions {
        abortOnError false
    }
同时也可以使用assembleDebug的task,忽略Lint的检查
  • 开始编译
不进行Lint检查:
gradle –daemon  assembleDebug

3,TroubleShooting

TBD
由Adb Emulator问题谈起

由Adb Emulator问题谈起

0,问题描述

进入RK3288平台,执行adb devices会有一个emulator-5554的模拟器的连接。

1,复现步骤

reboot rk3288
C:\Users\koffuxu>adb tcpip 5555
C:\Users\koffuxu>adb shell
shell@firefly:/ # adb devices
* daemon not running. starting it now on port 5038 *
* daemon started successfully *
List of devices attached
emulator-5554   device
or:
setprop service.adb.tcp.port 5555; stop adbd; start adbd;

2,port status

127.0.0.1的41114端口(adb),连接上了5555端口(adbd),认为有一个模拟器的存在。 
从而可以获得产生该问题的原因:
先启动adbd进程,监听127.0.0.1的5555端口;
再启动adb中的server,会去获得usb或者tcp链接上的adb状态,当有5555端口有响应的时候,认为有android device设备连接了,当对方的IP地址为127.0.0.1的话,就认为是emulator设备,即显示出来这个模拟器了。

3,打开调试

  1. --- a/system/core/adb/adb_trace.h
  2. +++ b/system/core/adb/adb_trace.h
  3. @@ -64,7 +64,7 @@ extern int adb_trace_mask;
  4. extern unsigned char adb_trace_output_count;
  5. void adb_trace_init(void);
  6. -# define ADB_TRACING ((adb_trace_mask & (1 << TRACE_TAG)) != 0)
  7. +# define ADB_TRACING 1// ((adb_trace_mask & (1 << TRACE_TAG)) != 0)

4,修正方案

第一次尝试:注册127.0.0.1:*<->127.0.0.1:5555的连接到transport_list全局链表时的时候,把类型由local转为非local型。
结果:几番adb kill-server /start-server之后还是会有emulator出现
 第二次尝试:不把与5555的连接注册到全局链表中。
结果:emulaotr没有添加到全局链表中,但连接是存在的,netstat 还是可以看与127.0.0.1有连接,仍有风险。
第三次尝试:不与5555进行连接
 结果:没有本地的5555的连接,emulator也没有产生,其它连接也不受影响。
其它功能测试如下:
  • PC通过usb或者网线连接RK3288的adbd

    

  • RK3288的通过usb或者网线连接手机I9300的adbd
    
     socket的连接情况
    
 .105  =RK3288的IP
.55     =I9300手机的IP
.239    =PC的IP
 

5,复盘

Overview
 

 5.1,2种Service:

Host Service:不需要与device进行通讯,直接在本地查询服务,比如版本号查询服务
Local Service:需要与Device进行通信,比如adb shell服务
ADB SERVER的各种类型参考SERVICES.txt,包含Host Service和Local Service

5.2,2个监听端口:

ADB Server监听TCP:localhost:5038,是用于等待Client的命令传输,指定127.0.0.1的连接
ADBD 监听TCP:0.0.0.0:5555,是用于监听TCP/IP的adb连接请求服务,任何IP皆可以连接 ,包括127.0.0.1

5.3,2种传输媒介:

USB:只限与物理设备的传输连接
TCP/IP:包括物理设备的连接,也包括本地adb与adbd的连接(这就是产生emualtor-5554的原因)

6,adb devices命令执行流程

ADB_HOST=1表示编译进adb可执行文件,否则为adbd可执行文件
adb devices
由services.txt可知,adb devices描述如下:
  1. host:devices host:devices-l Ask to return the list of available Android devices and their
  2. state. devices-l includes the device paths in the state.
  3. After the OKAY, this is followed by a 4-byte hex len,
  4. and a string that will be dumped as-is by the client, then
  5. the connection is closed
执行adb命令,首先进入main()
main()@adb.c
=>//adb_commandline()@commandline.c
system/core/adb/adb.c::main():Handling commandline() 
=>进行一些参数判断之后,进入qury_adb()函数 
  1. if(!strcmp(argv[0], "devices")) {
  2. ...
  3. tmp = adb_query(buf);
  4. if(tmp) {
  5. printf("List of devices attached \n");
  6. printf("%s\n", tmp);
  7. return 0;
=>//adb_query()@adb_client.c打印如下
system/core/adb/adb_client.c::adb_query():adb_query: host:devices 
=>
第一步,执行host:version命令
system/core/adb/adb_client.c::_adb_connect():_adb_connect: host:version //_adb_connect()@adb_client.c
adb_connect之前,先进行adb version查询,通过Socket的方式查询SOCK_STREAM。初次会返回为负值,需要自己启动。如果已经启动就会接下来读取版本。
=>
此段打印,检查adb_connecet是否已经启动。
//调用writex(fd, tmp, 4) || writex(fd, service, len)
system/core/adb/transport.c::writex():writex: fd=3 len=4: 30303063 000c
system/core/adb/transport.c::writex():writex: fd=3 len=12: 686f73743a76657273696f6e host:version
//接收server的数据:adb_status(fd)
system/core/adb/transport.c::readx():readx: fd=3 wanted=4
system/core/adb/transport.c::readx():readx: fd=3 wanted=4 got=4
4f4b4159 OKAY
//连接成功,正常则返回!
system/core/adb/adb_client.c::_adb_connect():_adb_connect: return fd 3
//D(“adb_connect: service %s\n”, service);打印
system/core/adb/adb_client.c::adb_connect():adb_connect: service host:devices 
第二步,读取版本号进行比较
//因为fd>0走else流程,开始进行版本比较
//if(readx(fd, buf, 4)) goto error; 读取前面4个字节,即本次buf的长度
system/core/adb/transport.c::readx():readx: fd=3 wanted=4
system/core/adb/transport.c::readx():readx: fd=3 wanted=4 got=4
30303034 0004
//readx(fd, buf, n) 读取整个buf的长度,获取Version号
system/core/adb/transport.c::readx():readx: fd=3 wanted=4
system/core/adb/transport.c::readx():readx: fd=3 wanted=4 got=4
30303230 0020
第三步,执行host:devices命令
//版本比对成功之后,开始执行host:devices
system/core/adb/adb_client.c::_adb_connect():_adb_connect: host:devices//打印来自fd = _adb_connect(service);
//给Service写数据,包含长度的命令writex(fd, tmp, 4) || writex(fd, service, len)
//整个帧的格式[][][][]+[……]
system/core/adb/transport.c::writex():writex: fd=3 len=4: 30303063 000c//host:devices刚好为12个字节
system/core/adb/transport.c::writex():writex: fd=3 len=12: 686f73743a64657669636573 host:devices//再写命令
////接收server的数据:adb_status(fd)
system/core/adb/transport.c::readx():readx: fd=3 wanted=4
system/core/adb/transport.c::readx():readx: fd=3 wanted=4 got=4
4f4b4159 OKAY
//命令已经写给Server了,_adb_connect正常返回
system/core/adb/adb_client.c::_adb_connect():_adb_connect: return fd 3
//adb_connect()也正常返回,打印来自    D(“adb_connect: return fd %d\n”, fd);进入adb_query
system/core/adb/adb_client.c::adb_connect():adb_connect: return fd 3
第四步,读取host:devices的返回数据
//返回到adb_query()@adb_client.c
//再读buf,第一次读buf的的前面4个字节,即长度 if(readx(fd, buf, 4))
system/core/adb/transport.c::readx():readx: fd=3 wanted=4
system/core/adb/transport.c::readx():readx: fd=3 wanted=4 got=4
//读取的buf长度为0,即没有device连接,符合打印。
30303030 0000
system/core/adb/transport.c::readx():readx: fd=3 wanted=0
system/core/adb/transport.c::readx():readx: fd=3 wanted=0 got=0
List of devices attached
有模拟器的打印,就很容易分析其中的流程
1|shell@firefly:/ $ adb devices
system/core/adb/adb.c::main():Handling commandline()
system/core/adb/adb_client.c::adb_query():adb_query: host:devices
//写host:version命令
system/core/adb/adb_client.c::_adb_connect():_adb_connect: host:version
system/core/adb/transport.c::writex():writex: fd=3 len=4: 30303063 000c
system/core/adb/transport.c::writex():writex: fd=3 len=12: 686f73743a76657273696f6e host:version
system/core/adb/transport.c::readx():readx: fd=3 wanted=4
system/core/adb/transport.c::readx():readx: fd=3 wanted=4 got=4
4f4b4159 OKAY
system/core/adb/adb_client.c::_adb_connect():_adb_connect: return fd 3
system/core/adb/adb_client.c::adb_connect():adb_connect: service host:devices
//读取版本号
system/core/adb/transport.c::readx():readx: fd=3 wanted=4
system/core/adb/transport.c::readx():readx: fd=3 wanted=4 got=4
30303034 0004
system/core/adb/transport.c::readx():readx: fd=3 wanted=4
system/core/adb/transport.c::readx():readx: fd=3 wanted=4 got=4
30303230 0020
//版本比对成功,写host:device命令
system/core/adb/adb_client.c::_adb_connect():_adb_connect: host:devices
system/core/adb/transport.c::writex():writex: fd=3 len=4: 30303063 000c
system/core/adb/transport.c::writex():writex: fd=3 len=12: 686f73743a64657669636573 host:devices
system/core/adb/transport.c::readx():readx: fd=3 wanted=4
system/core/adb/transport.c::readx():readx: fd=3 wanted=4 got=4
4f4b4159 OKAY
system/core/adb/adb_client.c::_adb_connect():_adb_connect: return fd 3
system/core/adb/adb_client.c::adb_connect():adb_connect: return fd 3
//成功返回之后,读取buf信息
system/core/adb/transport.c::readx():readx: fd=3 wanted=4
system/core/adb/transport.c::readx():readx: fd=3 wanted=4 got=4
30303135 0015
system/core/adb/transport.c::readx():readx: fd=3 wanted=21
system/core/adb/transport.c::readx():readx: fd=3 wanted=21 got=21//读取21个字节即emulator-5554.de[vices]
656d756c61746f722d35353534096465 emulator-5554.de
List of devices attached
//获得连接数据
emulator-5554   device

7,解决思路

设备以及状态是在server中检测和连接,在Service.c中屏蔽对127.0.0.1:5555的连接。
或者,让Server不去连接127.0.0.1:5555端口。【最终就是采用这种方法】
首先要弄懂
  • adbd是如何通过5555端口来检测是否有设备接入的。【fdevent机制:transport_registration_func
  • adb的Server端是如何去连接5555端口的。【socket机制:local_init()@transport_local.c】

       socket_loopback_client(adb_port, SOCK_STREAM),会去连接local:5555端口。

  1. addr.sin_family = AF_INET;
  2. addr.sin_port = htons(port);
  3. addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
  4. s = socket(AF_INET, type, 0);
  • adb的Server与adbd的5555号端口连接,这种连接是谁先发起的?

从以下注释可以看到一些端倪

client_socket_thread()@transport_local.c
  1. /* try to connect to any number of running emulator instances */
  2. /* this is only done when ADB starts up. later, each new emulator */
  3. /* will send a message to ADB to indicate that is is starting up */
  4. for ( ; count > 0; count--, port += 2 ) {
  5. (void) local_connect(port);
  6. }
看来是新的模拟器先发消息给adb的,也就是adbd发起的??!!
7.1,adb start-server在Server端的处理
adb start-server是在adb_connect的时候执行host:version命令时就执行了,并未传递到Server端。
代码段为
_adb_connect()@adb_client.c,
fd = socket_loopback_client(__adb_server_port, SOCK_STREAM);
=>根据Unix Socket通信模型,此处即创建一个lopback地址(即127.0.0.1)端口号为5038的socket client,并进行连接。
  1. addr.sin_family = AF_INET;
  2. addr.sin_port = htons(port);
  3. addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
  4. s = socket(AF_INET, type, 0);
  5. connect(s, (struct sockaddr *) &addr, sizeof(addr))
那么这个Socket的Server端是在哪里创建的?
main()@adb.c->adb_main()
  1. ...
  2. local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);//DEFAULT_ADB_LOCAL_TRANSPORT_PORT=5555
  3. ...
  4. char local_name[30];
  5. build_local_name(local_name, sizeof(local_name), server_port);//server_port=5038
  6. if(install_listener(local_name, "*smartsocket*", NULL, 0)) {
  7. exit(1);
  8. }
local_init是初始化5555端口的socket,在local_init里面,会根据HOST的值,是创建一个监听5555的socket server(for adbd)还是连接5555端口的socket client?
  1. //port = 5555
  2. void local_init(int port)
  3. {
  4. ...
  5. if(HOST) {
  6. func = client_socket_thread;//在adb进程,属于soekct client
  7. } else {
  8. func = server_socket_thread;//在adbd进程,属于socket server
  9. ...
  10. adb_thread_create(&thr, func, (void *) (uintptr_t) port))
  11. ...
  12. }
接着,install_listener(local_name, “*smartsocket*”, NULL, 0)则是创建一个监听5038端口socket server。
7.2,adb devices命令在Server的处理
处理是在handle_host_request()@adb.c
  1. // return a list of all connected devices
  2. if (!strncmp(service, "devices", 7)) {
  3. char buffer[4096];
  4. int use_long = !strcmp(service+7, "-l");
  5. if (use_long || service[7] == 0) {
  6. memset(buffer, 0, sizeof(buffer));
  7. D("Getting device list \n");
  8. list_transports(buffer, sizeof(buffer), use_long);
  9. D("Wrote device list \n");
  10. send_msg_with_okay(reply_fd, buffer, strlen(buffer));
  11. return 0;
  12. }
  13. }
list_transports()方法
  1. for(t = transport_list.next; t != &transport_list; t = t->next) {
  2. len = format_transport(t, p, end - p, long_listing);
  3. if (p + len >= end) {
  4. /* discard last line if buffer is too short */
  5. break;
  6. }
主要是从transport_list这个全局链表中取出来。
这个链接是在transport_registration_func()@transport.c这个函数里添加元素的
  1. static void transport_registration_func(int _fd, unsigned ev, void *data)
  2. {
  3. ...
  4. /* put us on the master device list */
  5. t->next = &transport_list;
  6. t->prev = transport_list.prev;
  7. t->next->prev = t;
  8. t->prev->next = t;
  9. }
这个transport_registration_func()被void init_transport_registration(void)调用,注册为fdevent
  1. void init_transport_registration(void)
  2. {
  3. int s[2];
  4. if(adb_socketpair(s)){
  5. fatal_errno("cannot open transport registration socketpair");
  6. }
  7. transport_registration_send = s[0];
  8. transport_registration_recv = s[1];
  9. fdevent_install(&transport_registration_fde,
  10. transport_registration_recv,
  11. transport_registration_func,
  12. 0);
  13. fdevent_set(&transport_registration_fde, FDE_READ);
  14. }
按照道理,只要读某个socket fd,就会有回到全局链表中。
而init_transport_registration();由adb_main()调用,相当于adbd进程。
简而言之,在adbd进程,如果有其它进程读它的socket节点,将触发fdevent,会把访问它的client记录在全局列表transprot_list链表中,即devices的列表。
7.3,adb connect在Server端的处理

8,adb Server与adbd处理“客户端”的循环

8.1,在adb的Server端的处理,是在handle_host_request()@adb.c里面处理command line发过来的命令的。
由来:
在adb_main()的install_listener(local_name, “*smartsocket*”, NULL, 0)函数中,会创建一个名为*smartsocket*,监听端口为5028的socket server,同时还是注册监听事件
=>
  1. //install_listener("TCP:5038", "*smartsocket*", NULL, 0)
  2. static install_status_t install_listener(const char *local_name,
  3. const char *connect_to,
  4. atransport* transport,
  5. int no_rebind)
  6. {
  7. alistener *l;
  8. //1,填充l结构体
  9. if((l = calloc(1, sizeof(alistener))) == 0) goto nomem;
  10. if((l->local_name = strdup(local_name)) == 0) goto nomem;
  11. if((l->connect_to = strdup(connect_to)) == 0) goto nomem;
  12. //2,local_name_to_fd会调用socet()和bin(),初始化socket server
  13. l->fd = local_name_to_fd(local_name);
  14. //3,开始install listen,用于响应客户端
  15. if(!strcmp(l->connect_to, "*smartsocket*")) {
  16. fdevent_install(&l->fde, l->fd, ss_listener_event_func, l);
  17. }
  18. fdevent_set(&l->fde, FDE_READ);
  19. //4,添加到listener_list全局链表中。
  20. l->next = &listener_list;
  21. l->prev = listener_list.prev;
  22. l->next->prev = l;
  23. l->prev->next = l;
  24. l->transport = transport;
  25. }
以上4步就完成adb server的socket server(*smartsocket*)的创建,其中第3步,安装了listen的处理,处理函数是ss_listener_event_func,继续深挖goon!
  1. static void ss_listener_event_func(int _fd, unsigned ev, void *_l)
  2. {
  3. asocket *s;
  4. ...
  5. s = create_local_socket(fd);
  6. if(s) {
  7. connect_to_smartsocket(s);
  8. return;
  9. }
  10. }
先创建asocket类型的s,然后调用connect_to_smartsocket进行连接
=>
create_smart_socket()
=>
s->enqueue = smart_socket_enqueue;
=>
if(handle_host_request(service, ttype, serial, s->peer->fd, s) == 0) 
就调到handle_host_request函数里面了。DONE!
举例:比如说host:version的处理逻辑
    // returns our value for ADB_SERVER_VERSION
    if (!strcmp(service, “version”)) {
        char version[12];
        snprintf(version, sizeof version, “%04x”, ADB_SERVER_VERSION);
        send_msg_with_okay(reply_fd, version, strlen(version));
        return 0;
    }


8.2在adbd中,是在fdevent_loop()@fdevent.c处理客户端的请求;
  1. for(;;) {
  2. D("--- ---- waiting for events\n");
  3. fdevent_process();
  4. while((fde = fdevent_plist_dequeue())) {
  5. fdevent_call_fdfunc(fde);
  6. }
  7. }
死循环处理的,fdevent_loop()是在adb_main()@adb.c即adbd初始化的时候调用到。
AWS-IOT套件使用介绍

AWS-IOT套件使用介绍

Amazon IOT有一套完整的SDK,涉及到IOT设备端,IOT服务器(当然是Amazon的特长),以及包括手机,网页平台的开发SDK。Amazon真有一套,它也有全家桶,只是Level更高一些吧。来让我们一探全貌。

1,Overview

申请Amazon的开发者账户(非剁手账户),进入console就能看到所提供的服务
input之我见05——EVENTHUB兼谈EPOLL机制

input之我见05——EVENTHUB兼谈EPOLL机制

1,引子

Input事件从Kernel层传到用户层,传输节点通过/dev/input/下面的节点:
  1. p253:/dev/input # ls -al
  2. crw-rw---- 1 root input 13, 64 2015-01-01 00:00 event0
  3. crw-rw---- 1 root input 13, 65 2015-01-01 00:00 event1
  4. crw-rw---- 1 root input 13, 66 2015-01-01 00:00 event2
  5. crw-rw---- 1 root input 13, 63 2015-01-01 00:00 mice
从Kernel层的INPUT子系统层面,各个INPUT设备完成初始化之后,都是通过input_event()这个函数“上报”输入事件。深究这个“上报”,我们发现是input_event()往/dev/input/下面的某个event的节点里,按一定结构写数据包(type,code,value),而用户层是有专门的线程监听这个节点,一旦有数据更新,就立马读取,传传递给其它线程进一步加工处理。
从第一讲中的草图可以看到这些信息。
这里面讲的监听线程就是EventHub,而实现这种读取传递的机制就是EPOLL机制。这两个东西就是今天需要讲的。

2,EventHub的前世今生
2.1,构造
Android7.0 Input事件处理这篇文章可以知道,EventHub是在InputReader构造的时候,传进入去的参数。那么EventHub的构造是在哪里?再继续往上跟踪可以发现,EventHub的构造是在com_android_server_input_InputManagerService.cpp的NativeInputManager方法中
  1. NativeInputManager::NativeInputManager(jobject contextObj,
  2. jobject serviceObj, const sp<Looper>& looper) :
  3. mLooper(looper), mInteractive(true) {
  4. ...
  5. sp<EventHub> eventHub = new EventHub();
  6. mInputManager = new InputManager(eventHub, this, this);
  7. }
2.2,入口
同样在上一篇文章中,EventHub从InputMangaer的构造中传入,又作为参数传给了InputReader
  1. InputManager::InputManager(
  2. const sp<EventHubInterface>& eventHub,
  3. const sp<InputReaderPolicyInterface>& readerPolicy,
  4. const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
  5. mDispatcher = new InputDispatcher(dispatcherPolicy);
  6. mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
  7. initialize();
  8. }
这里的EventHubInterface是EventHub的父类,所以这类型也没问题。InputReader在自己的线程执行函数中,通过这个eventHub来获取各个Input设备的数据。
  1. ...
  2. size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
  3. ...
参数timeoutMillis,设置一个超时时间;
参数mEventBuffer,是一个RawEvent数组;
  1. // The event queue.
  2. static const int EVENT_BUFFER_SIZE = 256;
  3. RawEvent mEventBuffer[EVENT_BUFFER_SIZE];
参数EVENT_BUFFER_SIZE,即buffer大小。
3,EventHub的构造
先看一下相关类图
 再看代码实现
  1. EventHub::EventHub(void) {
  2. ...
  3. //获得epoll fd和inotify fd
  4. mEpollFd = epoll_create(EPOLL_SIZE_HINT);
  5. mINotifyFd = inotify_init();
  6. //通过inotifyFd监听/dev/input这个目录
  7. int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
  8. //实例化epoll_event结构体
  9. struct epoll_event eventItem;
  10. memset(&eventItem, 0, sizeof(eventItem));
  11. eventItem.events = EPOLLIN;
  12. eventItem.data.u32 = EPOLL_ID_INOTIFY;
  13. //设置notify FD
  14. result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
  15. //初始化PipeFD,并设置PipeFd

  16. result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
这里面主要是初始化了一些EPOLL机制,这也就进入我这篇文章的第二个主机,EPOLL机制
4,EPOLL机制
4.1,Linux IO操作模型
说到EPOLL机制,不得不提到Linux的IO操作模型,大概有如下几种模型
  • 同步阻塞IO
  • 同步非阻塞IO
  • 多路复用IO
  • 异步IO
%u5728%u8FD9%u91CC%u8F93%u5165%u56FE%u7247%u6807%u9898
IO模型 定义 过程图示 数据等待阶段 数据复制阶段 优点 缺点
同步阻塞IO  
%u5728%u8FD9%u91CC%u8F93%u5165%u56FE%u7247%u6807%u9898
BLOCK BLOCK 能够及时返回数据,无延迟;  
同步非阻塞IO 同步非阻塞就是
“每隔一会儿瞄一眼进度条” 的轮询(polling)方式

%u5728%u8FD9%u91CC%u8F93%u5165%u56FE%u7247%u6807%u9898
NON-BLOCK BLOCK 能够在等待任务完成的时间里干其他活了 任务完成的响应延迟增大了,因为每过一段时间才去轮询一次read操作
多路复用IO UNIX/Linux
下的 select、poll、epoll

%u5728%u8FD9%u91CC%u8F93%u5165%u56FE%u7247%u6807%u9898
NON-BLOCK BLOCK 监听多个Iofd  
异步IO 用户进程发起aio_read操作之后,立刻就可以开始去做其它的事,当这一切都完成之后,kernel会给用户进程发送一个signal或执行一个基于线程的回调函数来完成这次
IO 处理过程

%u5728%u8FD9%u91CC%u8F93%u5165%u56FE%u7247%u6807%u9898
       
EventHub使用的EPOLL机制是一种多路利用IO模型,习惯把这种模型归类为同步IO操作,其实无所以,来具体看看这种机制

4.2,EPOLL模型
看看相关的数据结构
3种类型
  1. /* Valid opcodes to issue to sys_epoll_ctl() */
  2. #define EPOLL_CTL_ADD 1
  3. #define EPOLL_CTL_DEL 2
  4. #define EPOLL_CTL_MOD 3
epoll_event数据结构
  1. struct epoll_event {
  2. __u32 events;
  3. __u64 data;
  4. } EPOLL_PACKED;
通知的两种类型
  1. // Ids used for epoll notifications not associated with devices.
  2. static const uint32_t EPOLL_ID_INOTIFY = 0x80000001;
  3. static const uint32_t EPOLL_ID_WAKE = 0x80000002;
重要的方法
  1. intepoll_create(int size);
  2. 生成一个Epoll专用的文件描述符,其实是申请一个内核空间,用来存放你想关注的socket fd上是否发生以及发生了什么事件。size就是你在这个Epoll fd上能关注的最大socket fd数,大小自定,只要内存足够。
  3. intepoll_ctl(int epfd, intop, int fd, structepoll_event *event);
  4. 控制某个Epoll文件描述符上的事件:注册、修改、删除。其中参数epfdepoll_create()创建Epoll专用的文件描述符。相对于select模型中的FD_SETFD_CLR宏。
  5. intepoll_wait(int epfd,structepoll_event * events,int maxevents,int timeout);
  6. 等待I/O事件的发生;参数说明:
  7. epfd:由epoll_create() 生成的Epoll专用的文件描述符;
  8. epoll_event:用于回传代处理事件的数组;
  9. maxevents:每次能处理的事件数;
  10. timeout:等待I/O事件发生的超时值;
  11. 返回发生事件数。
回到前面,在EventHub构造函数中,设置Epoll的最大数,获得了epoll_fd,之后,每打开一个Input设置,都会把这个设备的fd注册进epoll里面去
openDevLocked();
  1. // Register with epoll.
  2. struct epoll_event eventItem;
  3. memset(&eventItem, 0, sizeof(eventItem));
  4. eventItem.events = EPOLLIN;

  5. eventItem.data.u32 = deviceId;
  6. if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {
  7. ALOGE("Could not add device fd to epoll instance. errno=%d", errno);
  8. delete device;
  9. return -1;
  10. }
epoll_event只表明某个设备上有事件,并不包含事件内容,具体事件内容需要通过read来读取。
getEvents();
  1. //在调用epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);之后,读到的总event_poll保存在mPendingEventCount,mPendingEventIndex初始化为0,所以这里面的逻辑是一个个取出来
  2. while (mPendingEventIndex < mPendingEventCount) {
  3. const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
  4. //EPOLL_ID_INOTIFY类型事件
  5. if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {
  6. if (eventItem.events & EPOLLIN) {
  7. mPendingINotify = true;
  8. } else {
  9. ALOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events);
  10. }
  11. continue;
  12. }
  13. //EPOLL_ID_WAKE事件
  14. if (eventItem.data.u32 == EPOLL_ID_WAKE) {
  15. if (eventItem.events & EPOLLIN) {
  16. ALOGV("awoken after wake()");
  17. awoken = true;
  18. char buffer[16];
  19. ssize_t nRead;
  20. do {
  21. //进行数据读取
  22. nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
  23. } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
  24. } else {
  25. ALOGW("Received unexpected epoll event 0x%08x for wake read pipe.",
  26. eventItem.events);
  27. }
  28. continue;
  29. }
  30. ...
  31. Device* device = mDevices.valueAt(deviceIndex);
  32. if (eventItem.events & EPOLLIN) {
  33. int32_t readSize = read(device->fd, readBuffer,
  34. sizeof(struct input_event) * capacity);
  35. ...
  36. } else {
  37. int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
  38. //开始解析Buffuer
  39. size_t count = size_t(readSize) / sizeof(struct input_event);
  40. for (size_t i = 0; i < count; i++) {
  41. struct input_event& iev = readBuffer[i];
  42. ALOGV("%s got: time=%d.%06d, type=%d, code=%d, value=%d",
  43. device->path.string(),
  44. (int) iev.time.tv_sec, (int) iev.time.tv_usec,
  45. iev.type, iev.code, iev.value);
  46. // Some input devices may have a better concept of the time
  47. // when an input event was actually generated than the kernel
  48. // which simply timestamps all events on entry to evdev.
  49. // This is a custom Android extension of the input protocol
  50. // mainly intended for use with uinput based device drivers.
  51. if (iev.type == EV_MSC) {
  52. if (iev.code == MSC_ANDROID_TIME_SEC) {
  53. device->timestampOverrideSec = iev.value;
  54. continue;
  55. } else if (iev.code == MSC_ANDROID_TIME_USEC) {
  56. device->timestampOverrideUsec = iev.value;
  57. continue;
  58. }
  59. }
  60. if (device->timestampOverrideSec || device->timestampOverrideUsec) {
  61. iev.time.tv_sec = device->timestampOverrideSec;
  62. iev.time.tv_usec = device->timestampOverrideUsec;
  63. if (iev.type == EV_SYN && iev.code == SYN_REPORT) {
  64. device->timestampOverrideSec = 0;
  65. device->timestampOverrideUsec = 0;
  66. }
  67. ALOGV("applied override time %d.%06d",
  68. int(iev.time.tv_sec), int(iev.time.tv_usec));
  69. }
  70. // Use the time specified in the event instead of the current time
  71. // so that downstream code can get more accurate estimates of
  72. // event dispatch latency from the time the event is enqueued onto
  73. // the evdev client buffer.
  74. //
  75. // The event's timestamp fortuitously uses the same monotonic clock
  76. // time base as the rest of Android. The kernel event device driver
  77. // (drivers/input/evdev.c) obtains timestamps using ktime_get_ts().
  78. // The systemTime(SYSTEM_TIME_MONOTONIC) function we use everywhere
  79. // calls clock_gettime(CLOCK_MONOTONIC) which is implemented as a
  80. // system call that also queries ktime_get_ts().
  81. event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL
  82. + nsecs_t(iev.time.tv_usec) * 1000LL;
  83. ALOGV("event time %" PRId64 ", now %" PRId64, event->when, now);
  84. // Bug 7291243: Add a guard in case the kernel generates timestamps
  85. // that appear to be far into the future because they were generated
  86. // using the wrong clock source.
  87. //
  88. // This can happen because when the input device is initially opened
  89. // it has a default clock source of CLOCK_REALTIME. Any input events
  90. // enqueued right after the device is opened will have timestamps
  91. // generated using CLOCK_REALTIME. We later set the clock source
  92. // to CLOCK_MONOTONIC but it is already too late.
  93. //
  94. // Invalid input event timestamps can result in ANRs, crashes and
  95. // and other issues that are hard to track down. We must not let them
  96. // propagate through the system.
  97. //
  98. // Log a warning so that we notice the problem and recover gracefully.
  99. ...
  100. event->deviceId = deviceId;
  101. event->type = iev.type;
  102. event->code = iev.code;
  103. event->value = iev.value;
  104. event += 1;
  105. capacity -= 1;
  106. }
  107. if (capacity == 0) {
  108. // The result buffer is full. Reset the pending event index
  109. // so we will try to read the device again on the next iteration.
  110. mPendingEventIndex -= 1;
  111. break;
  112. }
  113. }
  114. }
  115. ...
这样就把数据读出来了

参考:
input之我见04——InputMangerService(Android7.0)

input之我见04——InputMangerService(Android7.0)

先抛出一个问题:
interceptKeyBeforQueueing 与 interceptKeyBeforDispatch的执行顺序。
很早以前…..
InputManager.cpp实例化的时候,会创建两个线程
  1. InputManager::InputManager(
  2. const sp<EventHubInterface>& eventHub,
  3. const sp<InputReaderPolicyInterface>& readerPolicy,
  4. const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
  5. mDispatcher = new InputDispatcher(dispatcherPolicy);
  6. mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
  7. initialize();
  8. }
  9. ...
  10. void InputManager::initialize() {
  11. mReaderThread = new InputReaderThread(mReader);
  12. mDispatcherThread = new InputDispatcherThread(mDispatcher);
  13. }
紧接着就start()
  1. status_t InputManager::start() {
  2. status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
  3. ...
  4. result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
  5. }
从此这两个线程就各自生活,貌似分道扬镳,其实却有着联系。
=>

InputReader

1,先看InputReader的构造函数,构造是在InputManager完成,
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
mDispatcher是InputDispacher的实例,而InputDispacher是继承了InputListenerInterface接口(继承爷爷接口),在此作为参数传递给InputReader,构造成mQueueListener变量,这就是InputReader与InputDispacher通信的桥梁。
=》
  1. InputReader::InputReader(const sp<EventHubInterface>& eventHub,
  2. const sp<InputReaderPolicyInterface>& policy,
  3. const sp<InputListenerInterface>& listener) :
  4. mContext(this), mEventHub(eventHub), mPolicy(policy),
  5. mGlobalMetaState(0), mGeneration(1),
  6. mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
  7. mConfigurationChangesToRefresh(0) {
  8. mQueuedListener = new QueuedInputListener(listener);
  9. { // acquire lock
  10. AutoMutex _l(mLock);
  11. refreshConfigurationLocked(0);
  12. updateGlobalMetaStateLocked();
  13. } // release lock
  14. }
来看InputReaderThread()的loopOnce(),是在InputReaderThread进行调用。
  1. bool InputReaderThread::threadLoop() {
  2. mReader->loopOnce();
  3. return true;
  4. }
InputReader.cpp里面
  1. void InputReader::loopOnce() {
  2. ...
  3. size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
  4. ...
  5. if (count) {
  6. processEventsLocked(mEventBuffer, count);
  7. }
  8. ...
  9. mQueuedListener->flush();
  10. }
去掉一些不重要的代码,loopOnce做了3件事
  • 通过EventHub->getEvents读取底层Input设备,是否有数据产生;
  • 如果有数据,就通过processEventLocked处理元数据,转化为input数据;
  • 处理完成之后,通过mQueueListener->flush()通知InputDispatcher去接收数据。
重点来看ProcessEventsLocked()
=>
processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
=>
device->process(rawEvents, count);
=>
InputMapper* mapper = mMappers[i];
mapper->process(rawEvent);
通过不同的mapper来调用相应的process函数,我们这里关注KeyBoard
=>
void KeyboardInputMapper::process(const RawEvent* rawEvent)
=>
processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode);
=>
把rawEvent封装成NotifyKeyArgs的指针,
NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
            down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
            AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
然后再传给某个Listener的nofifyKey,这个Listener就是InputListenerInterface,notifyKey调用就是InputDispacher的notifyKey。
getListener()->notifyKey(&args);
=>
进入InputDisaptcher.cpp
=>
  1. void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
  2. ...
  3. mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
  4. ...
  5. }
找到,interceptKeyBeforeQueueing终于又回到问题的出发点了。接下来看另外一处世界

InputDispacher

同理,先看InputDispacher的构造函数
  1. InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) :
  2. mPolicy(policy),
  3. mPendingEvent(NULL), mLastDropReason(DROP_REASON_NOT_DROPPED),
  4. mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX),
  5. mNextUnblockedEvent(NULL),
  6. mDispatchEnabled(false), mDispatchFrozen(false), mInputFilterEnabled(false),
  7. mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
  8. mLooper = new Looper(false);
  9. mKeyRepeatState.lastKeyEntry = NULL;
  10. policy->getDispatcherConfiguration(&mConfig);
  11. }
没啥要特别讲的,那么进入线程的loop函数
  1. bool InputDispatcherThread::threadLoop() {
  2. mDispatcher->dispatchOnce();
  3. return true;
  4. }
=>
  1. if (!haveCommandsLocked()) {
  2. dispatchOnceInnerLocked(&nextWakeupTime);
  3. }
如果有数据就进入dispachOnceInnerLocked,进一步处理。
=>
同理,我们只关注Key的处理事件
  1. void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
  2. ...
  3. case EventEntry::TYPE_KEY: {
  4. KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
  5. if (isAppSwitchDue) {
  6. if (isAppSwitchKeyEventLocked(typedEntry)) {
  7. resetPendingAppSwitchLocked(true);
  8. isAppSwitchDue = false;
  9. } else if (dropReason == DROP_REASON_NOT_DROPPED) {
  10. dropReason = DROP_REASON_APP_SWITCH;
  11. }
  12. }
  13. if (dropReason == DROP_REASON_NOT_DROPPED
  14. && isStaleEventLocked(currentTime, typedEntry)) {
  15. dropReason = DROP_REASON_STALE;
  16. }
  17. if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
  18. dropReason = DROP_REASON_BLOCKED;
  19. }
  20. done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
  21. break;
  22. }
  23. ...
  24. }
=>
dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime)
=>
  1. CommandEntry* commandEntry = postCommandLocked(
  2. & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
=>
 nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputWindowHandle,
            &event, entry->policyFlags);
又回到了问题的起点,interceptKeyBeforDispatching。我想到了我几年前画的一草图,其实当时并未完全理解,现在再看,只是信息更丰富了。
InputReader通过EventHub轮询读取底层数据,处理后,会通过Queue给到InputDispacher,InputDispacher再做进一步策略处理,发送给View做显示。
有了以上的信息,我们可以回答,一个Key键值的上报是先通过interceptKeyBeforQueueing 再执行interceptKeyBeforDispatch。
TOP