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