可能这篇总结写得会有一点凌乱,一会讲sensor,一会又讲event的。但是把两者摆在一起也是有原因的,sensor的处理是event事件的基础上实现的,正是因为sensor对event的依赖性,所以把两者摆在一起了。仔细想想,还是会有其中的原因。
开始前首先要说明两点
1、 这里说的android层面上是linux内核之上的
2、sensor指的是gsensor,lightsensor等
下面正式开始,首先简略讲一下驱动中的处理。通常对于驱动来说,一般是通过i2c来获取sensor器件的数据,当然这里面是有中断机制的处理。然后通过input_event,input_sync等函数上报事件。上报的数据,毋容置疑是写在设备节点里面(/dev/input/event*)。接下来就是看android层面上的处理了。
在android层面上,首先看到BoardConfig.mk
62 #BOARD_HAS_SENSOR := true
很明显,这是一个可配置的选项,意思也很明确,配置上了,就意味着sensor的相关的代码会被编译。接下来我们看看解析这个配置的地方:hardware里面的其中一个Android.mk
ifeq ($(BOARD_HAS_SENSOR),true)
28 LOCAL_SRC_FILES := \
29 sensors.cpp \
30 SensorBase.cpp \
31 LightSensor.cpp \
32 AccelSensor.cpp \
33 MagSensor.cpp \
34 PressSensor.cpp \
35 InputEventReader.cpp
我们看到31—34分别是:光感,加速度,磁性,压力。而这里也是可选,可以根据自己实际使用的sensor的进行增加或者删减。在这里,我们主要关注SensorBase.cpp,看以下的一段代码
145 int SensorBase::openInput(const char* inputName) {
146 int fd = -1;
147 int input_id = -1;
148 const char *dirname = "/dev/input";
149 const char *inputsysfs = "/sys/class/input";
150 char devname[PATH_MAX];
151 char *filename;
152 DIR *dir;
153 struct dirent *de;
154
155 dir = opendir(dirname);
156 if(dir == NULL)
157 return -1;
158 strcpy(devname, dirname);
159 filename = devname + strlen(devname);
160 *filename++ = '/';
161 while((de = readdir(dir))) {
162 if(de->d_name[0] == '.' &&
163 (de->d_name[1] == '\0' ||
164 (de->d_name[1] == '.' && de->d_name[2] == '\0')))
165 continue;
166 strcpy(filename, de->d_name);
167 fd = open(devname, O_RDONLY);
168
169 if (fd>=0) {
170 char name[80];
171 if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) {
172 name[0] = '\0';
173 }
174
175 if (!strcmp(name, inputName)) {
176 strcpy(input_name, filename);
177 break;
178 } else {
179 close(fd);
180 fd = -1;
181 }
182 }
183 }
184 closedir(dir);
185 ALOGE_IF(fd<0, "couldn't find '%s' input device", inputName);
186 return fd;
187 }
首先是定义event节点的目录:
148 const char *dirname = "/dev/input";
然后是打开这个目录,获取里面的event*,真正的上报数据就是从event*里面去获取。而在此之前,为了让程序有足够的权限去访问event*节点,在ueventd.$VENDOR.rc 里面会对节点解析权限设置:
25 /dev/input/event2 0666 root input
26 /dev/input/event0 0666 root input
这跟android下getevent这条命令的用法一样,我们平时在android串口命令行输入getevent 命令的时候会在串口终端打印出跟着event设备,如果用户触发上报数据,在终端命令行会输出对应的上报数据。如下:
root@rom_3420:/# getevent
couldnot get driver version for /dev/input/mice, Not a typewriter
adddevice 1: /dev/input/event1
name: "matrix-keypad"
adddevice 2: /dev/input/event0
name: "gpio-keys"
有兴趣客户自行研究一下getevent的代码,位于android源码目录下的:system/core/toolbox/getevent.c里面,读上报数据的位置是在main函数里面,用轮询机制来读取数据的,如下:
631 while(1) {
632 pollres = poll(ufds, nfds, -1);
实际上,android里面所有的读取event数据的地方,都是使用poll或select轮询机制来实现的。是一个等待,读取的过程。
但是有一点不一样的是,getevent能打印出系统里面所有的event上报数据,而上面讲到的sensor的hardware里面,仅仅读取了sensor的数据,那是因为系统会把各种event数据进行分类,如:key,touch,sensor等的数据,任何一个读取数据的地方都可以按照这种规范把数据进行分类(有兴趣可以自行研究一下)。实际上android上就是这么做的,刚才上面讲到的是hardware层读取sensor的event数据。但对于keyboard,key,touch等数据,是在framework里面处理的。如下:
frameworks/base/services/input/EventHub.cpp
70:static const char *DEVICE_PATH = "/dev/input";
处理过程跟上面讲到的hardware里面读取的过程一样,EventHub里面读取到event后,会对这些数据进行分类,然后分发到各种线程的控制器里面对数据进行最终的处理。如key,有key的处理控制器跟线程;touch,有touch的控制器跟线程。
但是讲到这里,也许会有这样一个疑问:问什么android不把sensor的数据也放到eventHub里面进行处理,而非要把sensor数据的处理分开?其实我也有这样的疑惑,如果有谁知道其中缘由,可告知。不过有一点可以确认的是,在framework里面会对sensor的数据进行过滤,就是不对sensor数据进行处理。如:
frameworks/base/services/input/EventHub.h
200 // Sets devices that are excluded from opening.
201 // This can be used to ignore input devices for sensors.
202 virtual void setExcludedDevices(const Vector& devices) = 0;
和:
frameworks/base/services/input/InputReader.cpp
496 void InputReader::refreshConfigurationLocked(uint32_t changes) {
497 mPolicy->getReaderConfiguration(&mConfig);
498 mEventHub->setExcludedDevices(mConfig.excludedDeviceNames);
其实android的代码里面也表明了,但这样做的原因,如上所说的,不知道为什么这样做。
如果想了解更多的 frameworks/base/services/input里面怎样对数据进行读取和分类的,或者是想了解android对event数据的处理的过程,可以参考下面几个博客:
Android输入事件流程中的EventHub分析及源码演示
http://blog.csdn.net/a345017062/article/details/6417929
Android应用程序键盘(Keyboard)消息处理机制分析
http://www.blogjava.net/mixer-a/archive/2012/04/17/374987.html
http://blog.csdn.net/yangwen123/article/details/14160289
好,说到这里,既然我们知道了android处理event数据的过程,那么,当我们需要的时候,就可以自己定制自己的hardware,来获取,甚至是截取android event数据。说白了,也就是对 /dev/input/ 里面节点信息的截取。说到这里,还想提一提,因为android所有处理event数据都是以轮询的机制来处理的。它不像fifo(管道)的形式,数据一旦被读取了就不见了。如果多个地方等待读取某一event的数据,那么一旦数据过来了,所有在轮询的地方都能获取到数据。而如果我们在截取数据的时候,只想让截取数据的地方获取到数据呢?个人觉得是可以实现的。这方面客户研究一下getevent和setevent的用法。
对event数据的获取就讲到这里,接下来回到sensor的处理中。
在“.mk”我们会看到会拷贝sensor的xml文件:
frameworks/native/data/etc/android.hardware.sensor.light.xml:system/etc/permissions/android.hardware.sensor.light.xml \
frameworks/native/data/etc/android.hardware.sensor.accelerometer.xml:system/etc/permissions/android.hardware.sensor.accelerometer.xml \
其实在manager里面就是通过是否存在这样一个xml文件来判断是否有sensor的存在:
frameworks/base/core/java/android/content/pm/PackageManager.java
988 @SdkConstant(SdkConstantType.FEATURE)
989 public static final String FEATURE_SENSOR_ACCELEROMETER = "android.hardware.sensor.accelerometer";
1013 /**
1014 * Feature for {@link #getSystemAvailableFeatures} and
1015 * {@link #hasSystemFeature}: The device includes a light sensor.
1016 */
1017 @SdkConstant(SdkConstantType.FEATURE)
1018 public static final String FEATURE_SENSOR_LIGHT = "android.hardware.sensor.light";
在device/$VENDOR/$BOARD/required_hardware.xml里面对字符串的定义:
overlay/frameworks/base/core/res/res/values/config.xml里面对光感强度等级的描述:
87 - 20
88 - 30
89 - 40
90 - 50
91 - 60
92 - 70
93 - 80
94 - 130
95 - 180
96 - 255
97 - 255
98 - 255
99
Framework里面会对xml的参数进行解析,具体就不去分析了。这方面还挺有意思,有兴趣可以自行分析。
init.rc里面对相关属性的定义:节点路径和默认光照强度
88 # Set light sensor sysfs path and light sensor threshold lux value
89 setprop ro.hardware.lightsensor "/sys/class/i2c-dev/i2c-2/device/2-0044/"
90 setprop ro.lightsensor.threshold 20
在后在LightSensor.cpp里面会对这两个属性解析解析:
55 property_get("ro.hardware.lightsensor", buffer, "0");
56 strcpy(ls_sysfs_path, buffer);
57 ls_sysfs_path_len = strlen(ls_sysfs_path);
58 enable(0, 1);
59 }
60
光照强度
61 /* Default threshold lux is 10 if ro.lightsensor.threshold
62 isn't set */
63 property_get("ro.lightsensor.threshold", buffer, "10");
64 mThresholdLux = atoi(buffer);
Hardware里面定义好后,接下来是JNI里面导出接口函数:
frameworks\base\core\jni\android_hardware_SensorManager.cpp
在源码里,我们可以看到JNI接口的函数列表:
static JNINativeMethod gMethods[] = {
{"nativeClassInit", "()V", (void*)nativeClassInit },
{"sensors_module_init","()I", (void*)sensors_module_init },
{"sensors_module_get_next_sensor","(Landroid/hardware/Sensor;I)I",
(void*)sensors_module_get_next_sensor },
{"sensors_create_queue", "()I", (void*)sensors_create_queue },
{"sensors_destroy_queue", "(I)V", (void*)sensors_destroy_queue },
{"sensors_enable_sensor", "(ILjava/lang/String;II)Z",
(void*)sensors_enable_sensor },
{"sensors_data_poll", "(I[F[I[J)I", (void*)sensors_data_poll },
};
然后,接下来是在framework层面对sensor数据的处理,报错以下几点。这里不着重分析,一般到这里,所有数据都交由系统源码进行处理,这部分我们基本上不会进行修改。
SensorManager.java
实现传感器系统核心的管理类SensorManager
Sensor.java
单一传感器的描述性文件Sensor
SensorEvent.java
表示传感器系统的事件类SensorEvent
SensorEventListener.java
传感器事件的监听者SensorEventListener接口
SensorListener.java
传感器的监听者SensorListener接口