AAOS系列之(六) ---CarPowerManager中写入的状态,如何在ViewRootImpl中读取问题
一文讲透AAOS架构,点到为止不藏私
📌 这篇帖子给大家分析下 AAOS和Framework代码的交互
前面的帖子给大家介绍了CarPowerManager模块的基本功能和逻辑代码,今天分享一个在开发中遇到一个问题:
需求如下:
当用户点击屏幕上的"电源"按钮, IVI主机进入息屏状态,关闭了声音输出. 但是触摸屏(TP)不能禁用. 息屏后, 用户点击屏幕的任意位置, 需要退出待机状态,点亮屏幕, 同时,这次屏幕的触摸事件不能触发UI的跳转, 也就是说, 在framework中需要消费掉这次点击事件.
触摸屏事件上报的流程:
触摸事件从 Kernel 传到 Android Framework(如 ViewRootImpl) 的完整流程,涉及 驱动层 → Input 子系统 → InputManager → WindowManager → ViewRootImpl,下面按层级详细说明:
1. Kernel 层(Linux Input 子系统)
触摸事件通过 TP 驱动(一般是 I2C/SPI)注册为 /dev/input/eventX。
触发时,驱动调用如下代码,Linux Input 子系统将这些事件写入 /dev/input/eventX,由上层读取。
input_report_abs(dev, ABS_MT_POSITION_X, x);
input_report_abs(dev, ABS_MT_POSITION_Y, y);
input_sync(dev);
2. Native 层:EventHub 和 InputReader(system/server)
💡 模块说明
模块 | 文件位置 | 作用 |
---|---|---|
EventHub | InputReader.cpp /EventHub.cpp | 从 /dev/input/eventX 读取事件 |
InputReader | InputReader.cpp | 解析事件、生成 MotionEvent |
InputDispatcher | InputDispatcher.cpp | 将事件派发给目标窗口(Window) |
主要流程如下:
-
EventHub 使用 epoll 监听 eventX 设备;
-
检测到事件后读入数据,传递给 InputReader;
-
InputReader 将原始事件解析为 MotionEvent;
-
InputDispatcher 根据坐标找到目标窗口,派发事件。
3.Java 层:WindowManagerService 和 View 层:
InputDispatcher↓
WindowManagerService↓
InputChannel↓
ViewRootImpl↓
DecorView / Activity↓
ViewGroup.dispatchTouchEvent()↓
View.onTouchEvent()
4. 触摸事件的拦截:
经过上面的流程分析, 我们得知,拦截用户点击屏幕的最佳位置是ViewRootImpl.java 的onInputEvent方法, 该方法的原始代码如下:
public final class ViewRootImpl implements ViewParent,View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {@Overridepublic void onInputEvent(InputEvent event) {Trace.traceBegin(Trace.TRACE_TAG_VIEW, "processInputEventForCompatibility");List<InputEvent> processedEvents;try {processedEvents =mInputCompatProcessor.processInputEventForCompatibility(event);} finally {Trace.traceEnd(Trace.TRACE_TAG_VIEW);}if (processedEvents != null) {if (processedEvents.isEmpty()) {// InputEvent consumed by mInputCompatProcessorfinishInputEvent(event, true);} else {for (int i = 0; i < processedEvents.size(); i++) {enqueueInputEvent(processedEvents.get(i), this,QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY, true);}}} else {// 分发触摸屏事件enqueueInputEvent(event, this, 0, true);}}
拦截事件的位置找到了, 我们在这里添加代码来读取当前的屏幕状态, 如果是息屏状态,则写入需要亮屏的指令,然后把事件的类型改为 MotionEvent.ACTION_CANCEL, 修改后的代码如下:
public void onInputEvent(InputEvent event) {//=== add start// 如果是息屏状态下,点亮屏幕,不处理这次触摸事件int screenState = CarDataManager.getInt(CarPowerManager.CACHE_STATE_URI, CarPowerManager.KEY_SCREEN_STATE);if (event != null && screenState == CarPowerManager.SCREEN_STATE_OFF) {CarDataManager.putInt(CarPowerManager.CACHE_STATE_URI, CarPowerManager.KEY_SCREEN_STATE, CarPowerManager.SCREEN_STATE_ON);// 如果需要亮屏, 把事件类型转为ACTION_CANCEL, 不让APP响应事件.((MotionEvent)event).setAction(MotionEvent.ACTION_CANCEL);}//=== add endTrace.traceBegin(Trace.TRACE_TAG_VIEW, "processInputEventForCompatibility");List<InputEvent> processedEvents;
添加完代码后开始编译framework:
android11.0$ make services framework framework-minus-apex javac-check-framework car-frameworks-service framework-res -j32^C
不好的事情发生了, 编译报错了. 啊! 啊! 啊!
s-apex/android_common/javac/shard14/classes -D out/soong/.intermediates/frameworks/base/framework-minus-apex/android_common/javac/shard14/classes && rm -rf "out/soong/.intermediates/frameworks/base/framework-minus-apex/android_common/javac/shard14/srcjars"
frameworks/base/core/java/android/view/ViewRootImpl.java:188: error: package android.car.datacenter does not exist
import android.car.datacenter.CarDataManager;^
frameworks/base/core/java/android/view/ViewRootImpl.java:189: error: package android.car.hardware.power does not exist
import android.car.hardware.power.CarPowerManager;^
2 errors
15:23:30 ninja failed with: exit status 1#### failed to build some targets (02:10 (mm:ss)) ####
报错信息显示,ViewRootImpl中import的包不存在,代码明明是存在的, 为啥会引用不到呢???
尝试1:
既然是ViewRootImpl中依赖了car-lib中的代码, 编译引用不到, 是不是在android11.0/frameworks/base/Android.bp的依赖文件中没有添加car-lib的jar包呢?
碰到问题不可怕,冷静下来想方法!!!
报错显示找不到包, 那么我给你指定下包在哪是不是就行了?
马上行动, AOSP 编译的时候,在打包framework.jar时, 会指定很多的三方jar文件,配置如下:
// Collection of classes that are generated from non-Java files that are not listed in
// framework_srcs. These have no or very limited dependency to the framework.
java_library {name: "framework-internal-utils",static_libs: ["apex_aidl_interface-java","suspend_control_aidl_interface-java","framework-protos","game-driver-protos","android.hidl.base-V1.0-java","android.hardware.cas-V1.0-java","android.hardware.cas-V1.1-java","android.hardware.cas-V1.2-java","android.hardware.contexthub-V1.0-java","android.hardware.contexthub-V1.1-java","android.hardware.gnss-V1.0-java","android.hardware.gnss-V2.1-java","android.hardware.health-V1.0-java-constants","android.hardware.radio-V1.0-java","android.hardware.radio-V1.1-java","android.hardware.radio-V1.2-java","android.hardware.radio-V1.3-java","android.hardware.radio-V1.4-java","android.hardware.radio-V1.5-java","android.hardware.thermal-V1.0-java-constants","android.hardware.thermal-V1.0-java","android.hardware.thermal-V1.1-java","android.hardware.thermal-V2.0-java","android.hardware.tv.input-V1.0-java-constants","android.hardware.tv.tuner-V1.0-java-constants","android.hardware.usb-V1.0-java-constants","android.hardware.usb-V1.1-java-constants","android.hardware.usb-V1.2-java-constants","android.hardware.usb.gadget-V1.0-java","android.hardware.vibrator-V1.0-java","android.hardware.vibrator-V1.1-java","android.hardware.vibrator-V1.2-java","android.hardware.vibrator-V1.3-java","devicepolicyprotosnano",
把car-lib加入到依赖的jar包路径,修改如下:
"android.hardware.vibrator-V1.2-java","android.hardware.vibrator-V1.3-java","devicepolicyprotosnano", // 注意这个后面的",""android.car"
这里补充一句, 如何去查找CarPowerManager所在的jar包呢?
CarPowerManager的源码路径如下:
android11.0\packages\services\Car\car-lib\src\android\car\hardware\power\CarPowerManager.java
car-lib的编译脚本如下:
android11.0\packages\services\Car\car-lib\Android.bp
java_library {name: "android.car", // 编译的输入物srcs: ["src/**/*.java","src/**/I*.aidl",],aidl: {include_dirs: ["system/bt/binder",],},exclude_srcs: ["src/android/car/storagemonitoring/IoStats.aidl","src/android/car/storagemonitoring/IoStatsEntry.aidl",],static_libs: ["android.car.internal.event-log-tags",],product_variables: {pdk: {enabled: false,},},installable: true,
}
从编译脚本可见,编译后的输出物为android.car
满心欢喜,继续…
不出意外的话, 必然有意外.
又报另外一个错误, 信息如下:
[hardware/rockchip/libgralloc/bifrost frameworks/native/include system/core/libsync system/core/libsync/include external/libdrm/include/drm] 30
>>>>>>>>>>>>>>>>>>>>> rk356x
libcameradevice curr board is rk356x
error: frameworks/base/Android.bp:479:1: encountered dependency cycle:
error: frameworks/base/Android.bp:518:1: "framework" depends on "framework-minus-apex"
error: frameworks/base/Android.bp:479:1: "framework-minus-apex" depends on "framework-internal-utils"
error: frameworks/base/Android.bp:349:1: "framework-internal-utils" depends on "android.car"
error: packages/services/Car/car-lib/Android.bp:74:1: "android.car" depends on "framework"
16:19:26 soong bootstrap failed with: exit status 1#### failed to build some targets (16 seconds) ####
又来…
从这个报错信息来看, 是循环引用了:
framework 依赖 framework-minus-apex
framework-minus-apex 依赖 framework-internal-utils
framework-internal-utils 依赖 android.car
android.car 依赖 framework
遇到一个"鸡生蛋, 还是蛋生鸡的问题"…
经过一番折腾后, 发现此路不通, 只得另寻它路.
一番冥思苦想之后, 发现我们的需求是 “在不同的进程间共享状态”, 系统提供了几种进程间共享数据的方式如下:
对比项 | SystemProperties | Settings.Global.putInt() | 广播(Broadcast) |
---|---|---|---|
本质 | 内核属性(属性服务) | ContentProvider(数据库访问) | Binder + Intent 派发 |
通信方向 | 单向(读/写) | 单向(读/写) | 单向或多向(可被多个组件接收) |
使用场景 | 轻量级系统配置;开机参数;Boot 动态配置 | 全局配置项(如亮度、音量模式等) | 通知型事件(如网络变化、电池变化等) |
设置权限控制 | system 权限;部分 key 需 SELinux 配置 | 需系统签名或拥有权限(如 WRITE_SETTINGS) | 权限控制精细(需定义权限过滤) |
读写效率(性能) | 非常高(C 层实现,几乎无 IO 成本) | 较慢(写入数据库,有 IO) | 中等偏慢(Intent 封送+跨进程) |
内存占用 | 很小 | 较小 | 中等(Intent 结构体 + 广播记录) |
实时性 / 响应速度 | 高 | 中等 | 中等偏低(尤其是异步广播) |
⛓ 是否支持监听/回调 | 否(仅轮询读取) | 否(只能主动查询) | 支持广播接收器监听(动态/静态) |
适合用于跨版本或模块通信 | 不推荐(key 容易变动) | 稳定性较差,schema 不统一 | 推荐,尤其适合解耦模块间传递状态或事件 |
安全性(信息暴露风险) | 较低(权限严格) | 中(视 key 设置而定) | 需要明确权限设置,广播可能被第三方监听 |
✅ 是否推荐用于模块解耦通信 | ❌ 不推荐(只做简单配置) | ❌ 不推荐(更多用于配置项) | ✅ 推荐(尤其是系统-APP/模块间事件通知) |
这三种进程间共享数据的优缺点总结如下:
场景 | 推荐方式 |
---|---|
传递轻量状态或只读参数 | ✅ SystemProperties |
写入系统设置项(如亮度) | ✅ Settings.Global |
模块间事件通知、状态变更 | ✅ 广播(Broadcast) |
高性能低延迟状态读写 | ✅ SystemProperties |
需要监听回调 | ✅ 广播 |
总结:
对于我们当前的场景,在PowerManager中通过SystemProperties.put()把状态保存下来. 在ViewRootImpl中通过SystemProperties.get()这种方式来传递数据.
到此, 问题得到完美的解决.