我是靠谱客的博主 顺心金毛,最近开发中收集的这篇文章主要介绍Android6.0 Sensor架构和问题分析,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

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

本文主要描述了在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模块架构和实现。


最后

以上就是顺心金毛为你收集整理的Android6.0 Sensor架构和问题分析的全部内容,希望文章能够帮你解决Android6.0 Sensor架构和问题分析所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(60)

评论列表共有 0 条评论

立即
投稿
返回
顶部