【Android】Sensor框架Framework層解讀


Sensor整體架構

  

整體架構說明

  1. 黃色部分表示硬件,它要掛在I2C總線上
  2. 紅色部分表示驅動,驅動注冊到Kernel的Input Subsystem上,然后通過Event Device把Sensor數據傳到HAL層,准確說是HAL從Event讀
  3. 綠色部分表示動態庫,它封裝了整個Sensor的IPC機制,如SensorManager是客戶端,SensorService是服務端,HAL部分是封裝了服務端對Kernel的直接訪問
  4. 藍色部分就是我們的Framework和Application了,JNI負責訪問Sensor的客戶端,而Application就是具體的應用程序,用來接收Sensor返回的數據,並處理實現對應的UI效果,如屏幕旋轉,打電話時滅屏,自動調接背光(這三個功能的具體實現會在以后分析)

 


Sensor總體調用關系圖

  本節主要解讀Android的Framework層框架。

  

 

  Sensor 框架分為三個層次,客戶度、服務端、HAL層,服務端負責從HAL讀取數據,並將數據寫到管道中,客戶端通過管道讀取服務端數據。

客戶端主要類

  SensorManager.java

    從android4.1開始,把SensorManager定義為一個抽象類,定義了一些主要的方法,該類主要是應用層直接使用的類,提供給應用層的接口

  SystemSensorManager.java

    繼承於SensorManager,客戶端消息處理的實體,應用程序通過獲取其實例,並注冊監聽接口,獲取sensor數據。

  sensorEventListener接口

    用於注冊監聽的接口

  sensorThread

    SystemSensorManager的一個內部類,開啟一個新線程負責讀取讀取sensor數據,當注冊了sensorEventListener接口的時候才會啟動線程

  android_hardware_SensorManager.cpp

    負責與java層通信的JNI接口

  SensorManager.cpp

    sensorNative層的客戶端,負責與服務端SensorService.cpp的通信

  SenorEventQueue.cpp

    消息隊列

服務端主要類

  SensorService.cpp

    服務端數據處理中心

  SensorEventConnection

    從BnSensorEventConnection繼承來,實現接口ISensorEventConnection的一些方法,ISensorEventConnectionSensorEventQueue會保存一個指針,指向調用服務接口創建的SensorEventConnection對象

  Bittube.cpp

    在這個類中創建了管道,用於服務端與客戶端讀寫數據

  SensorDevice

    負責與HAL讀取數據

HAL

  Sensor.hgoogleSensor定義的Hal接口,單獨提出去

 


客戶端讀取數據

調用時序圖

 

apk注冊監聽器

SensorManager  mSensorManager =
 (SensorManager)getSystemService(SENSOR_SERVICE);
 Sensor   mAccelerometer =
 mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
 
    protected void onResume() {
           super.onResume();
          mSensorManager. registerListenerImpl (this, mAccelerometer,
     SensorManager.SENSOR_DELAY_NORMAL);
     }
     protected void onPause() {
           super.onPause();
         mSensorManager.unregisterListener(this);
     }
 
public interface SensorEventListener {
    public void onSensorChanged(SensorEvent event);
    public void onAccuracyChanged(Sensor sensor, int accuracy);   
}
View Code

  Activity實現了SensorEventListener接口。

  在onCreate方法中,獲取SystemSensorManager,並獲取到加速傳感器的Sensor

  在onResume方法中調用SystemSensorManager,registerListenerImpl注冊監聽器;

  當Sensor數據有改變的時候將會回調onSensorChanged方法。

初始化SystemSensorManager

 public SystemSensorManager(Context context,Looper mainLooper) {
        mMainLooper = mainLooper;       
              mContext = context;
             
        synchronized(sListeners) {
            if (!sSensorModuleInitialized) {
                sSensorModuleInitialized = true;
 
                nativeClassInit();
 
                // initialize the sensor list
                sensors_module_init();
                final ArrayList<Sensor> fullList = sFullSensorsList;
                int i = 0;
                do {
                    Sensor sensor = new Sensor();
                    i = sensors_module_get_next_sensor(sensor, i);
 
                    if (i>=0) {
                        //Log.d(TAG, "found sensor: " + sensor.getName() +
                        //        ", handle=" + sensor.getHandle());
                        fullList.add(sensor);
                        sHandleToSensor.append(sensor.getHandle(), sensor);
                    }
                } while (i>0);
 
                sPool = new SensorEventPool( sFullSensorsList.size()*2 );
                sSensorThread = new SensorThread();
            }
        }
    }
