您的位置:首页 > 运维架构 > 网站架构

Android6.0 Sensor架构和问题分析

2016-09-03 11:07 274 查看
本文在借鉴网友的资料后再重新梳理了一遍,都是站在前人的基础、巨人的肩膀上再次总结分析出来的,仅供大家参考!

本文主要描述了在Android 6.0系统、MTK6755平台上sensor相关软硬件的体系架构的分析理解。

一、系统架构(Architecture)

1.1 Android体系架构图

 


图1.1

图1.1是Android系统的典型五层体系架构图,分别为Applications、Framework、Native、HAL和Kernel五大层次。我们接下来分析的sensor子系统也基本按照这五个层次分别进行分析。

1.2  Sensor子系统架构图

 


图1.2

1、黄色部分表示硬件,它要挂在I2C总线上(目前大部分sensor基本都通过I2C进行通信和控制)。

2、红色部分表示驱动,驱动中会申请Input设备,注册到Kernel的Input Subsystem上,然后通过Event Device把Sensor数据传到HAL层,准确说是HAL从Event读。

    在MTK6755平台中,这里的驱动driver实际上分成了两层,一层为虚拟逻辑Hub层,用来管理兼容不同类型的所有的sensor,如有可能有两家厂商的不同g-sensor,则可以通过这个虚拟逻辑出来的Hub进行统一管理;另一层为每款具体sensor的真正驱动,通过注册挂载到Hub中。如图1.3:

 


图1.3

从图1.3可以看出,MTK平台对虚拟逻辑Hub层使用了两种实现方式,一种是以前平台一直使用的hwmsen虚拟逻辑Hub,由它统一管理所有类型所有厂商的sensor,目前我司没有使用这种方式,因此在后面就不对它进行分析了;另一种是分别为每一种类型的sensor实现了一个虚拟逻辑Hub,如accel负责管理所有G-sensor。

3、图1.2绿色部分表示动态库,它封装了整个Sensor的IPC机制,如SensorManager是客户端,SensorService是服务端,而HAL部分是封装了服务端对Kernel的直接访问。在MTK平台的HAL层里,也同样为对接下层的两种虚拟逻辑Hub实现了不同的HAL层代码。

4、蓝色部分就是我们的Framework和Application了,JNI负责访问Sensor的客户端,而Application就是具体的应用程序,用来接收Sensor返回的数据,并处理实现对应的UI效果,如屏幕旋转,打电话时灭屏,自动调节背光等。

二、应用(Applications)

2.1 传感器介绍

    Android 6.0 系统sensors.h文件中定义了26种传感器类型,其中主要的8种传感器类型分别是:加速度传感器(accelerometer)、磁力传感器(magnetic field)、方向传感器(orientation)、陀螺仪(gyroscope)、环境光照传感器(light)、压力传感器(pressure)、温度传感器(temperature)和距离传感器(proximity)。其他的几种传感器基本都可以由上述的传感器进行算法实现,如动作探测传感器(significant motion)、步行探测传感器(step
detector)、计步传感器(step counter)、心率计传感器(heart rate)等。

2.2 应用开发

    Android定义的API接口给应用层提供了几种方法,可以很容易让你在运行时确定设备上有哪些sensor。API还提供了让你确定每个sensor信息和功能的接口,比如sensor名字、类型、厂商、数据值的范围、数据精度等。

    Android开发者官网上给出的典型的应用开发代码如下:

 


图2.1

在构造函数中,获取SystemSensorManager,并获取到加速传感器的Sensor;在onResume方法中调用SystemSensorManager.registerListener注册监听器;

Activity同时实现了SensorEventListener接口。当Sensor数据有改变的时候将会调用onSensorChanged方法。

 


图2.2

三、框架(Framework)

3.1 Sensor总体调用关系图

 


图3.1

sensor框架分为三个层次,客户端、服务端、HAL层,服务端负责从HAL读取数据,并将数据写到管道中,客户端通过管道读取服务端数据。

客户端主要类

SensorManager.java

    从android4.1开始,把SensorManager定义为一个抽象类,定义了一些主要的方法,该类主要是应用层直接使用的类,提供给应用层的接口

SystemSensorManager.java

    继承于SensorManager,客户端消息处理的实体,应用程序通过获取其实例,并注册监听接口,获取sensor数据。

SensorEventListener接口

    用于注册监听的接口

Looper

    用申请注册的线程Looper或系统初始化线程的Looper,来负责读取sensor数据

android_hardware_SensorManager.cpp

    负责与java层通信的JNI接口

SensorManager.cpp

    sensor在Native层的客户端,负责与服务端SensorService.cpp的通信

