UEvent,全称User Space Event,是kernel通知用户空间的一种机制;

在android中很多地方使用到了UEvent机制,如图:


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

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

如果要使用UEvent,首先得new一个UEventObserver对象,UEventObserver用于你需要哪些uevent事件,你可以添加到监听里面来并且当事件来临你自己的逻辑处理;代码在UsbDeviceManager.java中,如下:

  /*     * 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();            }        }    }

然后如何让其监听我们想要监听的事件呢?通过startObserving方法把想要监听的项加入即可;

// Watch for USB configuration changes     mUEventObserver.startObserving(USB_STATE_MATCH);mUEventObserver.startObserving(ACCESSORY_START_MATCH);
startObserving的方法介绍如下:

 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的插拔等处理:USB插拔的监听处理逻辑如下代码所示:

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线程启动

   
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是如何工作的,

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的存储对象,sendEvent中会调用到onUEvent方法,就会回调到我们接收到UEvent时自己做的处理了;
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 mKeysAndObservers = new ArrayList();        private final ArrayList mTempObserversToSignal =                new ArrayList();        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实现,来看看这个方法:   
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()方法就是返回这个它:
mUtfChars = env->GetStringUTFChars(s, NULL);

返回utfChars;
const char* c_str() const {        return mUtfChars;    }

而监听UEvent事件的到来就是:
String message = nativeWaitForNextEvent();

来仔细看下这个方法:
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函数来看看在做什么;
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的格式为:
recv buffer = [email protected]/devices/platform/msm_ssbi.0/pm8921-core/pm8921-charger/power_supply/batteryrecv buffer = [email protected]/devices/system/cpu/cpu1

s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);

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

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

原文作者: cnhua5

原文地址: http://my.eoe.cn/535201/archive/20509.html












更多相关文章

  1. Android(安卓)之 使用SoundPool播放音频
  2. android 之MediaPlayer播放音频与SoundPool的区别
  3. Android中BroadCastReceiver使用(整理)
  4. Android(安卓)桌面组件【widget】初探
  5. Android(安卓)AdapterView 源码分析以及其相关回收机制的分析
  6. 【Android不太基础】换个思路来监听home键
  7. 一个Android(安卓)Service小例子
  8. android点击AlertDialog的button不退出对话框的方法
  9. Android中运用Pull解析器读取XML文件

随机推荐

  1. 固定导航栏|可编辑表格
  2. Anaconda环境配置
  3. Rocky Linux镜像在阿里云镜像站首发上线
  4. 自定义方法通过类名获取对象集合
  5. 一套有效应对技术面算法题的方法论
  6. Apache服务器是如何解析PHP 小编来给你解
  7. 2021年校招程序员之阿里的十轮面试问题真
  8. PHP高并发高可用系统以及面试分析
  9. Python(十)文件操作
  10. Linux SRE 必经之路