Android 14 - HDMI_CEC架构分析-调试

时间:2025-03-05 22:13:48

1)如何调试CEC?

1、对于这种两个设备的交互 调试,需要根据协议去 确认每一个收 和 发动作;
2、打印所有的位置信息进行调试;

根据打印获取目标平台信息
02-19 17:03:47.569 V/DeviceDiscoveryAction( 1174): ---------Wrap up Device Discovery:[1]---------
02-19 17:03:47.570 V/DeviceDiscoveryAction( 1174):  DeviceInfo: CEC: logical_address: 0x05 device_type: 5 cec_version: 5 vendor_id: 41182 display_name: RX-V471 power_status: 1 physical_address: 0x1000 port_id: 1
02-19 17:03:47.570 V/DeviceDiscoveryAction( 1174):   Device features: record_tv_screen: ? set_osd_string: ? deck_control: ? set_audio_rate: ? arc_tx: ? arc_rx: ? set_audio_volume_level: ? 

2)shell/adb发送CEC命令?

1、通过service call 调用HdmiControlService的底层方法
adb shell service call hdmi_control 42 i32 0 i32 0
2、dumpsys
# 查看 HDMI-CEC 服务状态
adb shell dumpsys hdmi_control
# 强制激活系统音频模式
adb shell dumpsys hdmi_control set_system_audio_mode on
3、利用广播机制(添加debug调试语句) - java层
adb shell am broadcast -a android.intent.action.HDMI_CEC_SYSTEM_AUDIO_CONTROL_ON
4、自己写一个apk - 需要打通整个调用链路

3)局部编译

生成物路径 - 板卡路径的对应

局部编译汇总一下 - cvte.mk/Android自带的模块(framework.jar / services.jar / so / bin / ko)

https://kb.cvte.com/pages/viewpage.action?pageId=274718709

模块依赖规则是什么?如何找? >> 夹出来, 往上找(java_library没有依赖即尽头)

Android.mk
一般mk会有几个模块:share/static/xml/doc
1、系统集成static,但特权APK需要share - mk增加share编译;
2、系统出share,特权apk直接用;

//单编services
source build/envsetup.sh && lunch mt5867_eu-userdebug
make services
out/target/product/mt5867/system/framework/services.jar

Android有几类文件?>> apk/java二进制/jar
java_library/java_library_static
java_sdk_library - 相对java_library-生成api文档、api版本管理、对外暴露api-比如android.jar
java_host_library
sh_binary - shell脚本打包为/system/bin/pm


Android.bp/Android.mk(Make系统,逐渐被淘汰)的关系:
Soong(基于Blueprint和Ninja)将Android.bp->build.ninja、Android.mk->build.ninjia
同一目录下存在Android.bp/Android.mk,编译系统优先Android.bp
ninja负责执行编译
m(全编译)/mm(目录)/mmm(具体路径),后两个不会处理依赖


系统运行时,APK/JAR都会被java虚拟机分解为更快运行的模块,在线调试注意将优化文件也清掉
APK: 安装是DEX,进一步优化OAT/VDEX/ODEX
>>用户安装/data/app 
>>预装:/system/app/* /system/priv-app
JAR:
/system/framework/arm/*
/system/framework/oat/*
/data/dalvik-cache/arm/*

Tree - 架构/分层/分类
frameworks(java层的集中地-下一层对接android/system)
--base(framework.jar)
----services(单独的services.jar)
----
--hardware\interfaces(hidl)
--native
--opt
----net
----tv
--av(audio/video)
--apk/jar/test

>>>>>>>>>framework
X:\AN14\android\frameworks\base\Android.bp
java_library {
    name: "framework",  //>>架构的演变 设备安装的是framework-minus-apex,但兼容 应用编译需求,打包framework.jar
    defaults: ["framework-aidl-export-defaults"],
    installable: false, // this lib is a build-only library >> 给AndroidStudio编译用
    static_libs: [
        "app-compat-annotations", >> 提供兼容性注解
        "framework-minus-apex",  >> minux(减去),apex(如com.android.art\com.android.runtime, Android10引入)
        "framework-updatable-stubs-module_libs_api", >> 提供APEX模块的API存根
    ],
    sdk_version: "core_platform",
    apex_available: ["//apex_available:platform"],
}

实际设备安装framework用的是(轻量化,存根分布在art/runtime中),>> 打包名称仍是framework.jar
java_library {
    name: "framework-minus-apex",
}

X:\AN14\android\frameworks\base\core\api\Android.bp
package {
    default_visibility: [
        "//frameworks/base",
        "//frameworks/base/api",
}
filegroup { //子目录通过vidibility暴露给父目录/异构目录使用
	visibility: ["//frameworks/base"],
}

>>>>>>>>>services.jar
X:\AN14\android\frameworks\base\services\Android.bp
services.jar > 依赖 services.wifi.jar 那services.wifi.jar在板卡上肯定没有,可以单编出来?>> 单编会失败

// merge all required services into one jar
// ============================================================
java_library {
    name: "services",
    defaults: ["services_java_defaults"],
    installable: true,
}
>>子目录下一般都是java_library_static {}
java_library_host{} 生成不会打包Android设备,而是主机端的工具



4)打开HDMI debug

1、
mount -o remount,rw /vendor;echo log.tag.HDMI=DEBUG >> /vendor/build.prop;reboot /*enable HDMI log*/

