**USB 拔插事件监听**
一、USB拔插事件监听
Kernal —> Framework UEvent这部分,并插拔USB时,Notification的更新。
安卓系统有状态栏,不管手机还是平板,在插入外设时,顶部会出现状态栏,提示信息。比如:U盘,耳机,Battery等。
1.1 USB常用设备框架介绍 (UsbService,java改UsbServiceManager.java)
当我们插拔usb的时候,手机的notification通知是如何触发的呢?
上图可知当我们需要接受底层的UEvent的时候,我们就需要注册一个UEventObserver,上层是如何处理这一过程的呢?
下面我们以USB为例分析UEventObserver是如何监听kernel的Event事件。
Kernal 与 Framework层交互 UEventObserver;插入与拔出USB设备,事件监听以及上报.UEventObserve
1.2UsbDeviceManager.java 监听内核事件
1.2.1 文件所在位置
./frameworks/base/services/usb/java/com/android/server/usb/UsbDeviceManager.java
1.2.2 初始化UEventObserver(判断处理事件)类
这里我们可以看到使用前必须先初始化一个新的UEventObserver类来处理事件,即函数onUEvent,这是一个回调函数,之后我们会看到他是怎么被调用的。这里这个回调函数根据处理的事件消息来判断是否要更新USB的状态,继而触发通知栏显示。
初始化完UEventObserver之后,我们还需要使用这个类,接下来的代码做了这个事。
1.2.3开启事件监听startObserving()方法
在UsbDeviceManager.java的构造方法中,添加了USB_STATE_MATCH和ACCESSORY_START_MATCH监听。因此,下面我们就按照源码跟踪framework与kernal交互的这段边界部分。
调用mEventObserver的StartObserving方法并重写onEvent方法,从下面的代码看出startobserving会传入一个参数 USB_STATE_MATCH.
从中我们看出他直接调用了UEventObserver的startObserving函数,并且指定了参数,即一段字符串。
其中startObserving函数 在UEventObserver.java
1.3 跳到UEventObserver.java文件
1.3.1 文件所在位置
/frameworks/base/core/java/android/os/UEventObserver.java
1.3.2 startObserving()函数实现
从中我们看出他直接调用了UEventObserver的startObserving函数,并且指定了参数,即一段字符串。
下面我们转到mUEventObserver中,UEventObserver 是一个Interface,定义了一个回调方法,onUEvent,通过它将kernal 上报事件传递至app/framework层、
的startObservering方法
函数一开始先判断match(前一个代码片段所传入的参数是USB_STATE_MATCH)是否为空,如果不为空会做两个动作,调用getThread()方法来获取UEventThread实例t,然后调用t的addObserver()方法。
接下来,startObserving方法转到了getThread()中。
1.3.3 startObserving方法转到了getThread()中,再到UEventThread()
这个函数比较简单,首先判断sThread是否为NULL,为空就新建一个 UEventThread线程,如果不为空,就直接返回线程。因为我们这里是第一次进行获取,所以这里会新建一个UEventThread类,并调用start函数启动它。
因为很多外设的Observer都是继承UEventObserver,所以sThread 是static变量,只要被第一次创建后,这个值就不会为NULL,这样就能做到多个外设通过一个线程来监控。进入到UEventThread看一下。
UEventThread函数的addObserver函数,把match和Observer都放入了mKeysAndObservers中,在使用的时候我们取match的下一个就是Observer了
当第一次启动这个线程的时候,会调用nativeSetup()方法做初始化,从名字 可以看出这个函数是native层来实现的。初始化完之后,进入一个while的死循环,不停的调用native层的 nativeWaitForNextEvent()函数来获取Event事件,然后将Event事件转换成message,再通过sendEvent() 将message事件传递给外设对应的Observer。先看一下初始化过程,nativeSetup()。
那么nativeSetup,UEvent哪里来呢?从android_os_UEventObserver.cpp
这里来
1.4跳到android_os_UEventObserver.cpp
1.4.1文件位置
frameworks/base/core/jni/android_os_UEventObserver.cpp
1.4.2 nativec层nativeSetup()函数调用
跳到uevent.c实现uevent_init
这个函数比较简单,就调用uevent_init()函数来做初始化,这个函数是一个C函数,实现在Uevent.c文件中。
1.5 跳到uevent.c实现uevent_init
1.5.1 文件位置
/hardware/libhardware_legacy/uevent/uevent.c
1.5.2 uevent_init()函数的实现
初始化socket,用来接收来自内核的event事件。从这个代码可以看出,用户空 间是用socket和内核通信的。初始化完之后就等待接受内核的Event事件。我们回到run()方法里面。初始化完了之后进入到while循环,不停 地调用nativeWaitForNextEvent()。
其实么这个函数调了很多层,最终只是调用了uevent实现里的初始化socket来接收 UEVENT消息的,注意这里的接收蔟为NETLINK_KOBJECT_UEVENT,这货好像是全局接收各种uevent的,不只是单单某一种。(具 体可以百度下socket知识,我也只是知道个皮毛。)
1.6 回跳到android_os_UEventObserver.cpp
1.6.1文件位置
frameworks/base/core/jni/android_os_UEventObserver.cpp
1.6.2 nativewaitForNextEvent()函数
这个函数又是一个死循环,不停地调用uevent_next_event(),从传入的参数 可以看出,从kernel传出来的event事件存在buffer中,紧接着调用isMatch判断buffer的数据是否匹配,如果匹配就将 buffer的数据转换成message,抛给上层server。
1.7回跳到uevent.c实现uevent_next_uevent()函数
1.7.1文件位置
/hardware/libhardware_legacy/uevent/uevent.c
1.7.2 uevent_next_uevent()函数
这个函数定义在Uevent.c中,死循环,不停的调用poll来等待socket的数据, 并将数据存放在buffer中,然后返回。
好了,回到nativeWaitForNextEvent函数里。在接收到uevent消息后,我们会调用isMatch函数来检测是否匹配。
1.8 跳到android_osUEventObserver.cpp 文件
1.8.1 文件位置
Frameworks/base/core/jni/android_os_UEventObserver.cpp
1.8.2 ismatch()函数的实现,比较enent消息和match
调用ismatch,这里就是判断是否这个返回的uevent消息存在于gMatches里,这里大家可能会问gMatches里什么都没有啊,不可能会存在。这时候我们在看下我们当初分析的时候,我们除了启动一个新线程,我们还做了什么。
我们还调用t.addObserver(match,this);函数。
1.9 跳到UEventObserver.java文件
1.9.1 文件所在位置
/frameworks/base/core/java/android/os/UEventObserver.java
1.9.2 addObserver()函数的实现
这个函数首先会把我们传进来的参数添加到mKeysAndObservers里,并且调用native函数nativeAddMatch,将match匹配字符串添加到gMatches(),用来在ismatch中比较。
2.0跳到android_os_UEventObserver.cpp
2.0.1文件位置
frameworks/base/core/jni/android_os_UEventObserver.cpp
2.0.2 nativeAddMatch()函数的实现
好吧,这里你看到了gMatches了吧,没错他就是记录我们要查询的字符串的。
之后我们回到判断gMatches的isMatch函数,这时候我们已经有了一个字符串,如果这时候我们接收的uevent消息里包含了这段字符串就会返回true
还记得我们设置的字符串吗 DEVPATH=/devices/virtual/android_usb/android0
一般USB线插拔的时候都会有这段uevent消息来告诉用户所属设配路径。
假设这时候确实插入了USB线,即会返回true
这个时候event事件已经比对检测完了,我们跳回UEventThread,这时候我们已经得到了一个uevent匹配的消息,然后调用sentEvent进行处理。
2.1 跳到UEventObserver.java文件
2.1.1 文件所在位置
/frameworks/base/core/java/android/os/UEventObserver.java
2.1.2 sentEvent()函数实现
首先它会根据返回的消息在 mKeysAndObservers查找UEventObserver,如果有匹配的话就返回下一个类,还记得我们在之前添加的两个参数吗,即查询字符串 和UEventObserver类。这里他就会找到UEventObserver,并且添加到mTempObserversToSignal里。当所有匹 配的UEventObserver被添加完后,循环调用每个类的onUEvent回调函数。这个函数我们已经在一开始新建UEventObserver的 时候实现过了。
2.2Notification的显示
2.2.1 跳到UsbDeviceManager.java 中
文件所在位置
./frameworks/base/services/usb/java/com/android/server/usb/UsbDeviceManager.java
2.2.2 回调onUEvent(event) 函数
从上面的分析我们可以看到,当有新的UEvent从kernel过来后,UEventObserver回调在UsbDeviceManager中,为此我们通过Handler消息机制,更新Notification的显示。
mHandler中的UpdateState:
2.2.3 此处对MSG_UPDATE_STATE 的处理是关键。
接下来我们只看updateUsbNotification方法。这个方法很简单,就是更新Notification,就是我们平时插上USB连接线的时候,顶部通知栏会显示的通知。
2.2.4 updateUsbNotification() 函数实现
2.3 USB 插拔事件时序图
参考文档:
Android框架理解之USB
http://blog.csdn.net/u011279649/article/details/15497991
android里面的USB功能—–Accessory模式
http://blog.csdn.net/hzl9966/article/details/51279574
Android 下的usb框架及功能点
http://blog.csdn.net/tianruxishui/article/details/37902959
android之UEventObserver分析
http://blog.csdn.net/yaphet__s/article/details/48136897
http://www.cfanz.cn/index.php?c=article&a=read&id=103231
http://blog.sina.com.cn/s/blog_6100a4f101015uw8.html