Sensor框架理解
在这个系列的文章我们只是为了讲清楚Sensor框架的设计和工作原理基于4.0,4.0以下的代码有所区别,尤其是2.2以下根本就没有Binder架构,不讲驱动,也不讲具体的某一个应用该怎么处理Sensor的数据。
一、整体的架构:
从这个图来看Sensor的架构还是非常的清淅,
黄色部分表示硬件,它要挂在I2C总线上
红色部分表示驱动,把驱动注册到Kernel的Input Subsystem上,然后通过Event Device把Sensor数据传到HAL层,准确说是HAL从Event读
绿色部分表示动态库,它封装了整个Sensor的IPC机制,如SensorManager是客户端,SensorService是服务端,而HAL部分是封装了服务端对Kernel的直接访问
蓝色部分就是我们的Framework和Application了,JNI负责访问Sensor的客户端,而Application就是具体的应用程序,用来接收Sensor返回的数据,并处理实现对应的UI效果,如屏幕旋转,打电话时灭屏,自动调接背光(这三个功能的具体实现会在以后分析)
相关代码:
从HAL到Framework:
Framework部分:
frameworks/base/core/java/android/hardware/SensorManager.java
frameworks/base/core/jni/android_hardware_sensorManager.cpp
下面的代码会生成到:libgui.so
frameworks/base/libs/gui/SensorManager.cpp
frameworks/base/libs/gui/SensorEventQueue.cpp
frameworks/base/libs/gui/SensorChannel.cpp
frameworks/base/libs/gui/Sensor.cpp
下面的代码会生成:libsensorservice.so
frameworks/base/services/sensorservice/SensorService.cpp
frameworks/base/services/sensorservice/SensorDevice.cpp
HAL部分:这部分代码最终会生成 sensor.default.so 到/system/lib/hw/
hardware/libhardware/include/hardware/Sensors.h
device/qcom/msm7627a/libsensors/Sensors.cpp
device/qcom/msm7627a/libsensors/SensorBase.h
device/qcom/msm7627a/libsensors/AccSensor.cpp
device/qcom/msm7627a/libsensors/ProximitySensor.cpp
device/qcom/msm7627a/libsensors/LightSensor.cpp
device/qcom/msm7627a/libsensors/TmdSensor.cpp
device/qcom/msm7627a/libsensors/MagnetoSensor.cpp
device/qcom/msm7627a/libsensors/GyroSensor.cpp
device/qcom/msm7627a/libsensors/InputEventRead.h
device/qcom/msm7627a/libsensors/InputEventRead.cpp
Drivers:
P-Sensor:
device/qcom/msm7627a/libsensors/Tmd2771.h
kernel/drivers/misc/Tmd2771.c
(从这个代码路径大家可以看出我用来分析的代码是高通7627a平台的,
和Google原生代码没什么差别,而MTK的代码差别就大了,从HAL层开始完全不一样。)
我们还是列一下Android一般有哪些Sensor吧!
AccelerometerSensor
MagneticSensor
OrientationSensor
ProximitySensor
LightSensor
Gyro
这是我们最常见手机上有的Sensor,不过一般低端手机是没有Gyro的,而A Sensor用的并不是三轴的而是两轴。
二、应用举例:
- SensorManager sensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
- Sensor accSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
- sensorManager.registerListener(this, accSensor, SensorManager.SENSOR_DELAY_NORMAL);
- sensorManager.unregisterListener(this, accSensor);
- //然后在当前Activity中实现以下的两个函数
- public void onSensorChanged(SensorEvent event)
- public void onAccuracyChanged(Sensor sensor, int accuracy)
三、SensorService
服务程序启动,它是由SystemManager启动起来的:
frameworks/base/cmds/system_server/library/system_init.cpp
- property_get("system_init.startsensorservice", propBuf, "1");
- if (strcmp(propBuf, "1") == 0) {
- // Start the sensor service
- SensorService::instantiate();
- }
整个C/S通信的架构图:
需要特别说明的是,BpSensorServer并没有在系统中被用到,如果你从ISensorServer.cpp中把它删除也不会对Sensor的工作有任何影响。
它的工作被SensorManager.cpp所取代,ServiceManager直接获取上面System_init文件中添加的SensorService对像。
四、创建SensorManager
1. new SensorManager
它有两个地方去创建这个Sensor client Object,一个就是ContextImpl,另一个就是PowerManagerService中,contextImpl大家都明白是为了应用程序很方便的获取Service,PowerManager中为什么要创建这个对象我们后面再分析。
2.natvieClassInit
它在SensorManager(JAVA)的构造函数中被调用,作用就是创建一个Sensor.java类的实例对象。
3.sensors_module_init()
它也是在SensorManager(JAVA)的构造函数中被调用的,它的作用就是初始华SensorManager(cpp)。
- static jint
- sensors_module_init(JNIEnv *env, jclass clazz)
- {
- SensorManager::getInstance();
- return 0;
- }
通过getInstance()就可以知道它是一个单件类,实例的创建由其父类Singleton<SensorManager>,
SensorManager只需要在实现文件调用以下的代码:
ANDROID_SINGLETON_STATIC_INSTANCE(SensorManager)
接着是SensorManager(cpp)的构造函数也没有做什么就是通过ServiceManager获取了SensorService的实例对象。
4. onFirstRef的实例
其实SensorService在添加实例到ServiceManager的时候就已经实例化过后了,因为在Binder.c中就会保存对它的引用,而RefBase的意思就是用来管理对像的引用,所以它会在对象第一次被引用的时候就调用onFirstRef。
接下来我们看看SensorService::onFirstRef里面做了哪些工作。
5. 创建SensorDevice
SensorDevice的构造函数:
- status_t err = hw_get_module(SENSORS_HARDWARE_MODULE_ID,
- (hw_module_t const**)&mSensorModule);
这句话的意思是JNI加载HAL的库文件,并创建SensorModle的对象,Sensor的库文件通常是sensor.default.so
上图接下来是sensors_open,这个函数并没有在SensorDevice中实现,而是调用的HAL层的函数,相关代码路径已在上面列出。
- static int open_sensors(const struct hw_module_t* module, const char* id,
- struct hw_device_t** device)
- {
- int status = -EINVAL;
- LOGE("%s %d => %s", __FILE__, __LINE__, __func__);
- sensors_poll_context_t *dev = new sensors_poll_context_t();
- memset(&dev->device, 0, sizeof(sensors_poll_device_t));
- dev->device.common.tag = HARDWARE_DEVICE_TAG;
- dev->device.common.version = 0;
- dev->device.common.module = const_cast<hw_module_t*>(module);
- dev->device.common.close = poll__close;
- dev->device.activate = poll__activate;
- dev->device.setDelay = poll__setDelay;
- dev->device.poll = poll__poll;
- *device = &dev->device.common;
- status = 0;
- return status;
- }
我们看new sensors_poll_device_t();
- sensors_poll_context_t::sensors_poll_context_t()
- {
- #ifdef TMD27713_SENSOR
- mSensors[tmd] = new TmdSensor();
- mPollFds[tmd].fd = mSensors[tmd]->getFd();
- mPollFds[tmd].events = POLLIN;
- mPollFds[tmd].revents = 0;
- #else
- mSensors[light] = new LightSensor();
- mPollFds[light].fd = mSensors[light]->getFd();
- mPollFds[light].events = POLLIN;
- #endif
- mSensors[acc] = new AccSensor();
- mPollFds[acc].fd = mSensors[acc]->getFd();
- mPollFds[acc].events = POLLIN;
- mPollFds[acc].revents = 0;
- mSensors[mag] = new MagnetoSensor((AccSensor*)mSensors[acc]);
- mPollFds[mag].fd = mSensors[mag]->getFd();
- mPollFds[mag].events = POLLIN;
- mPollFds[mag].revents = 0;
- int wakeFds[2];
- int result = pipe(wakeFds);
- fcntl(wakeFds[0], F_SETFL, O_NONBLOCK);
- fcntl(wakeFds[1], F_SETFL, O_NONBLOCK);
- mWritePipeFd = wakeFds[1];
- mPollFds[wake].fd = wakeFds[0];
- mPollFds[wake].events = POLLIN;
- mPollFds[wake].revents = 0;
- }
这部分代码就创建HAL和Kernel Event通信的类,还有Sensor数据读写管道的创建。
返回open_sensors再看剩下的代码,就是创建sensors_poll_device_t对象并把sensor控制的相关函数指针赋值给它。
6. SensorDevice 调用get_sensors_list
这个方法还是调用到了HAL中,而HAL中的这个函数也就是返回以下数组:
- /* The SENSORS Module */
- static const struct sensor_t sSensorList[] = {
- { "ST 3-axis Accelerometer",
- "STMicroelectronics",
- 1, SENSORS_ACCELERATION_HANDLE,
- SENSOR_TYPE_ACCELEROMETER, RANGE_A, CONVERT_A, 0.23f, 20000, { } },
- { "ST 3-axis Magnetic field sensor",
- "STMicroelectronics",
- 1, SENSORS_MAGNETIC_FIELD_HANDLE,
- SENSOR_TYPE_MAGNETIC_FIELD, 2000.0f, CONVERT_M, 6.8f, 16667, { } },
- { "iNemo Orientation sensor",
- "STMicroelectronics",
- 1, SENSORS_ORIENTATION_HANDLE,
- SENSOR_TYPE_ORIENTATION, 360.0f, CONVERT_O, 7.8f, 16667, { } },
- };
我们需要特别关组的是第4,5个参数,第4参数Handle是对kernel而言的,如激活,读写event,代码中的说明:
/* handle that identifies this sensors. This handle is used to activate
* and deactivate this sensor. The value of the handle must be 8 bits
* in this version of the API.
*/
而第五个参数是相对于上层代码而言。
7. mSensorDevice->activate
在获取到Sensor列表以后,我们就去激活每一个Sensor:
mSensorDevice->activate(mSensorDevice, list[i].handle, 0);
- int sensors_poll_context_t::activate(int handle, int enabled) {
- int index = handleToDriver(handle);
- if (index < 0) return index;
- LOGE("sensor.cpp:index = %d\t handle= %d\t en=%d",index,handle,enabled);//by zhangfeng
- int err = mSensors[index]->enable(handle, enabled);
- if (enabled && !err) {
- const char wakeMessage(WAKE_MESSAGE);
- int result = write(mWritePipeFd, &wakeMessage, 1);
- LOGE_IF(result<0, "error sending wake message (%s)", strerror(errno));
- }
- return err;
- }
这儿要介绍一下handleToDriver
- int handleToDriver(int handle) const {
- switch (handle) {
- case ID_A:
- return acc;
- case ID_M:
- case ID_O:
- return mag;
- #ifdef TMD27713_SENSOR
- case ID_P:
- case ID_L:
- return tmd;
- #else
- case ID_P:
- return proximity;
- case ID_L:
- return light;
- #endif
- case ID_GY:
- return gyro;
- }
- return -EINVAL;
- }
传进来的就是我们上面说的第4个参数Handle,返回的是对应的和kernel交互的类的数组下标(Sensors[acc])下标。
从上面的sensors_poll_context_t()中sensors[]的定义我们可以找到Sensors[acc]对应的值为AccSensor。
mSensors[index]->enable(handle,enabled)目的就是打开这个Sensor,里面如何打开的?linux上面不是一切兼为文件吗?就是打开对应的驱动文件嘛,所以里面的东西我们就不看了,HAL我们只分析到Sensors.cpp。
8. 扩展Sensor list
好SensorDevice里面的初始化代码走完了,回到SensorService。
- void SensorService::onFirstRef()
- {
- LOGD("nuSensorService starting...");
- SensorDevice& dev(SensorDevice::getInstance());
- ....
- if (hasGyro) {
- // Always instantiate Android's virtual sensors. Since they are
- // instantiated behind sensors from the HAL, they won't
- // interfere with applications, unless they looks specifically
- // for them (by name).
- registerVirtualSensor( new RotationVectorSensor() );
- registerVirtualSensor( new GravitySensor(list, count) );
- registerVirtualSensor( new LinearAccelerationSensor(list, count) );
- // these are optional
- registerVirtualSensor( new OrientationSensor() );
- registerVirtualSensor( new CorrectedGyroSensor(list, count) );
- // virtual debugging sensors...
- char value[PROPERTY_VALUE_MAX];
- property_get("debug.sensors", value, "0");
- if (atoi(value)) {
- registerVirtualSensor( new GyroDriftSensor() );
- }
- }
- ......
- run("SensorService", PRIORITY_URGENT_DISPLAY);
省去了很多的代码,从上面的代码可以看出如果有Gyro在Sensor List中,那么它就会注册RotationVector,Gravity,LinearAcceleration,Orientation,CorrectedGyro这些虚拟Sensor。
这些Sensor又是如何与Kernel通信的呢,我们在第七节会来分析。
最后这个run方法不得不介绍,其实SensorService是继承了Thread,而线程函数就是threadLoop,这个threadLoop在干什么呢?我们也放到第七节来讲吧!
好SensorService的初始化工作也看完了。
9、返回到SensorManager(Java)
首先它也会获取Sensor列表。
然后创建SensorEventPool和SensorThread,但这儿还没有用到,在第六节会用到。
五、获取Sensor
- public Sensor getDefaultSensor(int type) {
- // TODO: need to be smarter, for now, just return the 1st sensor
- List<Sensor> l = getSensorList(type);
- return l.isEmpty() ? null : l.get(0);
- }
这个很简单就不用解释了。
六、注册SensorLisenter
1. new ListenerDelegate(SensorEventListener listener, Sensor sensor, Handler handler)
这儿要特别说明一下,在这个构造函数中会创建一个Handler,它会在获取到Sensor数据的时候被调用。
- mHandler = new Handler(looper) {
- @Override
- public void handleMessage(Message msg) {
- final SensorEvent t = (SensorEvent)msg.obj;
- final int handle = t.sensor.getHandle();
- switch (t.sensor.getType()) {
- // Only report accuracy for sensors that support it.
- case Sensor.TYPE_MAGNETIC_FIELD:
- case Sensor.TYPE_ORIENTATION:
- // call onAccuracyChanged() only if the value changes
- final int accuracy = mSensorAccuracies.get(handle);
- if ((t.accuracy >= 0) && (accuracy != t.accuracy)) {
- mSensorAccuracies.put(handle, t.accuracy);
- mSensorEventListener.onAccuracyChanged(t.sensor, t.accuracy);
- }
- break;
- default:
- // For other sensors, just report the accuracy once
- if (mFirstEvent.get(handle) == false) {
- mFirstEvent.put(handle, true);
- mSensorEventListener.onAccuracyChanged(
- t.sensor, SENSOR_STATUS_ACCURACY_HIGH);
- }
- break;
- }
- mSensorEventListener.onSensorChanged(t);
- sPool.returnToPool(t);
- }
- };
2. sensors_create_queue
要注意一下SensorEventConnection的构造
- SensorService::SensorEventConnection::SensorEventConnection(
- const sp<SensorService>& service)
- : mService(service), mChannel(new SensorChannel())
- {
- }
SensorChannel构造://这部分还没有搞懂,这个管道的具体功能,接着往下分析希望能搞明白
- SensorChannel::SensorChannel()
- : mSendFd(-1), mReceiveFd(-1)
- {
- int fds[2];
- if (pipe(fds) == 0) {
- mReceiveFd = fds[0];
- mSendFd = fds[1];
- fcntl(mReceiveFd, F_SETFL, O_NONBLOCK);
- fcntl(mSendFd, F_SETFL, O_NONBLOCK);
- }
- }
3. sensors_data_poll
七、Sensor的数据处理流程
八、校准
初始化校准
它都是把校准数据写在一些文件里的,qcom 7627a的路径是:
/persist/GsensorCalibrationData
/persist/MsensorCalibrationData
/persist/PSensorCalibrateData
然后在Hal中对应的Sensor的构造函数中去读数据,如P-sensor对应的TmdSensor
- ioctl(dev_fd, TAOS_IOCTL_ALS_CALIBRATE, 0);
- if((fp = fopen(PSENSOR_CALIBRATED_DATA_FILE, "r+"))!= NULL)
- {
- fscanf(fp,"%d %d\n",&TaosProxCalibateData[0],&TaosProxCalibateData[1]);
- fclose( fp );
- if((TaosProxCalibateData[0] > 0) && (TaosProxCalibateData[1] < 1023) && (TaosProxCalibateData[0] < TaosProxCalibateData[1]))
- ioctl(dev_fd,TAOS_IOCTL_SET_PROX_CALIBRATE_DATA,&TaosProxCalibateData);
- else
- ioctl(dev_fd, TAOS_IOCTL_PROX_CALIBRATE, 0);
- }
- else
- {
- ioctl(dev_fd, TAOS_IOCTL_PROX_CALIBRATE, 0);
- }
发ioctl到Tmd驱动程序中去,其实这个功能比较的简单,从TaosProxcalibateData的定义可以看出就是传一个大值和一个小值。
Sensor框架理解相关推荐
- 基于 RT-Thread 的智能家居 DIY 连载教程(1)——如何正确使用 Sensor 框架
对 RT-Thread 的 Sensor 框架的理解与使用举例 如何正确使用 Sensor 框架?以 DS18B20 为例. 1. 任务清单 为了更好的讲解 Sensor 框架,我罗列了一份任务清单: ...
- 从Netflix的Hystrix框架理解服务熔断和服务降级
本文讲的是从Netflix的Hystrix框架理解服务熔断和服务降级,伴随着微服务架构被宣传得如火如荼,一些概念也被推到了我们面前,其实大多数概念以前就有,但很少被提的这么频繁.想起有人总结的一句话, ...
- lis3dh对接rt-thread sensor框架
RT-Thread的sensor框架 RT-Thread通过Sensor框架支持各种传感器,其中包括加速度传感器.使用sensor框架开发应用程序的好处就是同一套代码可以在不修改或小修改的情况下适配多 ...
- 智能家居DIY教程连载(1) ——如何正确使用 Sensor 框架
想要入门RT-Thread物联网操作系统的童鞋,出门左转这里走:https://www.rt-thread.org/document/site/ Hi~各位小伙伴们,距离 DIY 项目的发布已经有一周 ...
- 常见java面试遇到的ORM框架理解
常见ORM框架理解 前言: JAVA编程免不了和数据库打交道,那么如何高效便捷地操作数据库,也是一个需要而对的问题,原生的基于JDBC的方式当然是非常低效,而且要写一大堆无用的模板代码,不值得选取.好 ...
- 案例挑战——MVVM框架理解和实践
MVVM框架理解和实践 一.背景介绍 二. 什么是MVVM架构? 1.架构示意图 2.MVVM概念总结 3.实现VM的框架 三.通过案例来理解MVVM框架 1.没有使用MVVM架构的程序 2.使用了M ...
- linux container框架,理解和配置LinuxContainerExecutor
最近在研究如何为Hadoop开启基于Kerberos的安全配置,经过千辛万苦配置好后本以为到了见证奇迹的时刻,但是卡在NodeManger无法启动,折磨的死去活来.经历了各种百度.google后依然无 ...
- TECS——ArduPilot——代码框架理解
版权声明:本文为博主原创博文,未经允许不得转载,若要转载,请说明出处并给出博文链接 本文针对AP_TECS的框架给出对应代码及个人理解体会. 首先进行update_50hz()这个函数,50hz更新 ...
- SpringMVC框架理解
1. Spring与Web环境集成 1.1 ApplicationContext应用上下文获取方式 应用上下文对象是通过new ClasspathXmlApplicationContext(sprin ...
最新文章
- mxnet安装及NDArray初体验
- lighttpd缓存配置
- oracle数据库作业1,北京语言20秋《Oracle数据库开发》作业1(100分)
- 【Paper】2017_Distributed control for high-speed trains movements
- 为bootstrap的tab增加请求操作
- Oracle加密解密
- (八)JAVA springcloud ssm b2b2c多用户商城系统源码:配置中心服务化和高可用
- spring boot web 开发示例
- 试题3 基础练习 数列排序
- ssh架构之hibernate(二)进阶学习
- jstack分析线程状态
- Linux桌面没有minidwep,ubuntu安装minidwep-gtk
- 怎样在Excel中添加水印?学会这个方法可以轻松搞定
- 【06月25日】指数估值排名
- 国内无代码平台数据统计功能对比
- 视频直播声音不清晰的解决办法(小蜜蜂无线麦克风使用方式)
- iOS之一个超赞的视频直播、第三方库,直播看这个就够了,支持RTMP推流,美颜直播
- Windows10/11登录免密码
- 简述计算机视觉在各领域中的成功应用,计算机视觉技术在茶叶领域中的应用现状及展望...
- python控制示波器获取波形_三种方法教你用示波器快速捕获异常
热门文章
- 主键,唯一索引,唯一约束三者之间的联系与区别
- js作为参数,并且返回值;js的回调模式 callback
- 修改Linux系统分区后挂载问题
- C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(四十九) 落雷!治疗!陷阱!连锁闪电!多段群伤!魔法之终极五重奏②...
- 大数据时代下的用户洞察(转载)
- java 获取bean的注解_如何获取spring 注解的bean
- vue 类型字段除了用select框_pumelo-select
- 什么是IOC,IOC的优缺点及IOC的应用
- python 数据框按行拼接_使用python进行数据分析
- 如何避免学习linux必然会遇到的几个问题