2、对应的log语句
HdmiLogger.debug("Getting CEC setting value '" + name + "'.");

3、AML方案
1)echo 1 >/sys/class/cec/dbg_en打开驱动打印
2)echo 0f 86 10 00 > /sys/class/cec/cmd 输入相关命令


echo log.tag.HDMI=DEBUG > /data/local.prop
>>系统属性 log.tag.<TAG> >>Log.isLoggable(TAG, Log.DEBUG) 读取并返回


4、MTK抓log方式
echo log.tag.HDMI=DEBUG > /data/local.prop;chmod 644 /data/local.prop;
touch /data/tkui.print
sync
reboot

重啟後務必下完下方兩行command後再開始複製
logcat -G 64M
logcat -v time > /data/mycec.log &
dmesg --follow > /data/dmesg.log &

複製出問題, 請等待60秒 然後下
dumpsys hdmi_control > /data/hdmi_control.log

logcat | grep SystemAudioAutoInitiationAction // str(进程命令都还在) 待机也可以捉



3、touch /data/tkui.print
TvInputConst.DEBUG //log打开
private static void init() {
        File file = new File("/data/tkui.print");
        if (file.exists()) {
            DEBUG = true;
            Log.i(TvInputConst.class.getSimpleName(),  " TIS print debug log");
            return;
        }else{
            Log.i(TvInputConst.class.getSimpleName(),  " TIS don't print debug log");
        }
    }

5)堆栈

1、堆栈打印原理

1、Java 打印backtrace:  

1):Log.d(TAG,Log.getStackTraceString(new Throwable())); //android.util.Log >> 加了,验证没跑到
2):Thread.currentThread().dumpStack();  // java.lang.Thread  >> 加了,验证没跑到


3):new RuntimeException("cvte").printStackTrace();  //java.lang.RuntimeException >> 验证OK,不需要导包

实验
这个比较直接 - java.lang - 不需显示导入
Exception e = new Exception("DEBUG_HDMI");
e.printStackTrace();

02-18 18:24:45.040  1291  1291 W System.err:    at com.android.server.hdmi.SystemAudioAutoInitiationAction.handleSystemAudioModeStatusMessage(SystemAudioAutoInitiationAction.java:115)
02-18 18:24:45.040  1291  1291 W System.err:    at com.android.server.hdmi.SystemAudioAutoInitiationAction.processCommand(SystemAudioAutoInitiationAction.java:81)
>> 有时只能打印两级? 再实验就打印十多层,是当前栈环境导致?

比较有用的是在调用边界上?
>> 进程间调用?不能 只能打印同一进程中的线程栈(还要区分主线程/子线程);
>>比如多个进程调用同一个动态库,则可以了解到哪些进程调用此库接口;(进程1...进程n)->jar/so

设置栈的大小?嵌入式设备没有
java -Xss2m MyApplication
java -XX:+PrintFlagsFinal -version | grep ThreadStackSize

打印堆栈的层级如何决定?
栈是线程级,堆是进程级;
每一个进程/线程都有自己的一个栈,只能获取当前线程的栈
https://developer.huawei.com/consumer/cn/forum/topic/0201145712496750105

1、线程
返回1 (线程顶部 储存上一个线程返回点)
返回2

2、堆栈打印分析

