android之通过USB插拔流程来了解android UEvent

来源:互联网 发布:农资连锁软件 编辑:程序博客网 时间:2024/05/16 09:22

UEvent,全称User Space Event,是kernel通知用户空间的一种机制;
在Android中很多地方使用到了UEvent机制,如图:

图片说明文字

像HDMI,Battery,USB相关等;当我们需要接受底层的UEvent的时候,我们就需要注册一个UEventObserver,上层是如何处理这一过程的呢?来看看先;

比如当我们插拔usb的时候,手机的notification通知是如何触发的呢?
我现在就拿USB的插拔来分析下UEvent机制吧;首先看下它的用法

使用它当然得首先在UsbDeviceManager.Java中new一个其对象,

 1 2 3 4 5 6 7 8 91011121314151617
  /*     * Listens for uevent messages from the kernel to monitor the USB state     */    private final UEventObserver mUEventObserver = new UEventObserver() {        @Override        public void onUEvent(UEventObserver.UEvent event) {            if (DEBUG) Slog.v(TAG, "USB UEVENT: " + event.toString());            String state = event.get("USB_STATE");            String accessory = event.get("ACCESSORY");            if (state != null) {                mHandler.updateState(state);            } else if ("START".equals(accessory)) {                if (DEBUG) Slog.d(TAG, "got accessory start");                startAccessoryMode();            }        }    };

然后如何让其监听呢?

123
// Watch for USB configuration changes     mUEventObserver.startObserving(USB_STATE_MATCH);mUEventObserver.startObserving(ACCESSORY_START_MATCH);

startObserving的方法介绍如下:

 1 2 3 4 5 6 7 8 910
 void android.os.UEventObserver.startObserving(String match)Begin observation of UEvent's.This method will cause the UEvent thread to start if this is the first invocation of startObserving in this process.Once called, the UEvent thread will call onUEvent() when an incoming UEvent matches the specified string.This method can be called multiple times to register multiple matches. Only one call to stopObserving is required even with multiple registered matches.Parameters:match A substring of the UEvent to match. Try to be as specific as possible to avoid incurring unintended additional cost from processing irrelevant messages. Netlink messages can be moderately high bandwidth and are expensive to parse. For example, some devices may send one netlink message for each vsync period.

然后就是接收到UEvent后的各种处理,不做详细介绍了,就是会根据不同的操作做不同的处理,如adb enable/disable, MTP/PTT的切换,USB的插拔等处理:

 1 2 3 4 5 6 7 8 91011121314151617181920212223