SenorEventQueue.cpp

    消息队列

服务端主要类

SensorService.cpp

    服务端数据处理中心

SensorEventConnection

    从BnSensorEventConnection继承来,实现接口ISensorEventConnection的一些方法,ISensorEventConnection在SensorEventQueue会保存一个指针,指向调用服务接口创建的SensorEventConnection对象

BitTube.cpp

    在这个类中创建了管道,用于服务端与客户端读写数据

SensorDevice

    负责与HAL读取数据

HAL层

  sensor.h是google为Sensor定义的Hal接口,单独提出去

3.2客户端实现

调用时序图

 


图3.2

初始化SystemSensorManager

 


图3.3

系统开机启动的时候,会创建SystemSensorManager的实例,在其构造函数中,主要做了三件事情:

1、初始化Sensor列表:调用JNI函数nativeCreate,对sensor模块进行初始化。创建了native层SensorManager的实例。

2、初始化JNI:调用JNI函数nativeClassInit()进行初始化

3、获取Sensor列表:调用JNI函数nativeGetSensorAtIndex获取sensor,并存在mFullSensorsList和mHandleToSensor列表中

实现注册监听器

 


图3.4

当有应用程序调用registerListenerImpl()方法注册监听的时候,会用注册线程的Looper或系统初始化线程的Looper来循环等待SensorEvent事件并读取来自服务端的数据。

    接着实例化内部类SensorEventQueue,通过调用JNI函数nativeBaseEventQueue()来创建消息队列,JNI最终调用Native SensorManager来完成真正的创建消息队列。

    然后通过实例化的SensorEventQueue添加需监听的sensor并对其使能和设置采样事件等。

3.3 服务端实现

调用时序图

 


图3.5

启动SensorService服务

    在SystemServer进程中调用native 方法startSensorService()函数,通过JNI调用,在JNI函数中调用SensorService::instantiate()实例化SensorService对象。

SensorService初始化

SensorService创建完之后,将会调用SensorService::onFirstRef()方法,在该方法中完成初始化工作。

首先获取SensorDevice实例,在其构造函数中,完成了对Sensor模块HAL的初始化:

 


图3.6

这里主要做了三个工作:

1、调用HAL层的hw_get_modele()方法,加载Sensor模块so文件

2、调用sensor.h的sensors_open_1()方法打开设备

3、调用sensors_poll_device_1_t->activate()对Sensor模块使能

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

 


图3.7

在这个方法的前面部分中,主要做了3件事情:

1、创建SensorDevice实例

2、调用SensorDevice.getSensorList(),获取Sensor模块所有传感器列表

3、为每个传感器注册监听器,Android还同时注册实现了6个虚拟传感器

 


图3.8

注意!:如果O-sensor需要使用厂商自己的算法,需如上图添加宏控制过滤,不然会默认使用Android自带的算法计算O-sensor的值,这样有可能会导致数据不准确。

在新的线程中读取HAL层数据

SensorService实现了Thread类,当在onFirstRef中最后调用run方法,将在新的线程中调用SensorService::threadLoop()方法。在该方法while循环中一直poll读取HAL层数据,再调用SensorEventConnection->sendEvents将数据写到管道中。

3.4 客户端与服务端通信

 


图3.9

客户端服务端线程

    在图中可以看到有两个线程:

        1、一个是服务端的一个线程,这个线程负责源源不断的从HAL读取数据。

        2、另一个是客户端的一个线程,客户端线程负责从消息队列中读数据。

创建消息队列

    客户端可以创建多个消息队列,一个消息队列对应有一个与服务器通信的连接接口

创建连接接口

    服务端与客户端沟通的桥梁,服务端读取到HAL层数据后,会扫面有多少个与客户端连接的接口,然后往每个接口的管道中写数据

创建管道

    每一个连接接口都有对应的一个管道。

那么数据的形式是怎么从HAL层传到JAVA层的呢?

    其实数据是以一个结构体sensors_event_t的形式从HAL层传到JNI层。看HAL的结构体:

 


图3.10

在JNI层有一个ASensorEvent结构体与sensors_event_t向对应,

 


图3.11

    经过前面的介绍,现在知道了客户端实现的方式及服务端的实现,但是没有具体讲到它们是如何进行通信的,现在看看客户端与服务端之间的通信。

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

  客户端通过IBind通信获取到服务端的远程调用,然后通过管道进行sensor数据的传输。

服务端

  native层实现了sensor服务的核心实现,Sensor服务的主要流程的实现在sensorservice类中,下面重点分析下这个类的流程。

 


图3.12

看看sensorService继承的类:继承BinderService<SensorService>这个模板类添加到系统服务,用于Ibinder进程间通信。

 


