恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
深入解析Android传感器框架services_manager:从HAL管理到事件分发
首页
资讯中心
/
深入解析Android传感器框架services_manager:从HAL管理到事件分发
深入解析Android传感器框架services_manager:从HAL管理到事件分发
发布时间:2026/9/9 3:53:13
说实话我第一次在代码里追 Sensor 数据流时绕着framework层一圈又一圈始终没搞明白一个问题App 里SensorManager.registerListener之后传感器数据到底是怎么从底层一路冒到应用回调里的后来跟着调用链一路往下撞到native层那个叫做sensorservice的进程整个人才豁然开朗。这个模块在没看过源码的人眼里像是个黑盒但实际上它是整个安卓 Sensor 框架的调度中枢也就是services_manager这一层要讲的主角。这篇文章我按安卓系统源码的模块划分来拆services_manager重点讲它如何管理传感器 HAL 设备、如何维护传感器列表、如何处理客户端连接以及如何完成事件分发。适合想做系统定制的 ROM 工程师、搞安卓逆向的分析师以及刚开始啃传感器框架、想知道这些服务之间是怎么串起来的新手。1. services_manager到底承担什么角色先看它在Sensor框架里的位置1.1 从一次完整的传感器上报链路说起我先从一条真实的数据路径说起。你在 App 里写mSensorManager.registerListener(listener, accelerometer, SENSOR_DELAY_NORMAL)宏观上看经过了这样几条链路App 通过 Java 层的SensorManager发起注册SensorManager内部通过 JNI 调用到libandroid_runtime的SensorManagernative 实现。native 的SensorManager拿到SensorService的 binder 代理对象调用enableSensor、setEventRate这些接口。SensorService位于sensorservice进程也就是services_manager模块的核心把请求转给SensorDeviceSensorDevice再去操作 HAL 层提供的接口打开/使能具体的传感器硬件。之后SensorService内部会有一个专门的轮询线程不停从 HAL 设备读取传感器事件。事件读上来后SensorService根据哪些连接订阅了哪个 handle 的传感器来分发事件写入对应连接的事件队列。App 侧通过共享内存和唤醒机制消费这些事件最终回调到 Java 层SensorEventListener。整个过程里最能体现services_manager价值的就是第 3 步到第 5 步。它不仅把 App 的注册请求翻译成 HAL 能懂的操作还把一个传感器、多个消费者的扇出问题解决在了系统侧让上层应用不需要关心底层硬件到底有几个进程在多路读。1.2 先厘清两个容易混淆的SensorManager在安卓源码里叫SensorManager的类其实有两处很多人看代码看到怀疑人生就是因为没区分这两个东西。一处是frameworks/base/core/java/android/hardware/SensorManager.java这是应用框架层的管理器主要职责是封装 Binder 调用、维护 App 侧的监听器注册表、把 native 事件转换成 Java 层的SensorEvent对象。应用开发者接触的都是它。另一处是frameworks/native/libsensor/SensorManager.cpp这是libsensor库里的 nativeSensorManager它内部保存了spISensorService mSensorService这个 binder 代理所有对sensorservice的调用都是从这个类发出的。而这篇要讲的services_manager更准确的名称是frameworks/native/services/sensorservice这个模块。它里面没有叫SensorServiceManager的类核心类叫SensorService另外还有SensorDevice、SensorEventConnection、SensorInterface、SensorEventQueue等配套类。你可以在代码里搜android::services::sensor::SensorService这个名字它对外的 binder 服务名是sensorservice由system_server在启动的时候注册到servicemanager。打个不太严谨但很好用的比方Java 层的SensorManager是柜台窗口libsensor的SensorManager是窗口后面的柜员services_manager这个模块才是后台机房机房里有设备总控台SensorDevice、工单分发员SensorService、客户档案柜SensorEventConnection。1.3 services_manager职责拆开看我自己在梳理代码时习惯把这个模块的职责拆成四块硬件管理初始化时打开传感器 HAL拿到所有传感器设备的能力描述类型、采样率范围、量程、功耗、FIFO 容量等。运行过程中向 HAL 发起激活、去激活、批量设置、flush 等操作。连接管理每个要消费传感器数据的客户端进程对应一个SensorEventConnection连接对象。连接管理包括创建连接、分配事件队列、处理订阅关系、监控客户端 Binder 死亡并及时清理。事件分发HAL 轮询线程读上来的原始事件要根据连接订阅情况精准投递到对应的事件队列。这里还涉及批量模式、唤醒事件、动态传感器等一系列特殊情况。系统集成作为 binder 服务sensorservice还要对上层提供传感器列表查询、direct channel直连通道、setOperationMode比如OP_MODE_DATA_INJECTION调试注入、传感器隐私开关等能力。掌握了这份职责清单再去看sensorservice目录下的代码就不会迷失在类关系里了。2. SensorService初始化过程一个binder服务是怎么活起来的2.1 instantiate与onFirstRef启动时序里的隐藏细节先看SensorService是怎么被拉起注册的。system_server里会调用// SystemServer.java 里通过 loadSensorService() 触发 SensorService::instantiate();instantiate()是BinderService模板类提供的静态方法它做的事情很简单new 出一个SensorService对象然后通过defaultServiceManager()-addService(String16(sensorservice), service)注册到servicemanager。这里有个值得注意的时序点SensorService构造函数里不会做真正的硬件初始化真正的初始化逻辑全部集中在onFirstRef()。原因跟 Binder 服务注册的引用计数机制有关——对象在addService之前可能还没有被强引用而在onFirstRef被触发的时机可以保证服务已经进入了 Binder 框架的管控范围。void SensorService::onFirstRef() { ALOGI(SensorService: starting); SensorDevice dev(SensorDevice::getInstance()); if (dev.initCheck() NO_ERROR) { sensor_t const* list; ssize_t count dev.getSensorList(list); if (count 0) { // 遍历 HAL 上报的所有传感器逐个构建 SensorInterface 与 Sensor 对象 for (ssize_t i 0; i count; i) { SensorInterface* sensor new HardwareSensor(list[i]); Sensor s sensor-getSensor(); // 一方面以 handle 为 key 存入 mSensors用于访问硬件接口 mSensors.add(s.getHandle(), sensor); // 另一方面存入 mSensorMap用于向上层返回 Sensor 描述信息 mSensorMap.add(s.getHandle(), s); } } mInitialized true; // 启动传感器事件轮询线程 run(SensorService, PRIORITY_URGENT_DISPLAY); } }上面这段逻辑我已经做了化简实际代码还包含mSensors里动态传感器管理的初始化以及mWakeLock的获取等但核心顺序就是这样。initCheck()起到关口作用只有 HAL 初始化成功的条件下后续所有的传感器列表构建和线程启动才有意义。2.2 SensorDevice打开HAL从hw_get_module到HIDL/AIDL代理初始化里最核心的一步是SensorDevice::getInstance()。这个单例在构造时就要跟传感器 HAL 建立联系。不同安卓版本打开 HAL 的方式差异很大Android 8.0 之前传统方式编译时直接链接 HAL 头文件运行时用hw_get_module(SENSORS_HARDWARE_MODULE_ID, ...)到系统路径下找对应的.so模块然后调用sensors_open_1拿到sensors_poll_device_1_t设备对象。这一阶段SensorDevice和 HAL 在同一个进程空间调用就是普通的函数指针跳转效率很高但缺点是系统编译时和 HAL 耦合太紧。Android 8.0 到 12HIDL 时代由于 Treble 架构sensorservice不能直接dlopenvendor 的库了。SensorDevice改为通过 HIDL 接口android.hardware.sensors1.0/2.0/2.1::ISensors获取 HAL 代理。初始化时大致是mSensorHal ISensors::getService(); mSensorHal-init(); mSensorHal-getSensorsList([](const hidl_vecSensorInfo list) { // 把 hidl 格式的 SensorInfo 转换成内部 sensor_t 列表 });Android 13 之后AIDL 化HIDL 接口逐渐被 AIDL 的android.hardware.sensors.aidl::ISensors替代SensorDevice里的代码越来越多地变成 AIDL 调用比如mSensorHal-getSensorsList()、mSensorHal-batch()、mSensorHal-flush()。从代码阅读的角度讲如果你是在新版本上做开发建议直接去看SensorDevice.cpp里那段带#ifdef USE_HIDL或USE_AIDL的宏分支这样能同时理解新旧两种 HAL 接入方式。2.3 传感器列表的解析与存储从sensor_t到Sensor对象HAL 上报的传感器信息实际是一个sensor_t结构体数组。sensor_t字段非常多平时开发最常用的有这些:字段含义实际影响handle传感器的整数标识后面所有订阅、事件分发都靠 handle 来匹配type传感器类型区分加速度、陀螺仪、磁场、光感等maxRange量程上限约定了上报数值的范围resolution分辨率数值的最小变化量power平均功耗mA系统省电策略会参考这个值minDelay最短采样间隔微秒对应最高采样频率fifoMaxEventCountFIFO 最大容量批量模式下能缓存多少事件fifoReservedEventCountFIFO 保留容量单个传感器独占的 FIFO 深度SensorService拿到sensor_t数组后会为每个传感器创建一个HardwareSensor对象实现SensorInterface同时包装一个Sensor对象。Sensor对象是能跨 binder 传给上层的名片里面带着传感器名字、厂商、handle、type、参数等方便 App 侧展示和判断能力。这个阶段最容易遇到的一个问题是 HAL 实现不规范导致sensor_t里的type填错或者重复填同一个handle。SensorService对重复 handle 的处理很直接后面的传感器会把前面的覆盖掉表现就是某个传感器在上层列表里消失。我见过不少 ROM 的传感器列表缺项最后排查下来就是 vendor HAL 里面handle没有保证全局唯一。3. 事件轮询与分发services_manager最繁忙的流水线3.1 poll循环的线程模型SensorService初始化完成后会启动一个线程专门做事件轮询。线程的核心逻辑写在threadLoop里基本形态是这样bool SensorService::threadLoop() { spSensorEvent buffer new SensorEvent[MAX_POLL_EVENTS]; while (true) { // 阻塞式读取一批事件 ssize_t count mSensorDevice-poll(buffer, MAX_POLL_EVENTS); if (count 0) { ALOGE(sensor poll failed (%s), strerror(-count)); // 休眠一小段时间避免持续空转 usleep(1000); continue; } // 逐个处理事件 for (ssize_t i 0; i count; i) { processEvent(buffer[i]); } } return false; }MAX_POLL_EVENTS在源码里通常取 16。为什么不一次只 poll 一个因为 HAL 层在poll里往往已经积累了多个传感器的事件一次性读取一批再处理可以减少系统调用次数提高吞吐。真正深挖的话SensorDevice::poll在传统线程模型里是直接调用 HAL 的poll函数指针它会被事件或超时唤醒。而在 HIDL/AIDL 时代poll是阻塞式 binder 调用里面实际上会等待 vendor 实现poll回调返回一批事件。这个等待过程会吃掉一个线程所以sensorservice进程的线程数里你会看到一个常驻的 SensorService 线程。3.2 事件分发的规则连接匹配与共享内存队列事件从 HAL 读上来之后processEvent要解决一个问题这一个事件到底应该发给谁SensorService内部维护了一个VectorspSensorEventConnection mSensorConnections。每个连接对象记录了它订阅了哪些传感器 handle、各自对应的采样参数和 pending 事件队列。分发的核心思想就是遍历所有连接如果这个连接订阅了当前事件所属的 handle就把事件写入该连接的队列。早期的实现里事件的载体是SensorEventQueue这个类往上会跟应用进程建立一块共享内存队列。SensorService往共享内存里写事件App 侧从里面读事件配合 binder 的唤醒机制通过BitTube发送通知实现低延迟、低拷贝的事件传递。这里有三个容易被忽视的点Flush 事件当 App 请求flush强制立即输出一次当前结果时事件本身不被poll读上来而是 HAL 异步返回一个META_DATA_FLUSH_COMPLETE类型的元数据事件。SensorService需要识别它然后转发给发起 flush 的那个连接。唤醒事件带有WAKE_UP属性的传感器读到事件后SensorService会持有一段时间的 wakelock防止系统在应用还没处理事件时进入 suspend。分发给连接后wakeup 事件会被标记为已在唤醒状态下消费再配合SensorEventQueue的wakeLockTimeOutLocked逻辑自动释放。消费慢的连接如果某个连接处理事件的速度跟不上它的队列就会满。SensorService在写不进去时要决定是丢弃新事件还是断开连接通常处理逻辑是丢弃并记录丢包计数避免因为一个慢消费者拖垮整个系统。3.3 批量模式、动态传感器与direct channel如何接入管道services_manager不仅仅是轮询扇出这么简单它还承载了不少高级特性的接入。批量模式Batching老 Android 里每个事件都得上报CPU 频繁被唤醒。后来 HAL 支持硬件 FIFO 缓冲SensorService在连接订阅时根据 App 设置的maxDelay调用batch()接口把事件先在 HAL FIFO 里攒一段时间再批量读上来。这解释了为什么processEvent里面对事件时间戳的判断逻辑必须允许一批事件里的时间戳跨越较大范围而不能假设它们是点对点实时上报的。动态传感器Dynamic Sensor像外接 USB 传感器、可穿戴设备上的传感器是运行时可插拔的。HAL 实现通过ISensorsCallback回调通知SensorService在onDynamicSensorConnected时SensorService要动态创建一个新的Sensor对象并广播给所有连接在onDynamicSensorDisconnected时要清理对应状态。动态传感器有个很大的特征它不受静态传感器列表的保护通常只对指定连接可见订阅关系也比普通传感器复杂得多。direct channel这个特性比较高端它是 Android 8.0 引入的。正常路径是感官事件先到SensorService再由SensorService分发给 Appdirect channel 则直接把共享内存的句柄传给 HAL让 HAL 自行写入传感器事件绕过SensorService的事件循环。这样可以实现极低延迟主要用于 VR/AR 这类对延迟极度敏感的交互场景。实际的项目里真正启用 direct channel 的厂商不算多因为需要 HAL 硬件和驱动的配合但框架层SensorService的createDirectChannel/destroyDirectChannel接口你一定要知道它代表了传感器框架的另一个发展方向。4. 客户端连接管理SensorEventConnection的完整生命周期4.1 连接的创建与通道分配每次 App 调registerListener最终会走到SensorService::createSensorEventConnection不同版本方法名略有差异比如 Android 13 上可能是createConnection。这个接口会 new 一个SensorEventConnection对象并且把连接跟一个SensorEventQueue绑定队列的核心是一个共享内存环形缓冲区以及用于唤醒 App 读取线程的 fd。连接创建后App 侧的SensorManager会拿到这个连接再做setEventRate和enableSensor。连接对象上有几个在dumpsys里很常见的字段mSensorConnections全局连接列表。mActiveConnections处于激活状态的连接数量。mEvents连接内部的事件 pending 队列。mSensorInfo记录每个订阅传感器 handle 对应的采样参数、最大延迟等。从开发排查的角度通过dumpsys sensorservice能看到每个连接的 pid、包名、订阅了哪些传感器、最近是否有丢包这对定位谁在偷跑传感器功耗特别有用。4.2 采样率、Flush与批量参数的处理App 在SensorManager.registerListener时传的samplingPeriodUs最终会在SensorEventConnection::setEventRate里被换算成两个参数传给 HALsamplingPeriodNs采样周期纳秒maxBatchReportLatencyNs最大批量上报延迟纳秒这里有个经常被踩的坑多个 App 同时订阅同一个传感器但采样率不同HAL 的采样率怎么算SensorService的做法是尽量满足所有连接里最高的采样率。所以当你看到某个 App 用SENSOR_DELAY_FASTEST订阅了加速度计整个系统的加速度计输出频率都会被拉满其他普通 App 的事件率也会跟着变高省电效果自然就差了。Flush 的流程也很有意思。App 调requestFlush()Binder 调用到SensorServiceSensorService找到对应的连接然后调用 HAL 的flush(handle)。HAL 处理完之后会返回一个 flush complete 事件type 为META_DATA_FLUSH_COMPLETESensorService会把这个事件塞回对应连接的事件队列。如果 HAL 的 flush 一直没有返回App 侧的 flush 回调也一直等不到——这在一些实现不完善的 vendor HAL 上是常见的兼容性问题。4.3 死亡通知与连接清理最容易泄漏的一环SensorEventConnection生命周期里我遇到过最多的问题就是连接清理不及时。客户端进程崩溃、被杀、或者长时间挂起之后它跟SensorService的 binder 连接理论上应该断开但如果SensorService侧没有正确处理死亡通知连接就会一直残留在mSensorConnections里。正确的处理逻辑在ISensorEventConnection上通过linkToDeath注册死亡回调客户端进程死掉时SensorService收到binderDied回调然后从mSensorConnections移除该连接。遍历连接订阅的所有传感器如果这个传感器只剩这一个连接在订阅就调用activate(handle, 0)去禁用硬件。释放连接内部的事件队列和共享内存。但这里有个细节binderDied是在 Binder 驱动检测到客户端死亡时回调的它运行在SensorService的某个线程上下文中清理过程需要加锁还要避免在持锁状态下调用 HAL 的activate否则可能造成死锁因为 HAL 侧回调有可能反过来调用SensorService的接口。实际开发里我看到有团队在这一步简化处理把 HALactivate放到释放锁之后虽然大多数时候不会出问题但并发量大时确实可能触发异常。所以连接清理这一块代码看着简单坑其实很深。5. 版本演进中的services_manager从直接读库到AIDL化5.1 传统sensor HAL的加载方式安卓早期传感器框架的设计是SensorService直接通过hw_get_module加载 HAL 动态库。这种模式的好处是调用路径极短事件读写和函数调用都在同一个进程内完成性能表现很好。但它的坏处也同样明显整个系统镜像编译的时候SensorService和 HAL 的接口头文件必须保持一致HAL 升级依赖系统镜像升级无法做到硬件抽象与系统框架解耦。这也为后来 Treble 架构的大规模改动埋下了伏笔。在看老代码时SensorDevice里会有sensors_module_t、sensors_poll_device_1_t、sensors_event_t这些结构体SensorService发的事件类型和 HAL 上报的事件是同一种sensors_event_t所以在老版本里事件处理不需要做类型转换逻辑干净很多。现在的代码里则到处是ASensorEvent、Event、ISensorsEvent之间的转换这个变化就是版本演进最直观的体现。5.2 Treble之后HIDL接口带来的隔离和代价Android 8.0 引入 Treble 架构本质诉求是让 system 分区和 vendor 分区可以独立升级。为此SensorService不能再直接链接 vendor HAL而是通过 HIDL binder 接口android.hardware.sensors1.0::ISensors访问底层。这个改动对services_manager的影响主要有三点初始化路径变了SensorDevice不再hw_get_module而是ISensors::getService()去拿 HAL 代理。如果 HAL 服务没起来getService()默认会等待一段时间所以新版本开机时如果 vendor HAL 启动慢SensorService也可能跟着启动慢。数据结构多了转换层HAL 侧上报的是 HIDL 的Event包含SensorInfo、Uint64Data、FloatData等 union 类型字段SensorService拿到后要转成内部通用的ASensorEvent再分发这里的转换逻辑占了不少代码量。动态传感器回调走 binderHAL 需要实现ISensorsCallback接口在动态传感器插拔时跨进程回调到SensorService相比老版本的同进程回调又多了一层时序和线程的复杂性。HIDL 版本里还衍生出针对 Wear 和其他场景的特性比如2.0::ISensors增加了injectSensorData2.1::ISensors对动态传感器和事件回调做了更多完善。对应用层没有太大影响但对SensorService内部的实现版本判断影响明显。5.3 Android 13之后AIDL化对sensor事件管道的改造安卓在 13 之后推动把各类 HIDL 接口迁移到 AIDLsensor 相关接口也不例外。新接口是android.hardware.sensors.aidl::ISensors从 HIDL 的SensorInfo换成了 AIDL 的SensorInfo事件结构从 HIDL 的Event换成 AIDL 的ISensorsEvent并引入了AIDL环境下事件类型更紧凑的编码方式。对SensorService这一层来说AIDL 化不只是改改接口名还涉及到SensorDevice内部增加USE_AIDL分支AIDL 方式下可以通过registerCallback注册传感器事件回调也可以继续使用poll()方式。动态传感器的连接方式从ISensorsCallback变成 AIDL 的ISensorsCallback回调线程模型的约束跟 HIDL 时代有所不同。事件分发时如果批量读上来的事件里同时包含多种传感器类型SensorService需要按handle拆分并分发到不同的连接这部分逻辑在 AIDL 环境下也经历了重构。从实操角度讲如果你在维护一个 Android 13 的 ROM又恰好在做 sensor 相关的功能改造一定要先去SensorDevice.cpp里确认当前代码走的是 HIDL 分支还是 AIDL 分支因为两者的接口名、返回码、权限校验逻辑都不同照搬老代码很容易编译过了但运行时报错。6. 实战排障SensorService上那些让人头疼的问题6.1 传感器列表为空先看初始化链路再查vendor现象App 层SensorManager.getSensorList返回空列表dumpsys sensorservice里mSensors集合为空。排查链路先看logcat搜SensorService。如果能看到SensorDevice: couldnt init之类的错误说明 HAL 初始化失败。如果错误是getService超时基本是 vendor 侧的 sensor HAL 进程没有起来用ps -A | grep sensors确认对应进程是否存在。如果 HAL 进程在但传感器列表还是空重点检查getSensorList返回的count是否为 0以及sensor_t数组是否被正确填充。还有个容易出现的情况是 SELinux 权限拦截老版本直接读 HAL 库时如果 SELinux 策略没放行SensorService会在initCheck阶段失败。这类问题 80% 出在 vendor HAL 侧SensorService本身的逻辑非常稳定不要上来就怀疑框架代码。6.2 高频率事件导致CPU飙高分发路径上的性能瓶颈现象某个 App 用SENSOR_DELAY_FASTEST订阅传感器整机 CPU 占用明显升高部分场景出现掉帧。排查链路先dumpsys sensorservice找到当前连接列表确认谁在用最高频率订阅哪个传感器。用systrace抓SensorService线程观察事件分发阶段的耗时如果分发耗时长通常是连接数量多导致的循环开销大。确认是不是多个连接订阅了同一个高频传感器SensorService对每个事件都要遍历所有连接判断是否订阅连接的订阅数量直接影响分发性能。修复思路从框架侧限制单连接的最高采样率或者让SensorService维护每个传感器对应的活跃连接列表避免每次分发都全量遍历。从产品策略上建议收紧那些后台应用请求 FASTEST的场景只允许前台特定白名单应用使用最高频率。6.3 动态传感器拔插后连接泄漏现象外接传感器拔出后个别应用的传感器回调仍然在触发或者dev.magnetic这类动态传感器重新插上后无法再被识别。排查链路先用dumpsys sensorservice查看mDynamicSensors和连接列表确认拔出后动态传感器是否还残留在列表里。检查SensorService::onDynamicSensorDisconnected是否被回调。如果没回调问题出在 HAL 侧拔插检测或者 HAL 的 callback 没触发。如果回调触发了但连接状态没清理就要检查是哪个连接还在引用这个传感器。常见原因是应用侧收到DISCONNECT事件后没有解除监听SensorService这边虽然标记了传感器断开但客户端的 enable 状态没有同步撤销。经验教训动态传感器功能启用前HAL 侧的拔插检测协议一定要先测透否则每次拔插都会在框架层留下一堆半连接状态时间一长整个传感器系统会越来越卡。6.4 wakeup传感器让系统无法休眠wakelock何时释放现象设备灭屏后无法进入 suspenddumpsys power显示某个 wakelock 被SensorService持有。排查链路判断是哪个传感器持锁。通常wakeup类传感器如抬手亮屏用的WAKE_UP加速度计事件上报后会持有 wakelock。看SensorEventQueue是否一直有未消费的 wakeup 事件如果 App 侧处理太慢队列积压SensorService认为事件还没被消费掉就会一直不释放 wakelock。调整思路不是让SensorService永远不持锁而是在事件成功写入队列后尽快释放因为真正决定 App 是否收到事件的是共享内存队列消费而不是 wakelock 持有时间。这种问题修复起来往往需要框架和 App 两侧配合框架侧减少异常持锁窗口App 侧保证事件回调里的处理逻辑足够轻快不要在回调里做耗时操作。我自己的经验是遇到传感器框架的疑难杂症先用dumpsys sensorservice和logcat -s SensorService拉一轮基本现场再做代码分析。很多时候答案不在于复杂的源码逻辑而在于某个连接把高频传感器订阅得太久、或某个 vendor HAL 把延迟处理当成了默认行为。只要你把services_manager这个模块的职责链条理清楚——从初始化、事件轮询、连接管理到 HAL 接口变迁——绝大多数定位过程都能在几分钟内缩小到一个明确的疑点。如果再往后扩展你可以继续往下看 JNI 层和 framework 层的传感器管理是怎么跟services_manager对接的也可以往上研究 HAL 里sensor的硬件驱动模型。每深入一层整个安卓传感器体系的图景都会更完整一些。