1. 程式人生 > >android之通過USB插拔流程來了解android UEvent

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 = [email protected]/devices/platform/msm_ssbi.0/pm8921-core/pm8921-charger/power_supply/battery

recv buffer = [email protected]/devices/system/cpu/cpu1

s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);

然後就是不斷的recv來自這個socket的訊息,然後往上報,總的來說就是kernel通過socket傳送一個字串,然後解析處理上報,通知上層做出反應;再kernel下的程式碼現在不跟蹤了;

其實可以看出,kernel uevent會去拼接一個固定格式的字串作為socket訊息進行傳送







相關推薦

android通過USB流程android UEvent

UEvent,全稱User Space Event,是kernel通知使用者空間的一種機制;在android中很多地方使用到了UEvent機制,如圖: 像HDMI,Battery,USB相關等;當我們需要接受底層的UEvent的時候,我們就需要註冊一個UEventOb

從一個音樂播放器android的Service元件和BroadcastReceiver

從一個音樂播放器來理解android的Service元件和BroadcastReceiver 前言:很久以前寫過一個音樂播放器,採用Service元件實現後臺播放,BroadcastReceiver實現訊息傳遞。播放器原始碼在此:簡陋音樂播放器。 Music

「補課」進行時:設計模式(2)——通過一個超級汽車工廠工廠模式

![](https://cdn.geekdigging.com/DesignPatterns/java_design_pattern.jpg) ## 1. 超級汽車工廠 汽車相信大家都不陌生,我們現在最常用的交通工具得益於在賓士在 1885 年研製的第一輛「三輪車」,就是下面這個傢伙: ![](http

Android檢測手機上和USB盾以及線和

1、MyUsbManager.java public class MyUsbManager { public static final String ACTION_USB_STATE = "android.hardware.usb.action.USB_STATE

Android與H5互調(通過實例Hybrid App)

ext 傳感器 顯示 相同 blank show ima 一次 點擊 前些日子,Android原生開發將被取締的吵得火熱,JavaScript是能做一個完全的APP,但只使用JavaScript做出來的APP也不會牛逼到哪裏去。最好的是混合(Hybrid)開發,在需要的

通過一個輪播圖構造函數

num 類型 bsp 函數的原型 size cti type屬性 箭頭 true 例子:https://github.com/wayaha/rotateChart 在ES5中,構造函數的使用可以說是很能體現面向對象的編程思想,有學過c的同學,可以很明顯體會到面向過程和面向對

Android通過ContentResolver獲取手機圖片和視訊的路徑和生成縮圖和縮圖路徑

1 問題 獲取手機所有圖片和視訊的路徑和生成圖片和視訊的縮圖和縮圖路徑 生成縮圖我們用的系統函式 public static Bitmap getThumbnail(ContentResolver cr, long origId, int kind, Opti

android通過usb和pc進行通訊

找到的最好的部落格 https://blog.csdn.net/jakemiao/article/details/17270785?locationNum=7&fps=1 下面是demo: https://github.com/yunzheyue/usbCommunication

JAVA監控windows7系統的USB事件

package usb;import java.io.File;public class UsbDevice { //掃描系統的碟符 File[] root = File.listRoots(); public static void main(String[] args) {

Android USB Host與USB Hid裝置的通訊(印表機和android通訊,通過USB

記事:android和印表機通過網路通訊(網路印表機),有空也要記錄一下 堆疊關係也要記錄一下(通常我們所說的堆疊都是指棧,比如一個方法被呼叫的時候,我們就會把相關變數壓棧(巢狀才會壓棧,一個方法呼叫完是要出棧的),除非遞迴,一般情況下,棧是不會溢位的) 近期一直在做Android USB

android 手機通過usb資料線與OTG裝置通訊

1.首先在AndroidManifest.xml檔案中新增所需要的許可權 <uses-feature android:name="android.hardware.usb.host" /> <uses-permission android:name="an

Android 裝置通過USB 轉RJ45有線網絡卡上網

一、下載驅動 網絡卡採用USB 2.0轉乙太網控制晶片AX88772B, android 2.3 和4.0下的通用網絡卡驅動不支援這款晶片, 需要從網上下載最新的linux 下AX88772B驅動 二、將下載的驅動原始碼編譯進kernel image. 下載的驅

RockchipRK3288HDMI介面檢測

Rockchip之RK3288HDMI介面插拔檢測 任務背景: 最近機器的一塊屏出現不顯示或者白屏現象,這塊屏是一塊MIPI屏,但它是由3288上的HDMI介面通過一塊LPC轉接板轉成MIPI介面的,所以根源還是HDMI介面,猜想可能是HDMI轉MIPI的HDMI插拔檢測腳導致的,因此,

Android通過socket.io實現長連線

在專案開發中,時常有服務端向客戶端主動發起交流的需求,可以整合極光推送,但是如果網路不好的情況下,推送可能會遲遲收不到,這樣就導致了使用者體驗得不到保證。 若改用socket實現長連線的話,速度就快很

淺談AndroidActivity Decor View建立流程介紹

6 Activity DecorView建立流程介紹 上頭已經完整的介紹了Activity的啟動流程,Activity是如何繫結Window,Window的décor view是如何通過ViewRootImpl與WMS建立關聯的,也就是說,整個框架已經有了,唯一缺的就是Ac

Android事件處理詳

        整個過程從Kernel檢測到SD卡插入事件開始,之前的一些硬體中斷的觸發以及driver的載入這裡並不敘述,一直到SD卡掛載訊息更新到“Android——系統設定——儲存”一項中。        1.    Kernel發出SD卡插入uevent。        2.    Netlink

Android通過shape.xml製作漸變背景色

一、在res/drawable/下建一個xml檔案,例如:shape_background_grey.xml: ①.簡單的型別。 <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="h

Android手機通過USB資料線共享Linux電腦網路

這裡要講述的是手機通過usb資料線共享電腦(linux系統)的網路來自由上網。通過USB資料線將手機與電腦相連, 再分別在電腦和手機上虛擬出一個網路介面用於網路通訊, 這很類似於VPN與虛擬機器上網的原理。 好處是不論臺式還是筆記本都適用, 只要有USB介面,並且可以一邊

Windows下檢測USB的demo

#include <Windows.h> #include <stdlib.h> #include <string.h> #include <tchar.h> #include <string> #include

Android如何通過手機獲取驗證碼完成註冊功能

註冊很多app或者網路賬戶的時候,經常需要手機獲取驗證碼,來完成註冊,那時年少,只是覺得手機獲取驗證碼這件事兒很好玩,並沒有關心太多,她是如何實現的,以及她背後的故事到底是什麼樣子的,現在小編接手的這個專案裡面,就需要通過手機號進行註冊,並且手機號傳送相應的驗證碼,來完成註冊,那麼在一些應用app裡面到底