转自: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) { } |