android之通過USB插拔流程來了解android UEvent

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<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實現,來看看這個方法:
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 = change@/devices/platform/msm_ssbi.0/pm8921-core/pm8921-charger/power_supply/battery

recv buffer = online@/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












發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章