图3.13

SensorService服务的实例是在JNI函数中调用SensorService::instantiate()创建的,即调用了上面的instantiate()方法,接着调用了publish().在该方法中,我们看到了new SensorService的实例,并且调用了defaultServiceManager::addService()将Sensor服务添加到了系统服务管理中,客户端可以通过defaultServiceManager:getService()获取到Sensor服务的实例。

    继承BnSensorServer这个是sensor服务抽象接口类提供给客户端调用:

 


图3.14

ISensorServer接口提供了两个抽象方法给客户端调用,关键在于createSensorEventConnection()方法,该在服务端被实现,在客户端被调用,并返回一个SensorEventConnection的实例,创建连接,客户端拿到SensorEventConnection实例之后,可以对sensor进行通信操作,仅仅作为通信的接口而已,它并没有用来传送Sensor数据,因为Sensor数据量比较大,IBind实现比较困难。真正实现Sensor数据传送的是管道,在创建SensorEventConnection实例中,创建了BitTube对象,里面创建了管道,用于客户端与服务端的通信。

客户端

    客户端主要在SensorManager.cpp中创建消息队列

 


图3.15

SensorEventQueue类作为消息队列,作用非常重要,在创建其实例的时候,传入了SensorEventConnection的实例,SensorEventConnection继承于ISensorEventConnection。

  SensorEventConnection其实是客户端调用SensorService的createSensorEventConnection()方法创建的,它是客户端与服务端沟通的桥梁,通过这个桥梁,可以完成一下任务:

1.    获取管道的句柄

2.    往管道读写数据

3.    通知服务端对Sensor使能

3.5 流程解析

交互调用时序图

 


图3.16

客户端获取SensorService服务实例

  客户端初始化的时候,即SystemSensorManager的构造函数中,通过JNI调用,创建native层SensorManager的实例,然后调用SensorManager::assertStateLocked()方法做一些初始化的动作。

 


图3.17

前面我们讲到过,SensorService的创建的时候调用了defaultServiceManager:getService()将服务添加到了系统服务管理中。

  现在我们又调用defaultServiceManager::geService()获取到SensorService服务的实例。

  在通过IBind通信,就可以获取到Sensor列表,所以在客户端初始化的时候,做了两件事情:

    1. 获取SensorService实例引用

    2. 获取Sensor传感器列表

创建消息队列

    当客户端第一次注册监听器的时候,就需要创建一个消息队列,也就是说,android在目前的实现中,一个监听器对应一个消息队列,一个消息队列中有一个管道,用于服务端与客户端传送Sensor数据。

  在SensorManager.cpp中的createEventQueue方法创建消息队列:

 


图3.18

客户端与服务端创建一个SensorEventConnection连接接口,而一个消息队列中包含一个连接接口。

创建连接接口:

 


 


图3.19

关键在于BitTube,在构造函数中创建了管道:

 


 


图3.20

其中:sockets[0]就是对应的mReceiveFd,是管道的读端,sensor数据的读取端,对应的是客户端进程访问的。

  sockets[1]就是对应mSendFd,是管道的写端,sensor数据写入端,是sensor的服务进程访问的一端。

  通过socketpair()创建管道,通过fcntl来设置操作管道的方式,设置通道两端的操作方式为O_NONBLOCK,非阻塞IO方式,read或write调用返回-1和EAGAIN错误。

总结下消息队列

  客户端第一次注册监听器的时候,就需要创建一个消息队列,客户端通过Looper从消息队列里面读取数据。

  SensorEventQueue中有一个SensorEventConnection实例的引用,SensorEventConnection中有一个BitTube实例的引用。

使能Sensor

  客户端创建了连接接口SensorEventConnection后,可以调用其方法使能Sensor传感器:

 


图3.21

服务端往管道写数据

前面介绍过,在SensorService中,创建了一个线程不断从HAL层读取Sensor数据,就是在threadLoop方法中。关键在于下面的一个for循环,其实是扫描有多少个客户端连接接口,然后就往每个连接的管道中写数据。

 


 


图3.22

调用了SensorEventQueue消息队列的write()方法

 


图3.23

调用该连接接口的BitTube::sendObjects()和BitTube::write():

 


 


图3.24

到此,服务端就完成了往管道的写端写入数据。

客户端读管道数据

服务端写完数据后,客户端的Looper收到有数据可读事件后,调用JNI中的回调函数从消息队列读取数据。

 


图3.25

然后调用到了BitTube::read():

 


 


图3.26

读取到sensor数据后,通过JNI调回JAVA层的SystemSensorManager类dispatchSensorEvent()方法:

 


 