View Code

  系統開機啟動的時候,會創建SystemSensorManager的實例,在其構造函數中,主要做了四件事情:

  1. 初始化JNI調用JNI函數nativeClassInit()進行初始化
  2. 初始化Sensor列表調用JNI函數sensors_module_init,對Sensor模塊進行初始化。創建了nativeSensorManager的實例。
  3. 獲取Sensor列表調用JNI函數sensors_module_get_next_sensor()獲取Sensor,並存在sHandleToSensor列表中
  4. 構造SensorThread構造線程的類函數,並沒有啟動線程,當有應用注冊的時候才會啟動線程

啟動SensorThread線程讀取消息隊列中數據

protected boolean registerListenerImpl(SensorEventListener listener, Sensor sensor,
            int delay, Handler handler) {
    
        synchronized (sListeners) {
            ListenerDelegate l = null;
            for (ListenerDelegate i : sListeners) {
                if (i.getListener() == listener) {
                    l = i;
                }
            }
            …….
            // if we don't find it, add it to the list
            if (l == null) {
                l = new ListenerDelegate(listener, sensor, handler);
                sListeners.add(l);
                  ……
                    if (sSensorThread.startLocked()) {
                        if (!enableSensorLocked(sensor, delay)) {
                          …….
                        }
                 ……
            } else if (!l.hasSensor(sensor)) {
                l.addSensor(sensor);
                if (!enableSensorLocked(sensor, delay)) {
                    ……
                }
            }
        }
        return result;
    }
View Code

  當有應用程序調用registerListenerImpl()方法注冊監聽的時候,會調用SensorThread.startLoacked()啟動線程

  線程只會啟動一次,並調用enableSensorLocked()接口對指定的sensor使能,並設置采樣時間。

  SensorThreadRunnable實現了Runnable接口,在SensorThread類中被啟動

boolean startLocked() {
            try {
                if (mThread == null) {
                    SensorThreadRunnable runnable = new SensorThreadRunnable();
                    Thread thread = new Thread(runnable, SensorThread.class.getName());
                    thread.start();
                    synchronized (runnable) {  //隊列創建成功,線程同步
                        while (mSensorsReady == false) {
                            runnable.wait();
                        }
                    }
                  
        }
private class SensorThreadRunnable implements Runnable {
            SensorThreadRunnable() {
            }
            private boolean open() {
                sQueue = sensors_create_queue();
                return true;
            }
            public void run() {
                …….
                if (!open()) {
                    return;
                }
                synchronized (this) {
                    mSensorsReady = true;
                    this.notify();
                }
                while (true) {
                    final int sensor = sensors_data_poll(sQueue, values, status, timestamp);
                    …….
            }
        }
    }
View Code

  在open函數中調用JNI函數sensors_create_queue()來創建消息隊列,然后調用SensorManager. createEventQueue()創建。

  在startLocked函數中啟動新的線程后,做了一個while的等待while (mSensorsReady == false),只有當mSensorsReady等於true的時候,才會執行enableSensorLocked()函數對sensor使能。而mSensorsReady變量,是在open()調用創建消息隊列成功之后才會true,所以認為,三個功能調用順序是如下:

  1. 調用open函數創建消息隊列
  2. 調用enableSensorLocked()函數對sensor使能
  3. 調用sensors_data_poll從消息隊列中讀取數據,而且是在while (true)循環里一直讀取

 


服務端實現

調用時序圖

 

 

啟動SensorService服務

  在SystemServer進程中的main函數中,通過JNI調用,調用到com_android_server_SystemServer.cppandroid_server_SystemServer_init1()方法,該方法又調用system_init.cpp中的system_init():

extern "C" status_t system_init()
{
    ……
    property_get("system_init.startsensorservice", propBuf, "1");
    if (strcmp(propBuf, "1") == 0) {
        // Start the sensor service
        SensorService::instantiate();
    }
    …..
    return NO_ERROR;
}
View Code

  在這里創建了SensorService的實例。

SensorService初始化

  SensorService創建完之后,將會調用SensorService::onFirstRef()方法,在該方法中完成初始化工作。

  首先獲取SensorDevice實例,在其構造函數中,完成了對Sensor模塊HAL的初始化:

SensorDevice::SensorDevice()
    :  mSensorDevice(0),
       mSensorModule(0)
{
    status_t err = hw_get_module(SENSORS_HARDWARE_MODULE_ID,
            (hw_module_t const**)&mSensorModule);
 
    if (mSensorModule) {
        err = sensors_open(&mSensorModule->common, &mSensorDevice);
 
        ALOGE_IF(err, "couldn't open device for module %s (%s)",
                SENSORS_HARDWARE_MODULE_ID, strerror(-err));
 
        if (mSensorDevice) {
            sensor_t const* list;
            ssize_t count = mSensorModule->get_sensors_list(mSensorModule, &list);
            mActivationCount.setCapacity(count);
            Info model;
            for (size_t i=0 ; i<size_t(count) ; i++) {
                mActivationCount.add(list[i].handle, model);
                mSensorDevice->activate(mSensorDevice, list[i].handle, 0);
            }
        }
    }
}
View Code

  這里主要做了三個工作:

  1. 調用HAL層的hw_get_modele()方法,加載Sensor模塊so文件
  2. 調用sensor.hsensors_open方法打開設備
  3. 調用sensors_poll_device_t->activate()Sensor模塊使能

  再看看SensorService::onFirstRef()方法:

void SensorService::onFirstRef()
{
    SensorDevice& dev(SensorDevice::getInstance());
 
    if (dev.initCheck() == NO_ERROR) {
        sensor_t const* list;
        ssize_t count = dev.getSensorList(&list);
        if (count > 0) {
            ……
            for (ssize_t i=0 ; i<count ; i++) {
                registerSensor( new HardwareSensor(list[i]) );
                ……
            }
 
            // it's safe to instantiate the SensorFusion object here
            // (it wants to be instantiated after h/w sensors have been
            // registered)
            const SensorFusion& fusion(SensorFusion::getInstance());
 
            if (hasGyro) {
               ……
            }
            ……
            run("SensorService", PRIORITY_URGENT_DISPLAY);
            mInitCheck = NO_ERROR;
        }
    }
}
View Code

  在這個方法中,主要做了4件事情:

  1. 創建SensorDevice實例
  2. 獲取Sensor列表
  3. 調用SensorDevice.getSensorList(),獲取Sensor模塊所有傳感器列表
  4. 為每個傳感器注冊監聽器

  registerSensor( new HardwareSensor(list[i]) );

void SensorService::registerSensor(SensorInterface* s)
{
    sensors_event_t event;
    memset(&event, 0, sizeof(event));
 
    const Sensor sensor(s->getSensor());
    // 添加到Sensor列表,給客戶端使用
    mSensorList.add(sensor);
    // add to our handle->SensorInterface mapping
    mSensorMap.add(sensor.getHandle(), s);
    // create an entry in the mLastEventSeen array
    mLastEventSeen.add(sensor.getHandle(), event);
}
View Code

  HardwareSensor實現了SensorInterface接口。

  1. 啟動線程讀取數據

  調用run方法啟動新線程,將調用SensorService::threadLoop()方法。

在新的線程中讀取HAL層數據

  SensorService實現了Thread類,當在onFirstRef中調用run方法后,將在新的線程中調用SensorService::threadLoop()方法。

bool SensorService::threadLoop()
{
    ……
    do {
        count = device.poll(buffer, numEventMax);
 
        recordLastValue(buffer, count);
        ……
 
        // send our events to clients...
        const SortedVector< wp<SensorEventConnection> > activeConnections(
                getActiveConnections());
        size_t numConnections = activeConnections.size();
        for (size_t i=0 ; i<numConnections ; i++) {
            sp<SensorEventConnection> connection(
                    activeConnections[i].promote());
            if (connection != 0) {
                connection->sendEvents(buffer, count, scratch);
            }
        }
    } while (count >= 0 || Thread::exitPending());
    return false;
}
View Code

  在while循環中一直讀取HAL層數據,再調用SensorEventConnection->sendEvents將數據寫到管道中。

 


客戶端與服務端通信

數據傳送

  客戶端與服務端通信的狀態圖

  

  客戶端服務端線程

    在圖中可以看到有兩個線程:

    1. 一個是服務端的一個線程,這個線程負責源源不斷的從HAL讀取數據。
    2. 另一個是客戶端的一個線程,客戶端線程負責從消息隊列中讀數據。

  創建消息隊列

    客戶端可以創建多個消息隊列,一個消息隊列對應有一個與服務器通信的連接接口

  創建連接接口

    服務端與客戶端溝通的橋梁,服務端讀取到HAL層數據后,會掃面有多少個與客戶端連接的接口,然后往每個接口的管道中寫數據

  創建管道

    每一個連接接口都有對應的一個管道。

  上面是設計者設計數據傳送的原理,但是目前Android4.1上面的數據傳送不能完全按照上面的理解。

  因為在實際使用中,消息隊列只會創建一個,也就是說客戶端與服務端之間的通信只有一個連接接口,只有一個管道傳數據。

  那么數據的形式是怎么從HAL層傳到JAVA層的呢?

  其實數據是以一個結構體sensors_event_t的形式從HAL層傳到JNI層。看看HALsensors_event_t結構體:

typedef struct sensors_event_t {
    int32_t version;
    int32_t sensor;            //標識符
    int32_t type;             //傳感器類型
    int32_t reserved0;
    int64_t timestamp;        //時間戳
    union {
        float           data[16];
        sensors_vec_t   acceleration;   //加速度
        sensors_vec_t   magnetic;      //磁矢量
        sensors_vec_t   orientation;     //方向
        sensors_vec_t   gyro;          //陀螺儀
        float           temperature;     //溫度
        float           distance;        //距離
        float           light;           //光照
        float           pressure;         //壓力
        float           relative_humidity;  //相對濕度
    };
    uint32_t        reserved1[4];
} sensors_event_t;
View Code

  在JNI層有一個ASensorEvent結構體與sensors_event_t向對應,frameworks/native/include/android/sensor.h

typedef struct ASensorEvent {
    int32_t version;
    int32_t sensor;
    int32_t type;
    int32_t reserved0;
    int64_t timestamp;
    union {
        float           data[16];
        ASensorVector   vector;
        ASensorVector   acceleration;
        ASensorVector   magnetic;
        float           temperature;
        float           distance;
        float           light;
        float           pressure;
    };
    int32_t reserved1[4];
} ASensorEvent;
View Code

 


交互調用時序圖

  

  經過前面的介紹,現在知道了客戶端實現的方式及服務端的實現,但是沒有具體講到它們是如何進行通信的,現在看看客戶端與服務端之間的通信。

  主要涉及的是進程間通信,有IBind和管道通信。

  客戶端通過IBind通信獲取到服務端的遠程調用然后通過管道進行sensor數據的傳輸。

服務端

  native層實現了sensor服務的核心實現,Sensor服務的主要流程的實現在sensorservice類中,下面重點分析下這個類的流程。

class SensorService :
        public BinderService<SensorService>,
        public BnSensorServer,
        protected Thread
View Code

  看看sensorService繼承的類:

    繼承BinderService<SensorService>這個模板類添加到系統服務,用於Ibinder進程間通信。

template<typename SERVICE>
class BinderService
{
public:
    static status_t publish() {
        sp<IServiceManager> sm(defaultServiceManager());
        return sm->addService(String16(SERVICE::getServiceName()), new SERVICE());
    }
 
    static void publishAndJoinThreadPool() {
        sp<ProcessState> proc(ProcessState::self());
        sp<IServiceManager> sm(defaultServiceManager());
        sm->addService(String16(SERVICE::getServiceName()), new SERVICE());
        ProcessState::self()->startThreadPool();
        IPCThreadState::self()->joinThreadPool();
    }
 
    static void instantiate() { publish(); }
};
}; // namespace android
View Code

  在前面的介紹中,SensorService服務的實例是在System_init.cpp中調用SensorService::instantiate()創建的,即調用了上面的instantiate()方法,接着調用了publish(),在該方法中,我們看到了new SensorService的實例,並且調用了defaultServiceManager::addService()Sensor服務添加到了系統服務管理中,客戶端可以通過defaultServiceManager:getService()獲取到Sensor服務的實例。

  繼承BnSensorServer這個是sensor服務抽象接口類提供給客戶端調用:

class Sensor;
class ISensorEventConnection;
 
class ISensorServer : public IInterface
{
public:
    DECLARE_META_INTERFACE(SensorServer);
    //獲取Sensor列表
virtual Vector<Sensor> getSensorList() = 0;
//創建一個連接的接口,這些都是提供給客戶端的抽象接口,服務端實例化時候必須實現
    virtual sp<ISensorEventConnection> createSensorEventConnection() = 0;
};
class BnSensorServer : public BnInterface<ISensorServer>
{
public:
    //傳輸打包數據的通訊接口,在BnSensorServer被實現
    virtual status_t    onTransact( uint32_t code,
                                    const Parcel& data,
                                    Parcel* reply,
                                    uint32_t flags = 0);
};
}; // namespace android
View Code

  ISensorServer接口提供了兩個抽象方法給客戶端調用,關鍵在於createSensorEventConnection()方法,該在服務端被實現,在客戶端被調用,並返回一個SensorEventConnection的實例,創建連接,客戶端拿到SensorEventConnection實例之后,可以對sensor進行通信操作,僅僅作為通信的接口而已,它並沒有用來傳送Sensor數據,因為Sensor數據量比較大,IBind實現比較困難。真正實現Sensor數據傳送的是管道,在創建SensorEventConnection實例中,創建了BitTube對象,里面創建了管道,用於客戶端與服務端的通信。

客戶端

  時序圖

  

  客戶端主要在SensorManager.cpp中創建消息隊列

class ISensorEventConnection;
class Sensor;
class Looper;
 
// ----------------------------------------------------------------------------
 
class SensorEventQueue : public ASensorEventQueue, public RefBase
{
public:
            SensorEventQueue(const sp<ISensorEventConnection>& connection);
    virtual ~SensorEventQueue();
    virtual void onFirstRef();
    //獲取管道句柄
    int getFd() const;
    //向管道寫數據
    static ssize_t write(const sp<BitTube>& tube,
            ASensorEvent const* events, size_t numEvents);
    //向管道讀數據
    ssize_t read(ASensorEvent* events, size_t numEvents);
 
    status_t waitForEvent() const;
    status_t wake() const;
    //使能Sensor傳感器
    status_t enableSensor(Sensor const* sensor) const;
    status_t disableSensor(Sensor const* sensor) const;
    status_t setEventRate(Sensor const* sensor, nsecs_t ns) const;
 
    // these are here only to support SensorManager.java
    status_t enableSensor(int32_t handle, int32_t us) const;
    status_t disableSensor(int32_t handle) const;
 
private:
sp<Looper> getLooper() const;
//連接接口,在SensorService中創建的
sp<ISensorEventConnection> mSensorEventConnection;
//管道指針
    sp<BitTube> mSensorChannel;
    mutable Mutex mLock;
    mutable sp<Looper> mLooper;
};
View Code

 

  SensorEventQueue類作為消息隊列,作用非常重要,在創建其實例的時候,傳入了SensorEventConnection的實例,SensorEventConnection繼承於ISensorEventConnection

  SensorEventConnection其實是客戶端調用SensorServicecreateSensorEventConnection()方法創建的,它是客戶端與服務端溝通的橋梁,通過這個橋梁,可以完成一下任務:

  1. 獲取管道的句柄
  2. 往管道讀寫數據
  3. 通知服務端對Sensor使能

 


流程解析

客戶端獲取SensorService服務實例

  客戶端初始化的時候,即SystemSensorManager的構造函數中,通過JNI調用,創建nativeSensorManager的實例,然后調用SensorManager::assertStateLocked()方法做一些初始化的動作。

status_t SensorManager::assertStateLocked() const {
    if (mSensorServer == NULL) {
        // try for one second
        const String16 name("sensorservice");
        ……
            status_t err = getService(name, &mSensorServer);
        ……
        mSensors = mSensorServer->getSensorList();
        size_t count = mSensors.size();
        mSensorList = (Sensor const**)malloc(count * sizeof(Sensor*));
        for (size_t i=0 ; i<count ; i++) {
            mSensorList[i] = mSensors.array() + i;
        }
    }
    return NO_ERROR;
}
View Code

 

  前面我們講到過,SensorService的創建的時候調用了defaultServiceManager:getService()將服務添加到了系統服務管理中。

  現在我們又調用defaultServiceManager::geService()獲取到SensorService服務的實例。

  在通過IBind通信,就可以獲取到Sensor列表,所以在客戶端初始化的時候,做了兩件事情:

  1.  獲取SensorService實例引用
  2.  獲取Sensor傳感器列表

注冊SensorLisenter

  時序圖

  

  new ListenerDelegate(SensorEventListener listener, Sensor sensor, Handler handler) 

  在這個構造函數中會創建一個Handler,它會在獲取到Sensor數據的時候被調用。

mHandler = new Handler(looper) {  
                @Override  
                public void handleMessage(Message msg) {  
                    final SensorEvent t = (SensorEvent)msg.obj;  
                    final int handle = t.sensor.getHandle();  
  
                    switch (t.sensor.getType()) {  
                        // Only report accuracy for sensors that support it.  
                        case Sensor.TYPE_MAGNETIC_FIELD:  
                        case Sensor.TYPE_ORIENTATION:  
                            // call onAccuracyChanged() only if the value changes  
                            final int accuracy = mSensorAccuracies.get(handle);  
                            if ((t.accuracy >= 0) && (accuracy != t.accuracy)) {  
                                mSensorAccuracies.put(handle, t.accuracy);  
                                mSensorEventListener.onAccuracyChanged(t.sensor, t.accuracy);  
                            }  
                            break;  
                        default:  
                            // For other sensors, just report the accuracy once  
                            if (mFirstEvent.get(handle) == false) {  
                                mFirstEvent.put(handle, true);  
                                mSensorEventListener.onAccuracyChanged(  
                                        t.sensor, SENSOR_STATUS_ACCURACY_HIGH);  
                            }  
                            break;  
                    }  
  
                    mSensorEventListener.onSensorChanged(t);  
                    sPool.returnToPool(t);  
                }  
            };  
View Code

 

創建消息隊列

  時序圖

  

  當客戶端第一次注冊監聽器的時候,就需要創建一個消息隊列,也就是說,android在目前的實現中,只創建了一個消息隊列,一個消息隊列中有一個管道,用於服務端與客戶斷傳送Sensor數據。

  在SensorManager.cpp中的createEventQueue方法創建消息隊列:

sp<SensorEventQueue> SensorManager::createEventQueue()
{
    sp<SensorEventQueue> queue;
    Mutex::Autolock _l(mLock);
while (assertStateLocked() == NO_ERROR) {
    //創建連接接口
        sp<ISensorEventConnection> connection =
                mSensorServer->createSensorEventConnection();
        if (connection == NULL) {
            // SensorService just died.
            LOGE("createEventQueue: connection is NULL. SensorService died.");
            continue;
        }
//創建消息隊列
        queue = new SensorEventQueue(connection);
        break;
    }
    return queue;
}
View Code

  客戶端與服務器創建一個SensorEventConnection連接接口,而一個消息隊列中包含一個連接接口。

  創建連接接口:

sp<ISensorEventConnection> SensorService::createSensorEventConnection()
{
    sp<SensorEventConnection> result(new SensorEventConnection(this));
    return result;
}
SensorService::SensorEventConnection::SensorEventConnection(
        const sp<SensorService>& service)
    : mService(service), mChannel(new BitTube ())
{
}
View Code

  關鍵在於BitTube,在構造函數中創建了管道:

BitTube::BitTube()
    : mSendFd(-1), mReceiveFd(-1)
{
    int sockets[2];
    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets) == 0) {
        int size = SOCKET_BUFFER_SIZE;
        setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
        setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
        setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
        setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
        fcntl(sockets[0], F_SETFL, O_NONBLOCK);
        fcntl(sockets[1], F_SETFL, O_NONBLOCK);
        mReceiveFd = sockets[0];
        mSendFd = sockets[1];
    } else {
        mReceiveFd = -errno;
        ALOGE("BitTube: pipe creation failed (%s)", strerror(-mReceiveFd));
    }
}
View Code

  其中:fds[0]就是對應的mReceiveFd,是管道的讀端,sensor數據的讀取端,對應的是客戶端進程訪問的。

  fds[1]就是對應mSendFd,是管道的寫端,sensor數據寫入端,sensor的服務進程訪問的一端。

  通過pipe(fds)創建管道,通過fcntl來設置操作管道的方式,設置通道兩端的操作方式為O_NONBLOCK ,非阻塞IO方式,readwrite調用返回-1EAGAIN錯誤。

  總結下消息隊列

  客戶端第一次注冊監聽器的時候,就需要創建一個消息隊列,客戶端創了SensorThread線程從消息隊列里面讀取數據。

  SensorEventQueue中有一個SensorEventConnection實例的引用,SensorEventConnection中有一個BitTube實例的引用。