堆栈分析
02-18 18:21:24.652 W/System.err( 1291): 	at com.android.server.hdmi.SystemAudioAutoInitiationAction.handleSystemAudioModeStatusMessage(SystemAudioAutoInitiationAction.java:115)
02-18 18:21:24.652 W/System.err( 1291): 	at com.android.server.hdmi.SystemAudioAutoInitiationAction.processCommand(SystemAudioAutoInitiationAction.java:81)
02-18 18:21:24.652 W/System.err( 1291): 	at com.android.server.hdmi.HdmiCecLocalDevice.dispatchMessageToAction(HdmiCecLocalDevice.java:411)
02-18 18:21:24.653 W/System.err( 1291): 	at com.android.server.hdmi.HdmiCecLocalDevice.onMessage(HdmiCecLocalDevice.java:298)
02-18 18:21:24.653 W/System.err( 1291): 	at com.android.server.hdmi.HdmiCecLocalDeviceTv.dispatchMessage(HdmiCecLocalDeviceTv.java:231)
02-18 18:21:24.653 W/System.err( 1291): 	at com.android.server.hdmi.HdmiControlService.dispatchMessageToLocalDevice(HdmiControlService.java:1672)
02-18 18:21:24.653 W/System.err( 1291): 	at com.android.server.hdmi.HdmiControlService.handleCecCommand(HdmiControlService.java:1646)
02-18 18:21:24.653 I/Icing   ( 4408): IndexChimeraService.getServiceInterface callingPackage=com.google.android.gms componentName=AppsCorpus serviceId=32 [CONTEXT service_id=21 ]
02-18 18:21:24.654 W/System.err( 1291): 	at com.android.server.hdmi.HdmiCecController.onReceiveCommand(HdmiCecController.java:679)
02-18 18:21:24.654 W/System.err( 1291): 	at com.android.server.hdmi.HdmiCecController.handleIncomingCecCommand(HdmiCecController.java:801)
02-18 18:21:24.654 W/System.err( 1291): 	at com.android.server.hdmi.HdmiCecController.-$$Nest$mhandleIncomingCecCommand(HdmiCecController.java:0)
02-18 18:21:24.654 W/System.err( 1291): 	at com.android.server.hdmi.HdmiCecController$HdmiCecCallback.lambda$onCecMessage$0(HdmiCecController.java:1604)
02-18 18:21:24.654 W/System.err( 1291): 	at com.android.server.hdmi.HdmiCecController$HdmiCecCallback.$r8$lambda$zN74oPJTivdwN7HPTyckyXx_D9w(HdmiCecController.java:0)
02-18 18:21:24.654 W/System.err( 1291): 	at com.android.server.hdmi.HdmiCecController$HdmiCecCallback$$ExternalSyntheticLambda0.run(R8$$SyntheticClass:0)
02-18 18:21:24.654 W/System.err( 1291): 	at com.android.server.hdmi.WorkSourceUidPreservingRunnable.run(WorkSourceUidPreservingRunnable.java:40)
02-18 18:21:24.654 W/System.err( 1291): 	at android.os.Handler.handleCallback(Handler.java:958)
02-18 18:21:24.655 W/System.err( 1291): 	at android.os.Handler.dispatchMessage(Handler.java:99)
02-18 18:21:24.655 W/System.err( 1291): 	at android.os.Looper.loopOnce(Looper.java:205)
02-18 18:21:24.658 D/TV_MtkTvInputSource( 1825): Enter getInputSourceRecbyidx, idx = 3
02-18 18:21:24.658 W/System.err( 1291): 	at android.os.Looper.loop(Looper.java:294)
02-18 18:21:24.658 W/System.err( 1291): 	at com.android.server.SystemServer.run(SystemServer.java:1002)
02-18 18:21:24.658 W/System.err( 1291): 	at com.android.server.SystemServer.main(SystemServer.java:675)
02-18 18:21:24.658 W/System.err( 1291): 	at java.lang.reflect.Method.invoke(Native Method)
02-18 18:21:24.659 W/System.err( 1291): 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
02-18 18:21:24.659 W/System.err( 1291): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1066)


WorkSourceUidPreservingRunnable 封装handle和run,保证线程在正确的环境下执行

hdmi control service 服务依附于SystemServer.main进程 - 只能观察到此进程的信息
原因?system 通过异步信息(loop监测) 来触发 handlemsg处理,因此没有更上级的调用

堆栈打印很耗时!

特殊符号 - java合成符 - 编译器生成 - 只需关注方法名即可
$$Nest$  - 嵌套类(内部类) 合成表达式
$lambda$ - Lambda合成表达式
$r8$lambda$ - R8优化的合成表达式

类的关系 - TV收发信息模型
HdmiCecController(接收) -> HdmiControlService(分发给具体设备) -> HdmiCecLocalDeviceTv / HdmiCecLocalDevice(父类方法) -> SystemAudioAutoInitiationAction (具体的action)


/android/frameworks/base/services/java/com/android/server/SystemServer.java
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_HDMI_CEC)) {
    t.traceBegin("StartHdmiControlService");
    mSystemServiceManager.startService(HdmiControlService.class);
    t.traceEnd();
}

如何拿到类的实体?
1、构造函数传进来;
2、通过目标类的单例方法;


异常的原因
1、必须要有的流程
[S] time=2025-02-18 07:52:52 message=<Give System Audio Mode Status> 05:7D
[R] time=2025-02-18 07:52:53 message=<System Audio Mode Status> 50:7E:01
[S] time=2025-02-18 07:52:53 message=<System Audio Mode Request> 05:70:00:00 //异常时没有发次消息
[R] time=2025-02-18 07:52:53 message=<Set System Audio Mode> 5F:72:01  //AVR设备也就不会去设


case Constants.MESSAGE_SYSTEM_AUDIO_MODE_STATUS:
                return "System Audio Mode Status";
                
case Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE:
                return "Set System Audio Mode";

//AN14没有跑这流程
handleSetSystemAudioMode
handleSystemAudioModeStatus