图3.27

到此,客户端就完成了从管道的读端读入数据,并把数据发给对应监听器的应用。

四、硬件抽象层(HAL)

4.1 基本框架图

 


图4.1

    MTK平台的HAL层和Kernel层,跟高通平台的很不一样。之前说过,MTK平台使用两套机制来实现与sensor的管理和通信。一种是通过hwmsen来管理控制所有sensor,并通过ioctl接口来操作底层sensor和获取数据;另一种是通过为每一种类型的sensor分配一个对象来管理各自类型的sensor,并通过/sys节点来操作底层sensor,通过Input子系统和Event设备来获取sensor数据。我司使用的是第二种方式,因此暂不对第一种方式进行分析。

4.2 基本定义

    在linux操作系统中,应用同硬件之间的交互都是通过设备驱动来实现,Android系统为了降低应用开发人员开发难度,屏蔽硬件差异,定义出硬件抽象层,为开发人员提供获取各种设备相关的信息的接口。

    Google为Sensor提供了统一的HAL接口,不同的硬件厂商需要根据该接口来实现并完成具体的硬件抽象层。Android中Sensor的HAL接口定义在:hardware/libhardware/include/hardware/sensors.h

Android6.0定义了26中传感器类型:

 


图4.2

传感器模块的定义结构体如下:

 


图4.3

对任意一个sensor设备都会有一个sensor_t结构体,其定义如下:

 


图4.4

每个传感器的数据由sensors_event_t结构体表示,定义如下:

 


图4.5

其中,sensor为传感器的标志符,而不同的传感器则采用union方式来表示。

sensors_vec_t结构体用来表示不同传感器的数据:

 


图4.6

Sensor设备结构体sensors_poll_device_1_t,对标准硬件设备hw_device_t结构体的扩展,主要完成读取底层数据,并将数据存储在struct sensors_poll_device_t结构体中;

poll函数用来获取底层数据,调用时将被阻塞。定义如下:

 


图4.7

其中batch()、flush()、inject_sensor_data()等方法,一般在外挂sensorhub或带有MCU、memory的器件上时才使用,因我司没有使用这些器件,因此本文讨论的sensor架构都不涉及该相关实现。

控制设备打开/关闭结构体定义如下:

 


图4.8

4.2 Sensor HAL实现

Native和HAL交互调用时序图

 


图4.9

SensorDevice属于JNI层,是与HAL进行通信的接口;在JNI层调用了HAL层的open_sensors()方法打开设备模块,再调用poll__activate()对设备使能,然后调用poll__poll读取数据。

创建SensorDevice

在SensorDevice的构造函数中,首先加载sensor设备模块:

 


图4.10

这句代码的意思是JNI加载HAL的库文件,并创建SensorModle的对象,Sensor的库文件通常是sensor.default.so

上图接下来是sensors_open_1(),这个函数并没有在SensorDevice中实现,而是调用的HAL层的函数。

 


 


图4.11

我们先看new sensors_poll_device_t();

 


图4.12

这部分代码就创建HAL和Kernel Event通信的类,还有Sensor数据读写管道的创建。

返回open_sensors再看剩下的代码,就是创建sensors_poll_device_t对象并把sensor控制的相关函数指针赋值给它。

SensorDevice 调用get_sensors_list

这个方法还是调用到了HAL中,而HAL中的这个函数也就是返回以下数组:

 


图4.13

我们需要特别关组的是第4,5个参数,第4参数handle是对kernel而言的,如激活,读写event;而第五个参数是相对于上层代码而言。

SensorDevice的activate方法

在该方法中,会先调用HAL的activate方法,在调用HAL的setDelay方法。

 


图4.14

传进来的就是我们上面说的第4个参数handle,handleToDriver()函数返回的是对应的和kernel交互的类的数组下标(Sensors[acc])。从上图可以看出MTK平台就是这样兼容操作底层sensor的两种实现方式的。

最终通过读写/sys节点来操作底层的sensor设备。

 


图4.15

SensorDevice的poll方法

这个方法同样调用到了HAL中poll__poll()函数,该函数再调用到pollEvents()函数来轮询获取所有sensor数据。

 


图4.16

可以看到,最终会通过对应sensor和kernel交互的类来获取底层的数据,并把数据以sensors_event_t的格式存储在上层传递下来的内存区里,然后返回数量值。

 


图4.17

HAL和kernel通过Input子系统和Event设备来完成数据的传送。

至此,HAL层的部分就分析到这里。Kernel的部分就不再进行详细分析,具体可以阅读MTK文档sensor_all_in_one.pdf来了解MTK平台底层的sensor模块架构和实现。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android sensor架构