Android层面上对sensor及event事件的处理

可能这篇总结写得会有一点凌乱,一会讲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<String8>& 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里面对字符串的定义:

<feature name="android.hardware.sensor.accelerometer" />
<feature name="android.hardware.sensor.compass" />

overlay/frameworks/base/core/res/res/values/config.xml里面对光感强度等级的描述:

<!-- Array of light sensor LUX values to define our levels for auto backlight brightness support.
 85     <integer-array name="config_autoBrightnessLcdBacklightValues">
 86         <item>5</item>    <!-- 0-5 -->
 87         <item>20</item>   <!-- 5-15 -->
 88         <item>30</item>   <!-- 15-50 -->
 89         <item>40</item>   <!-- 50-100 -->
 90         <item>50</item>   <!-- 100-200 -->
 91         <item>60</item>   <!-- 200-400 -->
 92         <item>70</item>   <!-- 400-1000 -->
 93         <item>80</item>   <!-- 1000-2000 -->
 94         <item>130</item>  <!-- 2000-3000 -->
 95         <item>180</item>  <!-- 3000-5000 -->
 96         <item>255</item>  <!-- 5000-10000 -->
 97         <item>255</item>  <!-- 10000-30000 -->
 98         <item>255</item>  <!-- 30000+ -->
 99     </integer-array>

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接口



















阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页