使能Sensor

  客戶端創建了連接接口SensorEventConnection后,可以調用其方法使能Sensor傳感器:

status_t SensorService::SensorEventConnection::enableDisable(
        int handle, bool enabled)
{
    status_t err;
    if (enabled) {
        err = mService->enable(this, handle);
    } else {
        err = mService->disable(this, handle);
    }
    return err;
}
View Code

  handle對應着Sensor傳感器的句柄

服務端往管道寫數據

bool SensorService::threadLoop()
{
    ……
    do {
        count = device.poll(buffer, numEventMax);
 
        recordLastValue(buffer, count);
        ……
 
        // send our events to clients...
        const SortedVector< wp<SensorEventConnection> > activeConnections(
                getActiveConnections());
        size_t numConnections = activeConnections.size();
        for (size_t i=0 ; i<numConnections ; i++) {
            sp<SensorEventConnection> connection(
                    activeConnections[i].promote());
            if (connection != 0) {
                connection->sendEvents(buffer, count, scratch);
            }
        }
    } while (count >= 0 || Thread::exitPending());
    return false;
}
View Code

  前面介紹過,在SensorService中,創建了一個線程不斷從HAL層讀取Sensor數據,就是在threadLoop方法中。

  關鍵在與下面了一個for循環,其實是掃描有多少個客戶端連接接口,然后就往沒每個連接的管道中寫數據。