public void updateState(String state) {            int connected, configured;            if ("DISCONNECTED".equals(state)) {                connected = 0;                configured = 0;            } else if ("CONNECTED".equals(state)) {                connected = 1;                configured = 0;            } else if ("CONFIGURED".equals(state)) {                connected = 1;                configured = 1;            } else {                Slog.e(TAG, "unknown state " + state);                return;            }            removeMessages(MSG_UPDATE_STATE);            Message msg = Message.obtain(this, MSG_UPDATE_STATE);            msg.arg1 = connected;            msg.arg2 = configured;            // debounce disconnects to avoid problems bringing up USB tethering            sendMessageDelayed(msg, (connected == 0) ? UPDATE_DELAY : 0);        }

我们重点看看UEventObserver是如何startObserving就可以接受到指定类型的UEvent的,也就是uevent通知是如何走到上面的;

往下跟踪代码可以发现在它的startObserving方法中我们可以看到UEventTread:

12345678
public final void startObserving(String match) {        if (match == null || match.isEmpty()) {            throw new IllegalArgumentException("match substring must be non-empty");        }        final UEventThread t = getThread();        t.addObserver(match, this);    }

首先获取UEventThread线程对象,然后想要监听的match加入到observer中去;

首先看看getThread是如何工作的,

123456789
private static UEventThread getThread() {        synchronized (UEventObserver.class) {            if (sThread == null) {                sThread = new UEventThread();                sThread.start();            }            return sThread;        }    }

看看UEventThread类,它继承自Thread,有sendEvent,addObserver,removeObserver方法,最重要的run是开启了一个无限循环,接收底层来的消息,然后通过sendEvent方法发送出去,上层接收,这下上层的处理就全部明白了:mKeysAndObservers用来存储match和observer,它是一个ArrayList对象,看其说明是一个用来一个match可以对应多个observer的存储对象,

 1 2 3 4 5 6 7 8 910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
private static final class UEventThread extends Thread {        /** Many to many mapping of string match to observer.         *  Multimap would be better, but not available in android, so use         *  an ArrayList where even elements are the String match and odd         *  elements the corresponding UEventObserver observer */        private final ArrayList<Object> mKeysAndObservers = new ArrayList<Object>();        private final ArrayList<UEventObserver> mTempObserversToSignal =                new ArrayList<UEventObserver>();        public UEventThread() {            super("UEventObserver");        }        @Override        public void run() {            nativeSetup();            while (true) {                String message = nativeWaitForNextEvent();                if (message != null) {                    if (DEBUG) {                        Log.d(TAG, message);                    }                    sendEvent(message);                }            }        }        private void sendEvent(String message) {            synchronized (mKeysAndObservers) {                final int N = mKeysAndObservers.size();                for (int i = 0; i < N; i += 2) {                    final String key = (String)mKeysAndObservers.get(i);                    if (message.contains(key)) {                        final UEventObserver observer =                                (UEventObserver)mKeysAndObservers.get(i + 1);                        mTempObserversToSignal.add(observer);                    }                }            }            if (!mTempObserversToSignal.isEmpty()) {                final UEvent event = new UEvent(message);                final int N = mTempObserversToSignal.size();                for (int i = 0; i < N; i++) {                    final UEventObserver observer = mTempObserversToSignal.get(i);                    observer.onUEvent(event);                }                mTempObserversToSignal.clear();            }        }        public void addObserver(String match, UEventObserver observer) {            synchronized (mKeysAndObservers) {                mKeysAndObservers.add(match);                mKeysAndObservers.add(observer);                nativeAddMatch(match);            }        }        /** Removes every key/value pair where value=observer from mObservers */        public void removeObserver(UEventObserver observer) {            synchronized (mKeysAndObservers) {                for (int i = 0; i < mKeysAndObservers.size(); ) {                    if (mKeysAndObservers.get(i + 1) == observer) {                        mKeysAndObservers.remove(i + 1);                        final String match = (String)mKeysAndObservers.remove(i);                        nativeRemoveMatch(match);                    } else {                        i += 2;                    }                }            }        }    }

java层代码就如上面解析的那样,我们继续往下看,在addObserver方法中调用了一个nativeAddMatch(match);方法,它是通过jni调用C++的UEventObserver实现,来看看这个方法:

123456
static void nativeAddMatch(JNIEnv* env, jclass clazz, jstring matchStr) {    ScopedUtfChars match(env, matchStr);    AutoMutex _l(gMatchesMutex);    gMatches.add(String8(match.c_str()));}

ScopedUtfChars是将matchStr转化成UTF8格式字符串,其c_str()方法就是返回这个它:

1
mUtfChars = env->GetStringUTFChars(s, NULL);

返回utfChars;

123
const char* c_str() const {        return mUtfChars;    }

而监听UEvent事件的到来就是:

1
String message = nativeWaitForNextEvent();

来仔细看下这个方法:

 1 2 3 4 5 6 7 8 910111213141516171819
static jstring nativeWaitForNextEvent(JNIEnv *env, jclass clazz) {    char buffer[1024];    for (;;) {        int length = uevent_next_event(buffer, sizeof(buffer) - 1);        if (length <= 0) {            return NULL;        }        buffer[length] = '/0'//这里反斜杠会导致文章发表出错,所以这里改成顺斜杠        ALOGV("Received uevent message: %s", buffer);        if (isMatch(buffer, length)) {            // Assume the message is ASCII.            jchar message[length];            for (int i = 0; i < length; i++) {                message[i] = buffer[i];            }            return env->NewString(message, length);        }    }}

开启一个无线循环用来监听uevent next event,进入uevent_next_event函数来看看在做什么;

 1 2 3 4 5 6 7 8 91011121314151617181920212223242526272829
int uevent_next_event(char* buffer, int buffer_length){    while (1) {        struct pollfd fds;        int nr;        fds.fd = fd;        fds.events = POLLIN;        fds.revents = 0;        nr = poll(&fds, 1, -1);        if(nr > 0 && (fds.revents & POLLIN)) {            SLOGE("recv buffer = %s", buffer);            int count = recv(fd, buffer, buffer_length, 0);            if (count > 0) {                struct uevent_handler *h;                pthread_mutex_lock(&uevent_handler_list_lock);                LIST_FOREACH(h, &uevent_handler_list, list)                    h->handler(h->handler_data, buffer, buffer_length);                pthread_mutex_unlock(&uevent_handler_list_lock);                return count;            }         }    }    // won't get here    return 0;}

这里的fd是在UEventThread start时,调用nativeSetup()时创建的socket套接字;可以知道,每次创建一个新的Observer,就会创建一个新的socket连接来进行交互;从log打印出来的这个buffer的格式为:

123
recv buffer = change@/devices/platform/msm_ssbi.0/pm8921-core/pm8921-charger/power_supply/batteryrecv buffer = online@/devices/system/cpu/cpu1
1
s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);

然后就是不断的recv来自这个socket的消息,然后往上报,总的来说就是kernel通过socket发送一个字符串,然后解析处理上报,通知上层做出反应;再kernel下的代码现在不跟踪了;

其实可以看出,kernel uevent会去拼接一个固定格式的字符串作为socket消息进行发送;

另外,也可以通过sendevent/getevent命令来模拟各种动作,比如我的另一篇关于耳机插入拔出的文章(
http://my.eoe.cn/cnhua5/archive/1252.html)中就有命令的用法:

1234
模拟标准3.5耳机插入sendevent /dev/input/event5 5 4 1sendevent /dev/input/event5 5 2 1sendevent /dev/input/event5 0 0 0

adb shell getevent,就可以看到各种操作的UEvent;如果你想了解命令的源码,在system/core/toolbox/下,在此我就不做介绍了,因为我也没看,不敢妄作评论啊;

好了,关于UEvent就介绍这么多了;如果有错误的地方希望eoer可以指出来;

0 0