android sensor 框架分析---sensor資料流分析
5,sensor資料流分析
前面幾章做了很多準備和鋪墊,這章終於可以分析sensor資料的傳輸流程了。主要步驟如下,
1,服務端通過HAL從驅動檔案節點中獲取sensor資料。
2,服務端通過管道傳送資料。
3,客戶端通過管道讀取資料。
4,客戶端吐出資料。
5.1服務端獲取資料
啟動sensor服務之後,就會呼叫SensorService.cpp的threadLoop方法,該方法首先不斷呼叫SensorDevice.cpp的poll方法獲取sensor資料,
do { ssize_t count = device.poll(mSensorEventBuffer, numEventMax);
然後遍歷activeConnections變數,向每一個客戶端傳送一份sensor資料。
for (size_t i=0 ; i < numConnections; ++i) {
if (activeConnections[i] != 0) {
activeConnections[i]->sendEvents(mSensorEventBuffer, count, mSensorEventScratch,
mMapFlushEventsToConnections);
poll方法呼叫流程圖如下,
這些方法都很簡單,沒什麼可論述的。加速度sensor的readEvents呼叫流程圖如下,
1,fill方法
Accelerometer.cpp 的readEvents中呼叫fill方法如下,
ssize_t n = mInputReader.fill(data_fd);
傳入的data_fd是NativeSensorManager.h的SensorContext結構體的變數。見第二章2.1.2 小節。
fill方法如下,
ssize_t InputEventCircularReader::fill(int fd) { size_t numEventsRead = 0; if (mFreeSpace) { const ssize_t nread = read(fd, mHead, mFreeSpace * sizeof(input_event)); if (nread<0 || nread % sizeof(input_event)) { // we got a partial event!! return nread<0 ? -errno : -EINVAL; } numEventsRead = nread / sizeof(input_event); if (numEventsRead) { mHead += numEventsRead; mFreeSpace -= numEventsRead; if (mHead > mBufferEnd) { size_t s = mHead - mBufferEnd; memcpy(mBuffer, mBufferEnd, s * sizeof(input_event)); mHead = mBuffer + s; } } } return numEventsRead; }
struct input_event* const mBuffer;
struct input_event* const mBufferEnd;
struct input_event* mHead;
struct input_event* mCurr;
ssize_t mFreeSpace;
mBuffer表示未讀事件;
mHead表示未讀事件的第一個,初始為緩衝區首部.
mBufferEnd表示未讀事件的最後一個,初始為緩衝區尾部.
mCurr表示當前未讀事件
struct input_event結構體定義在kernel/include/uapi/linux/input.h中,
struct input_event {
struct timeval time;
__u16 type;//型別,比如sensor,按鍵事件等
__u16 code;
__s32 value;//具體的數值
};
2,獲取sensor值,
while (count && mInputReader.readEvent(&event)) {
int type = event->type;
if (type == EV_ABS) {
float value = event->value;
if (event->code == EVENT_TYPE_ACCEL_X) {
mPendingEvent.data[0] = value * CONVERT_ACCEL_X;
} else if (event->code == EVENT_TYPE_ACCEL_Y) {
mPendingEvent.data[1] = value * CONVERT_ACCEL_Y;
} else if (event->code == EVENT_TYPE_ACCEL_Z) {
mPendingEvent.data[2] = value * CONVERT_ACCEL_Z;
}
} else if (type == EV_SYN) {
switch (event->code){
case SYN_TIME_SEC:
{
mUseAbsTimeStamp = true;
report_time = event->value*1000000000LL;
}
break;
case SYN_TIME_NSEC:
{
mUseAbsTimeStamp = true;
mPendingEvent.timestamp = report_time+event->value;
}
break;
•••
mInputReader.next();
首先獲取加速度sensor三個方向上的值,然後獲取對應的時間。
當然,時間有2部分組成,最後轉化為ns相加。
InputEventReader.cpp的readEvent方法如下,
ssize_t InputEventCircularReader::readEvent(input_event const** events)
{
*events = mCurr;
ssize_t available = (mBufferEnd - mBuffer) - mFreeSpace;
return available ? 1 : 0;
}
3,讀取下一個sensor的值
InputEventReader.cpp的next方法如下,
void InputEventCircularReader::next()
{
mCurr++;
mFreeSpace++;
if (mCurr >= mBufferEnd) {
mCurr = mBuffer;
}
}
這樣,通過InputEventReader.cpp讀取sensor裝置節點的值。
5.2服務端傳送資料
Sensor服務端每次讀取資料之後,當然,一次讀取一組sensor資料,這一組sensor資料可能有幾個sensor值,也可能一個都沒有。
for (size_t i=0 ; i < numConnections; ++i) {
if (activeConnections[i] != 0) {
activeConnections[i]->sendEvents(mSensorEventBuffer, count, mSensorEventScratch,
mMapFlushEventsToConnections);
呼叫SensorEventConnection物件的sendEvents方法將資料傳送給客戶端。傳送流程圖如下,
最後的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);
// cannot return less than size, since we're using SOCK_SEQPACKET
err = len < 0 ? errno : 0;
} while (err == EINTR);
return err == 0 ? len : -err;
}
利用mSendFd將資料寫入管道。
注意, mSensorChannel就是指BitTube.cpp物件,在服務端和客戶端共用一個。
5.3客服端讀取資料
android_hardware_SensorManager.cpp內部類Receiver的handleEvent首先會讀取服務端寫入管道的資料,然後傳送給Java上層。
讀取程式碼如下,
while ((n = q->read(buffer, 16)) > 0) {
read方法的呼叫流程圖如下,
BitTube.cpp的read方法如下,
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) {
// EAGAIN means that we have non-blocking I/O but there was
// no data to be read. Nothing the client should care about.
return 0;
}
return err == 0 ? len : -err;
}
使用mReceiveFd從管道中讀取服務端傳送過來的資料。
5.4客戶端傳送資料
handleEvent方法獲取資料之後, 回撥Java層SystemSensorManager的內部類SensorEventQueue的dispatchSensorEvent方法。
env->CallVoidMethod(receiverObj.get(),gBaseEventQueueClassInfo.dispatchSensorEvent,
buffer[i].sensor, mScratch, status, buffer[i].timestamp);
最後呼叫註冊的onSensorChanged吐出資料。
小結:
經過了前面那麼多的鋪墊,終於論述了資料從服務端到客戶端客戶端的過程。