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

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架构与实战,欢迎关注一起探索车载开发。”

相关文章:

  • 用Python绘制动态爱心:代码解析与浪漫编程实践
  • 驱动开发(2)|鲁班猫rk3568简单GPIO波形操控
  • 一个maven项目中直接引入两个版本的jar包
  • 15.5 【TS基础项目】构建随机密码生成器
  • IO Vs NIO
  • 数据集分享 | 塑料类型检测
  • 《P2324 [SCOI2005] 骑士精神》
  • PhysUnits】15.2 引入P1后的减一特质(sub1.rs)
  • 大模型深度学习之双塔模型
  • python 中的接口、类 、抽象类、使用场景和用法示例
  • 捋捋wireshark
  • The 2020 ICPC Asia Yinchuan Regional Programming Contest
  • Maven-生命周期
  • 排序算法-归并排序与快速排序
  • 如何避免客户频繁更换对接人
  • ASP.NET MVC添加视图示例
  • [C]基础17.自定义类型:结构体
  • MMdetection推理保存图片和预测标签脚本
  • API网关和API管理的区别
  • C++模板类深度解析与气象领域应用指南
  • 微信官网网站/友情链接免费发布平台
  • 招聘网站排行榜/惠州关键词排名优化
  • 网站首页添加浮动飘窗/百度竞价推广是什么工作
  • 南昌vi设计公司/seo是什么岗位
  • 新疆省建设厅官方网站/百度站长工具seo查询
  • 重庆建设工程质量信息网/seo和sem推广