Android 14 - HDMI_CEC架构分析
文章目录
- 1、资料快车
- 2、系统架构
- 1)源码目录
- 2)官方软件架构
- 3、基本概念
- 1)术语
- 2)CEC message分析
- 3)命令码打印格式
- 4、源码记录
- 1)总体流程
- 2)接口技术
- 1、APP->CommonTIS
- 2、CommonTIS->System_services
- 3、System_service -> HAL
- 3)关键类构造函数/初始化介绍
- 4)HDMI Control Service
- 5)HDMI Control Service 类图
- 6)语法剖析
- 7)源码阅读过程
- 5、调试
- 1)如何调试CEC?
- 2)shell/adb发送CEC命令?
- 3)局部编译
- 4)打开HDMI debug
- 5)堆栈
- 1、堆栈打印原理
- 2、堆栈打印分析
1、资料快车
1、10.0 代码分析:http://www.cjcbill.com/2020/04/20/hdmi-cec/
2、HDMI-CEC控制服务(google_doc):https://source.android.google.cn/docs/devices/tv/hdmi-cec
3、CEC-总结(付费):https://blog.csdn.net/lml1010402004/article/details/106058539
4、CEC-唤醒逻辑移植(5.0):https://blog.csdn.net/lml1010402004/article/details/106058539
5、CEC-唤醒协议:https://blog.csdn.net/qq_58264156/article/details/133998009
为什么要掌握整体架构 >> 处理问题可以循序渐进一一排查,否则无法判断!
2、系统架构
1)源码目录
androd/framework/base/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
test这类代码,一般用于单元测试/CTS测试/认证测试
Application
AN14\android\vendor\mediatek\proprietary_tv\open\packages\apps\MtkTvInput >>生成物 MtkTvInput.apk
TIF
AN14\android\vendor\mediatek\proprietary_tv\open\frameworks\broadcast\CommonTIS >> 生成物com_mediatek_tvinputservice.jar
Framewrok
AN14\android\frameworks\base\services\core\java\com\android\server\hdmi >> 生成物services.jar
AN14\android\frameworks\base\core\java\android\hardware\hdmi >> 生成物framework.jar
HAL - "AIDL HAL"
AN14\android\hardware\interfaces\tv\hdmi\cec\aidl\android\hardware\tv\hdmi\cec\IHdmiCec.aidl
AN14\android\hardware\interfaces\tv\cec\1.0\IHdmiCec.hal
AN14\android\hardware\interfaces\tv\cec\1.0\default\HdmiCec.cpp >> 生成物 android.hardware.tv.cec@1.0-service
Native? >> 注意没有native
Driver
扩展:
厂商的代码为什么单独放?修改规则是?
>>新增功能往往会有大量代码? 不能直接嵌入到开源架构(开源会不断变更,方便维护) >> 单独集中放vendor目录.
>>BUG/兼容性修改,则可以直接改framework.
内核相关:
AN14\android\vendor\mediatek\proprietary_tv\apollo
系统相关:
AN14\android\vendor\mediatek\proprietary_tv\open
2)官方软件架构
3、基本概念
1)术语
1、CEC控制器是一个主芯片的一个微处理器;
2、MTK-CommonTIS ( tv input system )
4、什么是CEC MHL? 接口设备
5、功放属于哪一类device?
功放属于:DEVICE_AUDIO_SYSTEM 相关LOG:HDMI : Getting CEC setting value ‘system_audio_control’.
6、playback?播放设备,比如碟机/DVD
7、AVR : Audio Video Receiver -> 音视频设备
2)CEC message分析
AN11:
1、power on流程;
handleReportPowerStatus
>>turnOnDevice();
>>selectDevice();
点击device开机流程:
01-13 08:31:33.721 1097 1404 D HDMI : [S]:<Set Stream Path> 0F:86:30:00
01-13 08:31:33.873 1097 1404 D HDMI : [S]:<Give Device Power Status> 05:8F
01-13 08:31:34.053 1097 1097 D HDMI : [R]:<Report Power Status> 50:90:01
01-13 08:31:34.054 1097 1404 D HDMI : [S]:<User Control Pressed> 05:44 <Keycode type = Power>
01-13 08:31:34.185 1097 1404 D HDMI : [S]:<User Control Release> 05:45
01-13 08:31:34.285 1097 1404 D HDMI : [S]:<User Control Pressed> 05:44 <Keycode type = Power on>
01-13 08:31:34.405 1097 1404 D HDMI : [S]:<User Control Release> 05:45
01-13 08:31:36.813 1097 1097 D HDMI : [P]:AllocatedAddress=[5]
01-13 08:31:39.056 1097 1404 D HDMI : [S]:<Give Device Power Status> 05:8F
01-13 08:31:39.229 1097 1097 D HDMI : [R]:<Report Power Status> 50:90:00
01-13 08:31:39.231 1097 1404 D HDMI : [S]:<Set Stream Path> 0F:86:30:00
power on 开机流程:
01-13 08:55:19.100 1097 1097 D HDMI : [R]:<Report Physical Address> 5F:84:30:00:05
01-13 08:55:19.103 1097 1404 D HDMI : [S]:<Give Osd Name> 05:46
01-13 08:55:19.398 1097 1097 D HDMI : [R]:<Set Osd Name> 50:47 <Redacted len=7>
01-13 08:55:19.400 1097 1404 D HDMI : [S]:<Give Device Vendor Id> 05:8C
01-13 08:55:19.657 1097 1097 D HDMI : [R]:<Device Vendor Id> 5F:87:00:A0:DE
01-13 08:55:19.659 1097 1404 D HDMI : [S]:<Give Device Power Status> 05:8F
01-13 08:55:19.841 1097 1097 D HDMI : [R]:<Report Power Status> 50:90:01
01-13 08:55:19.843 1097 1404 D HDMI : [S]:<Give Device Power Status> 05:8F
01-13 08:55:19.941 1097 1404 D HDMI : [S]:<Give System Audio Mode Status> 05:7D
01-13 08:55:20.021 1097 1097 D HDMI : [R]:<Report Power Status> 50:90:01
01-13 08:55:20.205 1097 1097 D HDMI : [R]:<System Audio Mode Status> 50:7E:00
01-13 08:55:20.206 1097 1404 D HDMI : [S]:<System Audio Mode Request> 05:70:00:00
01-13 08:55:20.237 1097 3356 D HDMI : Getting CEC setting value 'tv_wake_on_one_touch_play'.
01-13 08:55:20.237 1097 3356 D HDMI : Reading 'tv_wake_on_one_touch_play' shared preference.
01-13 08:55:20.433 1097 1097 D HDMI : [R]:<Set System Audio Mode> 5F:72:01
01-13 08:55:20.433 1097 1097 D HDMI : System Audio Mode change[old:false new:true]
01-13 08:55:20.434 1097 1097 D HDMI : [A]UpdateSystemAudio mode[on=true] output=[2]
01-13 08:55:20.444 1097 1404 D HDMI : [S]:<Set Audio Volume Level> 05:73:7F
01-13 08:55:20.649 1097 1097 D HDMI : [R]:<Feature Abort> 50:00:73:00
3)命令码打印格式
1、
HdmiLogger.debug("[S]:" + cecMessage);
[S]:<Active Source> 0F:82:00:00
[Sender/Receiver]:<HdmiCecMessage> (source)(destination)(opcode):(params)
1、S 和 R 通常代表发送方(Sender)和接收方(Receiver):
2、ACK代表接收方已确认;
[S] time=2025-02-18 18:24:45 message=<System Audio Mode Request> 05:70:00:00 (ACK) //发送<system audio mode request>让功放开机
[R] time=2025-02-18 18:24:45 message=<Set System Audio Mode> 5F:72:01
构造函数:
HdmiCecMessage(source, destination, opcode & 0xFF, params);
static HdmiCecMessage build(int source, int destination, int opcode, byte[] params) {}
1)开机状态
[R] time=2007-01-01 15:30:23 message=<Report Power Status> 50:90:00
2)关机状态
[R] time=2007-01-01 15:30:23 message=<Report Power Status> 50:90:00
2、
AN14/android/frameworks/base/services/core/java/com/android/server/hdmi/Constants.java
X:\AN14\android\frameworks\base\services\core\java\com\android\server\hdmi\HdmiCecMessage.java
private static String opcodeToString(@FeatureOpcode int opcode) {
switch (opcode) {
case Constants.MESSAGE_FEATURE_ABORT:
return "Feature Abort";
case Constants.MESSAGE_IMAGE_VIEW_ON:
return "Image View On";
}
Constants.MESSAGE_FEATURE_ABORT对应的是opcode
3、
AN14\android\frameworks\base\services\core\java\com\android\server\hdmi\HdmiCecKeycode.java
A mapping between Android and CEC keycode.
new KeycodeEntry(KeyEvent.KEYCODE_DPAD_CENTER, CEC_KEYCODE_SELECT);
private static final KeycodeEntry[] KEYCODE_ENTRIES = new KeycodeEntry[] {
new KeycodeEntry(KeyEvent.KEYCODE_DPAD_CENTER, CEC_KEYCODE_SELECT),
new KeycodeEntry(KeyEvent.KEYCODE_DPAD_UP, CEC_KEYCODE_UP),
}
HdmiCecKeycode对应是params
4、
功放是 05
4、源码记录
1)总体流程
按键类消息:
App(MtkTvInput) -> CommonTIS(抽象出hdmi的操作如hdmi通道插入、hdmi port选择、hdmi keyevent处理)-> hdmi manager services -> hdmi cec hal
代码架构图:
Android hdmi framwork,,MTK hdmi framework,以及livetv的衔接情况。
2)接口技术
1、APP->CommonTIS
MtkTvInput -> CommonTIS.jar
2、CommonTIS->System_services
待办:
HdmiInputChangeListener机制待探究
X:\AN14\android\vendor\mediatek\proprietary_tv\open\frameworks\broadcast\CommonTIS\src\com\mediatek\tvinputservice\AbstractHdmiTvInputServiceBase.java
CommonTIS会链接framework.jar
import android.hardware.hdmi.HdmiControlManager;
private void initHdmiControl() {
HdmiControlManager mHdmiControlManager =
(HdmiControlManager) getSystemService(Context.HDMI_CONTROL_SERVICE);
}
2、Tv input system
X:\AN14\android\vendor\mediatek\proprietary_tv\open\frameworks\broadcast\CommonTIS\Android.mk
LOCAL_MODULE:= com_mediatek_tvinputservice
LOCAL_MODULE_CLASS := JAVA_LIBRARIES
LOCAL_DROIDDOC_USE_STANDARD_DOCLET := true
3、AbstractHdmiTvInputSessionImpl抽象类-> AbstractTvInputSession
private final Map<String, AbstractHdmiTvInputSessionImpl> mSessionMap =
new HashMap<String, AbstractHdmiTvInputSessionImpl>();
派生类:HdmiHardwareInputSessionImpl
mSession = new HdmiHardwareInputSessionImpl(this, inputId, mHardwareDeviceId);
4、
import android.hardware.hdmi.HdmiControlManager;
HdmiControlManager mHdmiControlManager =
(HdmiControlManager) getSystemService(Context.HDMI_CONTROL_SERVICE);
5、
private HdmiTvClient mHdmiControl = null;
import android.hardware.hdmi.HdmiTvClient;
6、
public static int TV_INPUT_TYPE_HDMI = TvInputHardwareInfo.TV_INPUT_TYPE_HDMI;
7、
X:\AN14\android\frameworks\base\core\java\android\hardware\hdmi\HdmiControlManager.java
mService
X:\AN14\android\frameworks\base\services\core\java\com\android\server\hdmi\HdmiControlService.java
3、System_service -> HAL
System_service会链接到services.jar
X:\AN14\android\frameworks\base\services\core\java\com\android\server\hdmi\HdmiCecController.java
static HdmiCecController create(HdmiControlService service, HdmiCecAtomWriter atomWriter) {
HdmiCecController controller =
createWithNativeWrapper(service, new NativeWrapperImplAidl(), atomWriter);
if (controller != null) {
return controller;
}
HdmiLogger.warning("Unable to use CEC and HDMI Connection AIDL HALs");
//
controller = createWithNativeWrapper(service, new NativeWrapperImpl11(), atomWriter);
if (controller != null) {
return controller;
}
HdmiLogger.warning("Unable to use cec@1.1");
return createWithNativeWrapper(service, new NativeWrapperImpl(), atomWriter);
}
//连接HAL层的进程服务(通过AIDL-HAL),不需要JNI (实现JAVA进程->C++进程)
boolean connectToHal() {
mHdmiCec =
IHdmiCec.Stub.asInterface(
ServiceManager.getService(IHdmiCec.DESCRIPTOR + "/default"));
}
3)关键类构造函数/初始化介绍
1、android\frameworks\base\services\core\java\com\android\server\hdmi\HdmiCecController.java
private HdmiCecController(
HdmiControlService service, NativeWrapper nativeWrapper, HdmiCecAtomWriter atomWriter) {
mService = service;
mNativeWrapperImpl = nativeWrapper;
mHdmiCecAtomWriter = atomWriter;
}
static HdmiCecController create(HdmiControlService service, HdmiCecAtomWriter atomWriter) {
HdmiCecController controller =
createWithNativeWrapper(service, new NativeWrapperImplAidl(), atomWriter);
if (controller != null) {
return controller;
}
HdmiLogger.warning("Unable to use CEC and HDMI Connection AIDL HALs");
}
2、android\frameworks\base\services\core\java\com\android\server\hdmi\HdmiControlService.java
public void onStart() {
initService();
publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService());
}
void initService() {
if (mIoLooper == null) {
mIoThread.start();
mIoLooper = mIoThread.getLooper();
}
if (mCecController == null) {
mCecController = HdmiCecController.create(this, getAtomWriter()); //构造HdmiCecController
}
if (mCecController == null) {
Slog.i(TAG, "Device does not support HDMI-CEC.");
return;
}
if (mMhlController == null) {
mMhlController = HdmiMhlControllerStub.create(this);
}
}
3、HdmiCecLocalDeviceTv.java -> HdmiCecLocalDevice.java -> HdmiLocalDevice.java
4)HDMI Control Service
消息处理主要在HDMI Control Service层处理
1、部分消息直接在HDMI Control Service <-> cec控制器完成,举个例子 TV收到,然后回复
2、按键类消息
大致流程如下:
App(MtkTvInput) -> CommonTIS(抽象出hdmi的操作如hdmi通道插入、hdmi port选择、hdmi keyevent处理)-> hdmi manager services -> hdmi cec hal -> cec driver
5)HDMI Control Service 类图
6)语法剖析
待办:
1、异步机制?
handle/message/looper
2、多线程?
runable
3、listener
@GuardedBy("mLock")
private final ArrayMap<String, RemoteCallbackList<IHdmiCecSettingChangeListener>>
mHdmiCecSettingChangeListenerRecords = new ArrayMap<>();
// 传统的匿名内部类
Runnable runnable1 = new Runnable() {
@Override
public void run() {
System.out.println("Hello World!");
}
};
// 使用 Lambda 表达式
Runnable runnable2 = () -> System.out.println("Hello World!");
@VisibleForTesting
public void onCecMessage(int initiator, int destination, byte[] body) {
runOnServiceThread(
() -> handleIncomingCecCommand(initiator, destination, body));
}
7)源码阅读过程
TV处理功放消息:
protected final int onMessage(HdmiCecMessage message) {}
@Constants.HandleMessageResult
protected int handleGiveDevicePowerStatus(HdmiCecMessage message) {
mService.sendCecCommand(
HdmiCecMessageBuilder.buildReportPowerStatus(
mDeviceInfo.getLogicalAddress(),
message.getSource(),
mService.getPowerStatus()));
return Constants.HANDLED;
}
@ServiceThreadOnly
void addAndStartAction(final HdmiCecFeatureAction action) {
assertRunOnServiceThread();
mActions.add(action);
if (mService.isPowerStandby() || !mService.isAddressAllocated()) {
Slog.i(TAG, "Not ready to start action. Queued for deferred start:" + action);
return;
}
action.start();
}
点击device_list时执行此方法?
addAndStartAction(new SendKeyAction(this, logicalAddress, keyCode));
HdmiDeviceInfo getAvrDeviceInfo() {
assertRunOnServiceThread();
return getCecDeviceInfo(Constants.ADDR_AUDIO_SYSTEM);
}
private void turnOnDevice() {
if (!mIsCec20) {
sendUserControlPressedAndReleased(mTarget.getLogicalAddress(),
HdmiCecKeycode.CEC_KEYCODE_POWER);
sendUserControlPressedAndReleased(mTarget.getLogicalAddress(),
HdmiCecKeycode.CEC_KEYCODE_POWER_ON_FUNCTION);
}
}
2、
MESSAGE_USER_CONTROL_PRESSED
3、
sendUserControlPressedAndReleased
4、
buildUserControlPressed
5、
tv() ?
mSource ?
HdmiCecFeatureAction action = new SystemAudioAutoInitiationAction(mHdmiCecLocalDeviceTv,ADDR_AUDIO_SYSTEM);
6、addAndStartAction 会调用start
@ServiceThreadOnly
void addAndStartAction(final HdmiCecFeatureAction action) {
assertRunOnServiceThread();
mActions.add(action);
if (mService.isPowerStandby() || !mService.isAddressAllocated()) {
Slog.i(TAG, "Not ready to start action. Queued for deferred start:" + action);
return;
}
action.start();
}
7、
sendGiveSystemAudioModeStatus
8、
X:\AN14\android\frameworks\base\services\core\java\com\android\server\hdmi\HdmiCecLocalDeviceTv.java
void setSystemAudioMode(boolean on) {
if (!isSystemAudioControlFeatureEnabled() && on) {
HdmiLogger.debug("Cannot turn on system audio mode "
+ "because the System Audio Control feature is disabled.");
return;
}
HdmiLogger.debug("System Audio Mode change[old:%b new:%b]",
mService.isSystemAudioActivated(), on);
updateAudioManagerForSystemAudio(on);
synchronized (mLock) {
if (mService.isSystemAudioActivated() != on) {
mService.setSystemAudioActivated(on); //决定开关机?
mService.announceSystemAudioModeChange(on);
}
if (on && !mArcEstablished) {
startArcAction(true);
} else if (!on) {
startArcAction(false);
}
}
}
9、mService?
private void updateAudioManagerForSystemAudio(boolean on) {
int device = mService.getAudioManager().setHdmiSystemAudioSupported(on);
HdmiLogger.debug("[A]UpdateSystemAudio mode[on=%b] output=[%X]", on, device);
}
X:\AN14\android\frameworks\base\services\core\java\com\android\server\hdmi\HdmiLocalDevice.java
在父类中定义
protected final HdmiControlService mService;
HdmiControlService.java
AudioManagerWrapper getAudioManager() {
return mAudioManager;
}
mAudioManager = audioManager;
AudioManagerWrapper audioManager,
X:\AN14\android\frameworks\base\services\core\java\com\android\server\hdmi\AudioManagerWrapper.java
int setHdmiSystemAudioSupported(boolean on);
/android/frameworks/base/media/java/android/media/AudioManager.java
public int setHdmiSystemAudioSupported(boolean on) {
try {
return getService().setHdmiSystemAudioSupported(on);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
10、01-13 08:55:19.941 1097 1404 D HDMI : [S]:<Give System Audio Mode Status> 05:7D
对应的函数
X:\AN14\android\frameworks\base\services\core\java\com\android\server\hdmi\SystemAudioAutoInitiationAction.java
addactionandstart(SystemAudioAutoInitiationAction.class)
private void sendGiveSystemAudioModeStatus() {
sendCommand(HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(getSourceAddress(),
mAvrAddress), new SendMessageCallback() {
@Override
public void onSendCompleted(int error) {
if (error != SendMessageResult.SUCCESS) {
handleSystemAudioModeStatusTimeout();
}
}
});
}
11、一个action对应一个java类
X:\AN14\android\frameworks\base\services\core\java\com\android\server\hdmi\DevicePowerStatusAction.java
queryDevicePowerStatus();
12、处理结果
Constants.NOT_HANDLED;
Constants.HANDLED;
5、调试
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