一、概述
sensor(传感器)作为手机中一个非常重要且目前来说不可或缺的一种组件,功能强大,但是使用却很简单。Android 传感器属于虚拟设备,可提供来自以下各种物理传感器的数据:加速度计、陀螺仪、磁力计、气压计、湿度传感器、压力传感器、光传感器、近程传感器和心率传感器。因为对于日常生活来说有一部分sensor是使用频率是很高的,所以必然也伴随着手机功耗的增加如果每次都是CPU进行处理的化,而且CPU一旦休眠还伴随着sensor会停止工作,为了优化手机使用Google和MTK分别开发了CHRE 和SCP 进行sensor控制。
首先介绍以下SCP:
SCP 是用来处理sensor和audio相关功能和其他客制化需求的一个协处理理器,MTK SCP选择freeRTOS作为操作系统,CHRE是处理传感器相关操作的专门任务,它的架构如下
然后是CHRE:
在SCP下,MTK传感器集线器功能是在google CHRE ar上开发的,chre(Context Hub Runtime Environment)是一种事件驱动的体系结构,也可以被视为操作系统。
黄色部分是事件队列,CHRE只有一个while循环来处理事件队列中的头事件。如果以前的调用尚未完成,CHRE将无法调用队列中的一个任务。因此,没有优先级概念,当前事件队列处理只能
被中断中断。默认情况下,CHRE在事件队列中最多支持512个事件。CHRE的目的是实现实时性和轻量级,因此事件队列中调用的所有任务都必须快速运行。CHRE中的驱动程序实现称为nano hub app。
整个sensor体系中包括:应用层、framework层、jni、hal层、kernel层、SCP/CHRE。
二、应用层调用
sensor在应用层调用非常简单,代码如下:
public class SensorActivity extends Activity implements SensorEventListener {
private final SensorManager mSensorManager;
private final Sensor mAccelerometer;
public SensorActivity() {
mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
}
protected void onResume() {
super.onResume();
mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
}
protected void onPause() {
super.onPause();
mSensorManager.unregisterListener(this);
}
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
public void onSensorChanged(SensorEvent event) {
}
}
只需要获取到sensormanager然后注册监听就可以,sensor的信息就会通过回调接口上报上来
getSystemService 获取系统服务的标准接口,参数是表示要获取的服务类型
Sensormanger.getDefaultSensor 获取想要使用的sensor 实例 ,参数表示要获取的sensor 类型
SensorManager.registerListener 注册sensor的信息回调,参数分别是上下文,sensor,上报延时
三、framework层
经过上层的getSystemService获取了服务,功能就来到了framework层,这里对应framework/base/core/java/android/hardware/SensorManager.java,但是实际上这只是一个代理抽象类,真正获取的是SystemSensorManager:framework/base/core/java/android/hardware/SystemSensorManager.java。(从SystemServiceRegistry.java中可以看出,这是一个专门为上层注册接口的类)。
SystemSensorManager 在最上层控制着所有的sensor ,他的实现仅仅靠自己是不够的,它还链接了一个jni库,注册了很多native方法。android_hardware_SensorManager :framework/base/core/jni/android_hardware_SensorManager.cpp
static const JNINativeMethod gSystemSensorManagerMethods[] = {
{"nativeClassInit",
"()V",
(void*)nativeClassInit },
{"nativeCreate",
"(Ljava/lang/String;)J",
(void*)nativeCreate },
{"nativeGetSensorAtIndex",
"(JLandroid/hardware/Sensor;I)Z",
(void*)nativeGetSensorAtIndex },
{"nativeGetDynamicSensors",
"(JLjava/util/List;)V",
(void*)nativeGetDynamicSensors },
{"nativeIsDataInjectionEnabled",
"(J)Z",
(void*)nativeIsDataInjectionEnabled },
{"nativeCreateDirectChannel",
"(JJIILandroid/hardware/HardwareBuffer;)I",
(void*)nativeCreateDirectChannel },
{"nativeDestroyDirectChannel",
"(JI)V",
(void*)nativeDestroyDirectChannel },
{"nativeConfigDirectChannel",
"(JIII)I",
(void*)nativeConfigDirectChannel },
{"nativeSetOperationParameter",
"(JII[F[I)I",
(void*)nativeSetOperationParameter },
};
static const JNINativeMethod gBaseEventQueueMethods[] = {
{"nativeInitBaseEventQueue",
"(JLjava/lang/ref/WeakReference;Landroid/os/MessageQueue;Ljava/lang/String;ILjava/lang/"
"String;Ljava/lang/String;)J",
(void *)nativeInitSensorEventQueue},
{"nativeEnableSensor", "(JIII)I", (void *)nativeEnableSensor},
{"nativeDisableSensor", "(JI)I", (void *)nativeDisableSensor},
{"nativeDestroySensorEventQueue", "(J)V", (void *)nativeDestroySensorEventQueue},
{"nativeFlushSensor", "(J)I", (void *)nativeFlushSensor},
{"nativeInjectSensorData", "(JI[FIJ)I", (void *)nativeInjectSensorData},
};
} //unnamed namespace
int register_android_hardware_SensorManager(JNIEnv *env)
{
RegisterMethodsOrDie(env, "android/hardware/SystemSensorManager",
gSystemSensorManagerMethods, NELEM(gSystemSensorManagerMethods));
RegisterMethodsOrDie(env, "android/hardware/SystemSensorManager$BaseEventQueue",
gBaseEventQueueMethods, NELEM(gBaseEventQueueMethods));
gBaseEventQueueClassInfo.clazz = FindClassOrDie(env,
"android/hardware/SystemSensorManager$BaseEventQueue");
gBaseEventQueueClassInfo.dispatchSensorEvent = GetMethodIDOrDie(env,
gBaseEventQueueClassInfo.clazz, "dispatchSensorEvent", "(I[FIJ)V");
gBaseEventQueueClassInfo.dispatchFlushCompleteEvent = GetMethodIDOrDie(env,
gBaseEventQueueClassInfo.clazz, "dispatchFlushCompleteEvent", "(I)V");
gBaseEventQueueClassInfo.dispatchAdditionalInfoEvent = GetMethodIDOrDie(env,
gBaseEventQueueClassInfo.clazz, "dispatchAdditionalInfoEvent", "(III[F[I)V");
return 0;
}
上面代码是android_hardware_SensorManager用来绑定java类和相关方法的。然后又将方法实现转移到SensorManager.cpp:framework/native/libs/sensor/SensorManager.cpp,
SystemSensorManager创建时 会创建native SensorManager的实例并持有,然后获取sensorlist。SystemSensorManager中封装了很多方法,如:启用、禁用、刷新、添加监听等等。
SensorManager 也不是自己去做事情,它通过BitTube和SensorService 链接,将指令传到sensorservice。android_hardware_SensorManager 有一个内部类专门监听了BitTube通道文件描述符,会将sensor上报的信息通过这里上报到上层。
然后所有的事情都会转移到frameworks/native/services/sensorservice/ 这个包中,这里面会对所有sensor信息进行整理过滤,这里的所有信息都是从hal层获取,这里有个很关键的类SensorDevice,这个类链接了hal层
SensorDevice::SensorDevice()
: mHidlTransportErrors(20),
mRestartWaiter(new HidlServiceRegistrationWaiter()),
mEventQueueFlag(nullptr),
mWakeLockQueueFlag(nullptr),
mReconnecting(false) {
if (!connectHidlService()) {
return;
}
initializeSensorList();
mIsDirectReportSupported =
(checkReturnAndGetStatus(mSensors->unregisterDirectChannel(-1)) != INVALID_OPERATION);
}
...
bool SensorDevice::connectHidlService() {
HalConnectionStatus status = connectHidlServiceV2_1(); // 链接hal 2.1
if (status == HalConnectionStatus::DOES_NOT_EXIST) {
status = connectHidlServiceV2_0(); // 链接 hal 2.0
}
if (status == HalConnectionStatus::DOES_NOT_EXIST) {
status = connectHidlServiceV1_0(); // 链接 hal 1.0
}
return (status == HalConnectionStatus::CONNECTED);
}
SensorDevice::HalConnectionStatus SensorDevice::connectHidlServiceV1_0() {
// SensorDevice will wait for HAL service to start if HAL is declared in device manifest.
size_t retry = 10;
HalConnectionStatus connectionStatus = HalConnectionStatus::UNKNOWN;
while (retry-- > 0) {
sp<V1_0::ISensors> sensors = V1_0::ISensors::getService();
if (sensors == nullptr) {
// no sensor hidl service found
connectionStatus = HalConnectionStatus::DOES_NOT_EXIST;
break;
}
mSensors = new ISensorsWrapperV1_0(sensors);
mRestartWaiter->reset();
// Poke ISensor service. If it has lingering connection from previous generation of
// system server, it will kill itself. There is no intention to handle the poll result,
// which will be done since the size is 0.
if(mSensors->poll(0, [](auto, const auto &, const auto &) {}).isOk()) {
// ok to continue
connectionStatus = HalConnectionStatus::CONNECTED;
break;
}
// hidl service is restarting, pointer is invalid.
mSensors = nullptr;
connectionStatus = HalConnectionStatus::FAILED_TO_CONNECT;
ALOGI("%s unsuccessful, remaining retry %zu.", __FUNCTION__, retry);
mRestartWaiter->wait();
}
return connectionStatus;
}
SensorDevice::HalConnectionStatus SensorDevice::connectHidlServiceV2_0() {
HalConnectionStatus connectionStatus = HalConnectionStatus::UNKNOWN;
sp<V2_0::ISensors> sensors = V2_0::ISensors::getService();
if (sensors == nullptr) {
connectionStatus = HalConnectionStatus::DOES_NOT_EXIST;
} else {
mSensors = new ISensorsWrapperV2_0(sensors);
connectionStatus = initializeHidlServiceV2_X();
}
return connectionStatus;
}
SensorDevice::HalConnectionStatus SensorDevice::connectHidlServiceV2_1() {
HalConnectionStatus connectionStatus = HalConnectionStatus::UNKNOWN;
sp<V2_1::ISensors> sensors = V2_1::ISensors::getService();
if (sensors == nullptr) {
connectionStatus = HalConnectionStatus::DOES_NOT_EXIST;
} else {
mSensors = new ISensorsWrapperV2_1(sensors);
connectionStatus = initializeHidlServiceV2_X();
}
return connectionStatus;
}
...
ISensors::getService()是hal层service 的获取方式,对应的是hardware/interfaces/sensors/…/ISensors.hal ,ISensors.hal编译之后会生成ISensors.h,服务端只要实现这个头文件的方法即可
四、hal层
hal层代码接口位于:hardware/interfaces/sensors/
文件夹下定义了多个版本的hal层结构,1.0、2.0、2.1还有公共接口,一般使用最新的2.1或者2.0,文件结构如下
.hal 文件是接口文件,在编译过程中会被编译成.h,需要使用的就导入.h文件,然后ISensors::getService()获取service服务实例即可,multihal里面是关于多hal的实现,vts里面是vs测试相关,
hal层和framework层通信使用的是HIDL,和AIDL类似,底层使用的都是binder机制实现跨进程通信
ISensorsCallback.hal 里面定义的动态sensor的回调接口
ISensors.hal 里面定义的是服务的主要功能接口
types.hal 里面定义的是一些要用到的结构类型
4.1 hal的实现
真正实现hal转接口的代码是在:vendor/mediatek/proprietary/hardware/sensor 这个文件夹中也会有多个版本的代码根据android.mk 进行选择,由宏控制,选择相应的版本,当前选择最新2.0进行讨论
LOCAL_PATH := $(call my-dir)
ifdef MTK_GENERIC_HAL
## layer decoupling 2.0 chips will go to this branch
## DO NOT USE platform macro in this branch
include $(call all-named-subdir-makefiles, hidl 2.0)
else
ifeq ($(strip $(MTK_SENSOR_ARCHITECTURE)),1.0)
include $(call all-named-subdir-makefiles,hidl sensors-1.0)
else ifeq ($(MTK_SENSORS_1_0),yes)
include $(call all-named-subdir-makefiles,hidl sensors-1.0)
else
include $(call all-named-subdir-makefiles,hidl $(strip $(MTK_SENSOR_ARCHITECTURE)))
endif
endif
与framework层通信的代码在hidl文件夹下有多个文件夹:
1.0/2.0 表示版本号,配置hal哪个版本就哪个
multihal:多hal,需要单独配置由/vendor/etc/sensors/hals.conf这个文件控制
Sensors.h 中可以看出Sensors 继承了ISensors并实现了这些方法
Sensors::Sensors()
: mInitCheck(NO_INIT),
mSensorModule(nullptr),
mSensorDevice(nullptr),
mEventQueueFlag(nullptr),
mOutstandingWakeUpEvents(0),
mReadWakeLockQueueRun(false),
mAutoReleaseWakeLockTime(0),
mHasWakeLock(false),
mRunThreadEnable(false),
mEventQueueInitialized(false) {
status_t err = OK;
if (UseMultiHal()) {
mSensorModule = ::get_multi_hal_module_info();
} else {
err = hw_get_module(
SENSORS_HARDWARE_MODULE_ID,
(hw_module_t const **)&mSensorModule); //获取SENSORS_HARDWARE_MODULE_ID的模块
}
if (mSensorModule == NULL) {
err = UNKNOWN_ERROR;
}
if (err != OK) {
LOG(ERROR) << "Couldn't load "
<< SENSORS_HARDWARE_MODULE_ID
<< " module ("
<< strerror(-err)
<< ")";
mInitCheck = err;
return;
}
err = sensors_open_1(&mSensorModule->common, &mSensorDevice); // 加载模块中的sensors_module_methods,并赋值给mSensorDevice
if (err != OK) {
LOG(ERROR) << "Couldn't open device for module "
<< SENSORS_HARDWARE_MODULE_ID
<< " ("
<< strerror(-err)
<< ")";
mInitCheck = err;
return;
...
}
从Sensors.cpp代码可以看出,Sensors初始化时会先判断是否使用了多hal,如果使用则会加载多hal模块,如果使用则通过hw_get_module加载id为SENSORS_HARDWARE_MODULE_ID的模块,并把它赋值给了mSensorModule。发现这个模块代码就放在sensor目录下面对应的那些不同版本的文件夹下,直接到vendor/mediatek/proprietary/hardware/sensor/2.0/hal 这个目录下放的都是2.0 hal 的代码,其中sensors.cpp 中的正式id为SENSORS_HARDWARE_MODULE_ID的模块
...
static struct hw_module_methods_t sensors_module_methods = {
.open = open_sensors
};
struct sensors_module_t HAL_MODULE_INFO_SYM = {
.common = {
.tag = HARDWARE_MODULE_TAG,
.version_major = 1,
.version_minor = 0,
.id = SENSORS_HARDWARE_MODULE_ID,
.name = "MTK SENSORS Module",
.author = "Mediatek",
.methods = &sensors_module_methods,
},
所以可以从此看出,从framework层过来的指令给了mSensorModule和mSensorDevice 进行处理,间接的传递给了这个模块处理,然后此模块又分出很多个类进行分别处理不同的指令,部分指令又会汇集到一个类去和另一个层通信它就是HfManager,这个类在2.0/core/文件夹下,
进到HfManager 我们发现,这个类里面还有一个内部类HfLooper,但是无论哪个类,他们的方法最后会通过一个节点他就是dev/hf_manager,hal层则是通过这个节点来到了kernel层kernel-4.14/d/misc/mediatek/sensor/2.0/core/hf_manager.h
五、kernel 层
kernel层代码位于kernel文件夹下,至于kernel版本会在device下定义,我们先看kernel-4.14。sensor在kernel中的代码位于:
kernel-4.14/drivers/misc/mediatek/sensor
kernel-4.14/drivers/misc/mediatek/sensors-1.0
kernel-4.14/drivers/staging/nanohub
5.1 kernel-4.14/drivers/misc/mediatek/
首先这个文件夹下会包含很多驱动,其中有两个包sensor和sensors-1.0,这两个下面的内容其实是类似的,根据宏CONFIG_MTK_SENSOR_ARCHITECTURE会确定我们要的代码sensor中
Android.mk
...
88 ifeq ($(subst ",,$(CONFIG_MTK_SENSOR_ARCHITECTURE)),1.0)
89 obj-y += sensors-1.0/
90 else
91 obj-y += sensor/
92 endif
...
sensor 文件夹下有一个我们要的类hf_manager.c,打开这个文件不然发现,它创建了节点dev/hf_manager,这刚好就和hal层的HfManager.cpp 链接上了,hal层的指令通过这个节点来到了kernel,这是一个普通的文件节点,里面有一些读写、指令操作。这里面有个核心结构体
hf_core,后面很多操作都和这个结构体有关系
struct hf_core {
struct mutex manager_lock;
struct list_head manager_list; //外面注册的manager
struct sensor_state state[SENSOR_TYPE_SENSOR_MAX];
spinlock_t client_lock;
struct list_head client_list;
struct mutex device_lock;
struct list_head device_list;
struct kthread_worker kworker;
首先我们看下hf_manager 被打开时做了什么事情:创造了一个hf_client,这个hf_client记录了当前进程和线程的id,还创建了一个FIFO,用于数据交换
static int hf_manager_open(struct inode *inode, struct file *filp)
{
struct hf_client *client = hf_client_create();
if (!client)
return -ENOMEM;
filp->private_data = client;
nonseekable_open(inode, filp);
return 0;
}
然后,大概浏览一下代码不难发现,指令好像后面都被hf_device这个结构体承担了,这个结构体来自与hf_manager,hf_manager 每次使用之前都会调用hf_manager_find_manager方法来查找,那我么看看这个方法做了什么:
static struct hf_manager *hf_manager_find_manager(struct hf_core *core,
uint8_t sensor_type)
{
int i = 0;
struct hf_manager *manager = NULL;
struct hf_device *device = NULL;
list_for_each_entry(manager, &core->manager_list, list) {
device = READ_ONCE(manager->hf_dev);
if (!device || !device->support_list)
continue;
for (i = 0; i < device->support_size; ++i) {
if (sensor_type == device->support_list[i].sensor_type)
return manager;
}
}
pr_err("Failed to find manager, %u unregistered\n", sensor_type);
return NULL;
}
发现它只是从一个list中去查找,根据sensor_type进行匹配,这个core→manager_list 又是由hf_manager_create方法进行注册,而这个方法是向外提供的,然后搜索一下不难发现:
./2.0/sensorhub/transceiver.c 和 ./2.0/mtk_nanohub/mtk_nanohub.c使用到了它,也就是说这两个类分别注册了自己的hf_device,并且进行了相关的数据处理,这两个类所处的文件夹可以猜测一个是跟sensorhub相关,另一个则和CHRE相关。
5.2 sensor hub
sensor hub是个什么东西呢,故名思意,它是一个传感器集线器,也就是说某些传感器的数据要经过这个地方进行中转,transceiver.c它会和SCP通过IPI通信,将这边的指令还有DRAM地址发送到SCP端,然后把SCP 端的信息回传,先是读取DRAM内存获取信息,然后通过manager→report方法会传到hf_fifo,然后hf_fifo上的数据会被hal层读走。
ap侧sensor hub 通过kernel-4.14/drivers/misc/mediatek/sensor/2.0/sensorhub/ipi_comm.c和SCP侧建立链接:IPI_IN_SENSOR_NOTIFY接收ID,IPI_OUT_SENSOR_NOTIFY接收ID,IPI_IN_SENSOR_CTRL指令ID
sensor hub 所链接的SCP 侧在:vendor/mediatek/proprietary/tinysys/scp/middleware/sensorhub/comm/ipi_comm.c
5.4 nano hub
nano hub 是CHRE框架中的集线器,和sensor hub类似,nano hub 也是通过mtk_nonohub.c注册了hf_device ,和SCP通过IPI通信,将DRAM地址发送到SCP端,通过IPI把指令发过去,但是通过DRAM把信息读回来,然后通过manager→report 传递到hal层
ap侧nano hub 通过kernel-4.14/drivers/misc/mediatek/sensor/2.0/mtk_nanohub/nanohub/nanohub-mtk.c 和SCP建立链接 通过ID IPI_CHRE
nanohub 所链接的SCP侧在:vendor/mediatek/proprietary/hardware/contexthub/firmware/links/plat/src/hostIntfIPI.c
然后CHRE会调用bool SensorQueueEnqueue(struct SimpleQueue* sq, const void *data, int length, bool possiblyDiscardable)将数据传递到scp/middleware/contexthub/contexthub_fw.c,然后contexthub_fw.c 将数据写入DRAM,供AP侧读取
还有一部分指令会通过IPI_SENSOR直接传递到scp/middleware/contexthub/contexthub_fw.c
vendor/mediatek/proprierary/hardware/contexthub/firmware 下面有一些sensor,会将数据传递给hostIntf,然后传递给contexthub.c
vendor/mediatek/proprierary/tinysys/scp/middleware/sensorhub 下面也有一些sensor会传递信息
这两个地方有什么区别,目前暂未搞清楚,但是可以肯定的是sensor核心代码就在这两个地方了,CHRE 和 SCP侧,整个框架信息传递如下图: