当前位置: 首页 > backend >正文

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)

💡 模块说明

模块文件位置作用
EventHubInputReader.cpp/EventHub.cpp/dev/input/eventX 读取事件
InputReaderInputReader.cpp解析事件、生成 MotionEvent
InputDispatcherInputDispatcher.cpp将事件派发给目标窗口(Window)

主要流程如下:

  • EventHub 使用 epoll 监听 eventX 设备;

  • 检测到事件后读入数据,传递给 InputReader;

  • InputReader 将原始事件解析为 MotionEvent;

  • InputDispatcher 根据坐标找到目标窗口,派发事件。


3.Java 层:WindowManagerService 和 View 层:

InputDispatcherWindowManagerServiceInputChannelViewRootImplDecorView / ActivityViewGroup.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

遇到一个"鸡生蛋, 还是蛋生鸡的问题"…


经过一番折腾后, 发现此路不通, 只得另寻它路.
一番冥思苦想之后, 发现我们的需求是 “在不同的进程间共享状态”, 系统提供了几种进程间共享数据的方式如下:

对比项SystemPropertiesSettings.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()这种方式来传递数据.

到此, 问题得到完美的解决.

“专注AAOS架构与实战,欢迎关注一起探索车载开发。”

http://www.xdnf.cn/news/9937.html

相关文章:

  • [git]忽略.gitignore文件
  • 软件项目需求说明书简要模板
  • 【Redis】大key问题详解
  • 【计网】分片
  • websocket在vue中的使用步骤,以及实现聊天
  • MaaS(模型即服务)是什么?
  • IT Tools 部署
  • 食材走T台?Coze+即梦应用实例:实现一键生成食材走秀视频!!(附提示词)
  • [C]基础18.自定义类型:联合和枚举
  • Python实例题:Python实现Zip文件的暴力破解
  • Spring Boot整活指南:从Helo World到“真香”定律
  • 29、请求处理-常用参数注解使用
  • UE路径追踪Path Tracing和Lumen的区别
  • Lambda表达式Stream流
  • 三套知识系统的实践比较:Notion、Confluence 与 Gitee Wiki
  • 关于 smali:2. 从 Java 到 Smali 的映射
  • 无需自建高防:APP遭遇DDoS的解决方案
  • CODEFORCES----1999A - A+B Again?
  • SQL进阶之旅 Day 7:视图与存储过程入门
  • vue的h函数(在 Vue 2中也称为 createElement)理解
  • SAP BASIS常用事务代码ST06 操作系统监控
  • UVa1384/LA3700 Interesting Yang Hui Triangle
  • OpenCv高阶(十九)——dlib关键点定位
  • 深度学习核心网络架构详解:从 CNN 到 LSTM
  • 关于DJI Cloud API Demo 终止维护公告-上云API源码停止维护
  • 文本预处理
  • 学习黑客小故事理解 Metasploit 的 Meterpreter
  • 【2025年电工杯数学建模竞赛A题】光伏电站发电功率日前预测问题+完整思路+paper+源码
  • BugKu Web渗透之备份是个好习惯
  • LeetCode Hot100(矩阵)