轉自:http://blog.sina.com.cn/s/blog_89f592f5010132qy.html
(一) HAL實現
1 Android sensor構建
Android4.1 系統內置對傳感器的支持達13種,他們分別是:加速度傳感器(accelerometer)、磁力傳感器(magnetic field)、方向傳感器(orientation)、陀螺儀(gyroscope)、環境光照傳感器(light)、壓力傳感器(pressure)、溫度傳感器(temperature)和距離傳感器(proximity)等。Android實現傳感器系統包括以下幾個部分:
n
n
n
n
各部分之間架構圖如下:
2 Sensor HAL層接口
Google爲Sensor提供了統一的HAL接口,不同的硬件廠商需要根據該接口來實現並完成具體的硬件抽象層,Android中Sensor的HAL接口定義在:
hardware/libhardware/include/hardware/sensors.h
n
#define SENSOR_TYPE_ACCELEROMETER
#define SENSOR_TYPE_MAGNETIC_FIELD
#define SENSOR_TYPE_ORIENTATION
#define SENSOR_TYPE_GYROSCOPE
#define SENSOR_TYPE_LIGHT
#define SENSOR_TYPE_PRESSURE
#define SENSOR_TYPE_TEMPERATURE
#define SENSOR_TYPE_PROXIMITY
#define SENSOR_TYPE_GRAVITY
#define SENSOR_TYPE_LINEAR_ACCELERATION 10
#define SENSOR_TYPE_ROTATION_VECTOR
#define SENSOR_TYPE_RELATIVE_HUMIDITY #define SENSOR_TYPE_AMBIENT_TEMPERATURE 13 |
n
struct sensors_module_t {
}; |
該接口的定義實際上是對標準的硬件模塊hw_module_t的一個擴展,增加了一個get_sensors_list函數,用於獲取傳感器的列表。
n
struct sensor_t {
}; |
n
typedef struct sensors_event_t {
} sensors_event_t; |
其中,sensor爲傳感器的標誌符,而不同的傳感器則採用union方式來表示,sensors_vec_t結構體用來表示不同傳感器的數據,
n
typedef struct {
} sensors_vec_t; |
n
struct sensors_poll_device_t { struct hw_device_t common; //Activate/deactivate one sensor
}; |
n
static inline int sensors_open(const struct hw_module_t* module,
}
static inline int sensors_close(struct sensors_poll_device_t* device) {
} |
3 Sensor HAL實現(以bma250爲例子)
3.1打開設備流程圖
SensorDevice屬於JNI層,與HAL進行通信的接口, 在JNI層調用了HAL層的open_sensors()方法打開設備模塊,再調用poll__activate()對設備使能,然後調用poll__poll讀取數據。
3.2 實現代碼分析
在bma250傳感器中,只有加速度傳感器,所以在sensor.cpp中,首先需要定義傳感器數組sSensorList,其實就是初始化struct sensor_t結構體,只有加速傳感器,初始化如下:
static const struct sensor_t sSensorList[] = {
}; |
n
static struct hw_module_methods_t sensors_module_methods = {
}; static int open_sensors(const struct hw_module_t* module, const char* name,
{
} |
在這個方法中,首先需要爲hw_device_t分配內存空間,並對其初始化,設置重要方法的實現,然後調用open_input_device打開設備節點,返回文件描述符。
n
static int poll__activate(struct sensors_poll_device_t *device,
} static int set_sysfs_input_attr(char *class_path,
{
} |
代碼很簡單,通過系統調用open方法打開設備,然後調用write()方法寫指令使能。
n
static int poll__poll(struct sensors_poll_device_t *device,
} |
通過read讀取設備數據,並存儲在sensors_event_t結構體中
(二)Framework原理
1 總體調用關係圖
Sensor 框架分爲三個層次,客戶度、服務端、HAL層,服務端負責從HAL讀取數據,並將數據寫到管道中,客戶端通過管道讀取服務端數據。下面簡單解釋類的功能
1.1客戶端主要類
n
從android4.1開始,把SensorManager定義爲一個抽象類,定義了一些主要的方法,類主要是應用層直接使用的類,提供給應用層的接口
n
繼承於SensorManager,客戶端消息處理的實體,應用程序通過獲取其實例,並註冊監聽接口,獲取sensor數據
n
用於註冊監聽的接口
n
是SystemSensorManager的一個內部類,開啓一個新線程負責讀取讀取sensor數據,當註冊了sensorEventListener接口的時候纔會啓動線程
n
負責與java層通信的JNI接口
n
sensor在Native層的客戶端,負責與服務端SensorService.cpp的通信
n
消息隊列
1.2服務端主要類
n
服務端數據處理中心
n
n
在這個類中創建了管道,即共享內存,用於服務端與客戶端讀寫數據
n
負責與HAL讀取數據
1.3 HAL層
2客戶端讀取數據
2.1 調用時序圖
2.2 代碼分析
2.2.1 apk註冊監聽器
SensorManager
public interface SensorEventListener {
}
|
沒有把全部代碼寫出來,Activity實現了SensorEventListener接口,在onCreate方法中,獲取SystemSensorManager,並獲取到加速傳感器的Sensor,在onResume方法中調用SystemSensorManager. registerListenerImpl註冊監聽器,當Sensor數據有改變的時候將會回調onSensorChanged方法。
2.2.2初始化SystemSensorManager
|
系統開機啓動的時候,會創建SystemSensorManager的實例,在其構造函數中,主要做了四件事情:
n
調用JNI函數nativeClassInit()進行初始化
n
調用JNI函數sensors_module_init,對Sensor模塊進行初始化。創建了native層SensorManager的實例。
n
調用JNI函數sensors_module_get_next_sensor()獲取Sensor,並存在sHandleToSensor列表中
n
構造線程的類函數,並沒有啓動線程,當有應用註冊的時候纔會啓動線程
2.2.3啓動SensorThread線程讀取消息隊列中數據
protected boolean registerListenerImpl(SensorEventListener listener, Sensor sensor,
|
當有應用程序調用registerListenerImpl()方法註冊監聽的時候,會調用SensorThread.startLoacked()啓動線程,線程只會啓動一次,並調用enableSensorLocked()接口對指定的sensor使能,並設置採樣時間。
SensorThreadRunnable實現了Runnable接口,在SensorThread類中被啓動
private class SensorThreadRunnable implements Runnable {
|
在open函數中調用JNI函數sensors_create_queue()來創建消息隊列,然後調用SensorManager. createEventQueue()創建。
在startLocked函數中啓動新的線程後,做了一個while的等待while (mSensorsReady == false),只有當mSensorsReady等於true的時候,纔會執行enableSensorLocked()函數對sensor使能。而mSensorsReady變量,是在open()調用創建消息隊列成功之後纔會true,所以我們認爲,三個功能調用順序是如下:
n
n
n
3 服務端實現
3.1 調用時序圖
3.2代碼分析
3.2.1啓動SensorService服務
在SystemServer進程中的main函數中,通過JNI調用,調用到
com_android_server_SystemServer.cpp的android_server_SystemServer_init1()方法,該方法又調用system_init.cpp中的system_init():
extern "C" status_t system_init() {
} |
在這裏創建了SensorService的實例。
3.2.2 SensorService初始化
SensorService創建完之後,將會調用SensorService::onFirstRef()方法,在該方法中完成初始化工作。
SensorDevice::SensorDevice()
{
} |
這裏主要做了三個工作:
n
n
n
再來看看SensorService::onFirstRef()方法:
void SensorService::onFirstRef() {
} |
在這個方法中,主要做了4件事情:
n
n
調用SensorDevice.getSensorList(),獲取Sensor模塊所有傳感器列表
n
registerSensor( new HardwareSensor(list[i]) );
void SensorService::registerSensor(SensorInterface* s) {
} |
n
調用run方法啓動新線程,將調用SensorService::threadLoop()方法。
3.2.3 在新的線程中讀取HAL層數據
SensorService實現了Thread類,當在onFirstRef中調用run方法的後,將在新的線程中調用SensorService::threadLoop()方法。
bool SensorService::threadLoop() {
} |
在while循環中一直讀取HAL層數據,再調用SensorEventConnection->sendEvents將數據寫到管道中。
4客戶端與服務端通信
4.1 數據傳送
客戶端與服務端通信的狀態圖:
這是我對數據傳輸的理解畫出的一張圖,可以更好的去理解數據是如何從服務端傳到客戶端的。
n
在圖中我們可以看到有兩個線程,一個是服務端的一個線程,這個線程負責源源不斷的從HAL讀取數據。另一個是客戶端的一個線程,客戶端線程負責從消息隊列中讀數據。
n
客戶端可以創建多個消息隊列,一個消息隊列對應有一個與服務器通信的連接接口
n
服務端與客戶端溝通的橋樑,服務端讀取到HAL層數據後,會掃面有多少個與客戶端連接的接口,然後往每個接口的管道中寫數據
n
每一個連接接口都有對應的一個管道。
上面是設計者設計數據傳送的原理,但是目前Android4.1上面的數據傳送不能完全按照上面的理解。因爲在實際使用中,消息隊列只會創建一個,也就是說客戶端與服務端之間的通信只有一個連接接口,只有一個管道傳數據。那麼數據的形式是怎麼從HAL層傳到JAVA層的呢?其實數據是以一個結構體sensors_event_t的形式從HAL層傳到JNI層。看看HAL的sensors_event_t結構體:
typedef struct sensors_event_t {
} sensors_event_t; |
在JNI層有一個ASensorEvent結構體與sensors_event_t向對應,
frameworks/native/include/android/sensor.h:
typedef struct ASensorEvent {
} ASensorEvent; |
在JNI層,只會將結構體數據中一部分的信息傳到JAVA層:
sensors_data_poll(JNIEnv *env, jclass clazz, jint nativeQueue,
{
} |
其實只是傳送了加速度傳感器其的數據event.vector.v其實是一個數組,裏面包含了x、y、z三軸的的加速度。
4.2調用時序圖
4.3 代碼分析
經過前面的介紹,我們知道了客戶端實現的方式及服務端的實現,但是沒有具體講到它兩是如何進行通信的,這節我們專門介紹客戶端與服務端之間的通信。
這裏主要涉及的是進程間通信,有IBind和管道通信。客戶端通過IBind通信獲取到服務端的遠程調用,然後通過管道進行sensor數據的傳輸。
管道是Linux
管道是半雙工的,數據只能向一個方向流動;需要雙方通信時,需要建立起兩個管道;只能用於父子進程或者兄弟進程之間(具有親緣關係的進程);單獨構成一種獨立的文件系統:管道對於管道兩端的進程而言,就是一個文件,但它不是普通的文件,它不屬於某種文件系統,而是自立門戶,單獨構成一種文件系統,並且只存在與內存中。數據的讀出和寫入:一個進程向管道中寫的內容被管道另一端的進程讀出。寫入的內容每次都添加在管道緩衝區的末尾,並且每次都是從緩衝區的頭部讀出數據。管道兩端可分別用描述字fd[0]以及fd[1]來描述,需要注意的是,管道的兩端是固定了任務的。即一端只能用於讀,由描述字fd[0]表示,稱其爲管道讀端;另一端則只能用於寫,由描述字fd[1]來表示,稱其爲管道寫端。如果試圖從管道寫端讀取數據,或者向管道讀端寫入數據都將導致錯誤發生。一般文件的I/O函數都可以用於管道,如close、read、write等等。
4.3.1 服務端
native層實現了sensor服務的核心實現,Sensor服務的主要流程的實現在sensorservice類中,下面重點分析下這個類的流程。
class SensorService :
|
看看sensorService繼承的類:
n
template<typename SERVICE> class BinderService { public:
}; }; // namespace android |
在前面的介紹中,SensorService服務的實例是在System_init.cpp中調用SensorService::instantiate()創建的,即調用了上面的instantiate()方法,接着調用了publish(),在該方法中,我們看到了new SensorService的實例,並且調用了defaultServiceManager::addService()將Sensor服務添加到了系統服務管理中,客戶端可以通過defaultServiceManager:getService()獲取到Sensor服務的實例。
n
class Sensor; class ISensorEventConnection;
class ISensorServer : public IInterface { public:
virtual Vector<Sensor> getSensorList() = 0; //創建一個連接的接口,這些都是提供給客戶端的抽象接口,服務端實例化時候必須實現
}; class BnSensorServer : public BnInterface<ISensorServer> { public:
}; }; // namespace android |
ISensorServer接口提供了兩個抽象方法給客戶端調用,關鍵在於
createSensorEventConnect
4.3.2客戶端
客戶端主要在SensorManager.cpp中創建消息隊列
class ISensorEventConnection; class Sensor; class Looper;
// ----------------------------------------------------------------------------
class SensorEventQueue : public ASensorEventQueue, public RefBase { public:
private: sp<Looper> getLooper() const; //連接接口,在SensorService中創建的 sp<ISensorEventConnection> mSensorEventConnection; //管道指針
}; |
SensorEventQueue類作爲消息隊列,作用非常重要,在創建其實例的時候,傳入了SensorEventConnection的實例,SensorEventConnection繼承於ISensorEventConnection。SensorEventConnection其實是客戶端調用SensorService的createSensorEventConnect
n
n
n
4.4流程解析
4.4.1 客戶端獲取SensorService服務實例
客戶端初始化的時候,即SystemSensorManager的構造函數中,通過JNI調用,創建native層SensorManager的實例,然後調用SensorManager::assertStateLocked()方法做一些初始化的動作。
status_t SensorManager::assertStateLocked() const {
} |
前面我們講到過,SensorService的創建的時候調用了defaultServiceManager:getService()將服務添加到了系統服務管理中。現在我們又調用defaultServiceManager::geService()獲取到SensorService服務的實例。在通過IBind通信,就可以獲取到Sensor列表,所以在客戶端初始化的時候,做了兩件事情:
n
n
4.4.2創建消息隊列
當客戶端第一次註冊監聽器的時候,就需要創建一個消息隊列,也就是說,android在目前的實現中,只創建了一個消息隊列,一個消息隊列中有一個管道,用於服務端與客戶斷傳送Sensor數據。
在SensorManager.cpp中的createEventQueue方法創建消息隊列:
sp<SensorEventQueue> SensorManager::createEventQueue() {
while (assertStateLocked() == NO_ERROR) {
//創建消息隊列
} |
客戶端與服務器創建一個SensorEventConnection連接接口,而一個消息隊列中包含一個連接接口。
創建連接接口:
sp<ISensorEventConnection> SensorService::createSensorEventConnect {
} SensorService::SensorEventConnection::SensorEventConnection(
{ } |
關鍵在於BitTube,在構造函數中創建了管道:
BitTube::BitTube()
{
} |
其中:fds[0]就是對應的mReceiveFd,是管道的讀端,sensor數據的讀取端,對應的是客戶端進程訪問的。fds[1]就是對應mSendFd,是管道的寫端,sensor數據寫入端,是sensor的服務進程訪問的一端。通過pipe(fds)創建管道,通過fcntl來設置操作管道的方式,設置通道兩端的操作方式爲O_NONBLOCK ,非阻塞IO方式,read或write調用返回-1和EAGAIN錯誤。
總結下消息隊列:
客戶端第一次註冊監聽器的時候,就需要創建一個消息隊列,客戶端創了SensorThread線程從消息隊列裏面讀取數據。
SensorEventQueue中有一個SensorEventConnection實例的引用,SensorEventConnection中有一個BitTube實例的引用。
4.4.3使能Sensor
客戶端創建了連接接口SensorEventConnection後,可以調用其方法使能Sensor傳感器:
status_t SensorService::SensorEventConnection::enableDisable(
{
} |
handle對應着Sensor傳感器的句柄
4.4.4 服務端往管道寫數據
bool SensorService::threadLoop() {
} |
前面介紹過,在SensorService中,創建了一個線程不斷從HAL層讀取Sensor數據,就是在threadLoop方法中。關鍵在與下面了一個for循環,其實是掃描有多少個客戶端連接接口,然後就往沒每個連接的管道中寫數據。
status_t SensorService::SensorEventConnection::sendEvents(
{
} |
調用該連接接口的BitTube::write():
ssize_t BitTube::write(void const* vaddr, size_t size) {
} } |
到此,服務端就完成了往管道的寫端寫入數據。
4.4.5 客戶端讀管道數據
ssize_t SensorEventQueue::read(ASensorEvent* events, size_t numEvents) {
} |
調用到了BitTube::read():
static ssize_t recvObjects(const sp<BitTube>& tube,
ssize_t BitTube::recvObjects(const sp<BitTube>& tube,
{
}
ssize_t BitTube::read(void* vaddr, size_t size) {
} |