status_t SensorService::SensorEventConnection::sendEvents(
        sensors_event_t const* buffer, size_t numEvents,
        sensors_event_t* scratch)
{
    // filter out events not for this connection
    size_t count = 0;
    if (scratch) {
      ……
    }
    ……
    if (count == 0)
        return 0;
 
    ssize_t size = mChannel->write(scratch, count*sizeof(sensors_event_t));
    ……
}
View Code

  調用該連接接口的BitTube::write():

ssize_t BitTube::write(void const* vaddr, size_t size)
{
    ssize_t err, len;
    do {
        len = ::send(mSendFd, vaddr, size, MSG_DONTWAIT | MSG_NOSIGNAL);
        err = len < 0 ? errno : 0;
    } while (err == EINTR);
    return err == 0 ? len : -err;
 
} }
View Code

  到此,服務端就完成了往管道的寫端寫入數據。

客戶端讀管道數據

  時序圖

  

ssize_t SensorEventQueue::read(ASensorEvent* events, size_t numEvents)
{
    return BitTube::recvObjects(mSensorChannel, events, numEvents);
}
View Code

  調用到了BitTube::read():

static ssize_t recvObjects(const sp<BitTube>& tube,
            T* events, size_t count) {
        return recvObjects(tube, events, count, sizeof(T));
    }
ssize_t BitTube::recvObjects(const sp<BitTube>& tube,
        void* events, size_t count, size_t objSize)
{
    ssize_t numObjects = 0;
    for (size_t i=0 ; i<count ; i++) {
        char* vaddr = reinterpret_cast<char*>(events) + objSize * i;
        ssize_t size = tube->read(vaddr, objSize);
        if (size < 0) {
            // error occurred
            return size;
        } else if (size == 0) {
            // no more messages
            break;
        }
        numObjects++;
    }
    return numObjects;
}
 
ssize_t BitTube::read(void* vaddr, size_t size)
{
    ssize_t err, len;
    do {
        len = ::recv(mReceiveFd, vaddr, size, MSG_DONTWAIT);
        err = len < 0 ? errno : 0;
    } while (err == EINTR);
    if (err == EAGAIN || err == EWOULDBLOCK) {
        return 0;
    }
    return err == 0 ? len : -err;
}
View Code

 


參考文章

http://blog.sina.com.cn/u/2314572533

http://blog.csdn.net/cs_lht


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM