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

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

http://www.kler.cn/a/573021.html

相关文章:

  • 本地部署类似 ChatGPT 的大模型:基于 Ollama + Open-WebUI
  • XTDrone+Mavros+Gazebo仿真——配置与控制不同的无人机
  • html中几个符号的转义和还原
  • LeetCode 79: 单词搜索 (Word Search)
  • C++11中atomic
  • 【SpringBoot】一文讲懂什么是scanBasePackages
  • [MySQL初阶]MySQL(3)表的约束
  • 华为最新OD机试真题-计算堆栈中的剩余数字-Python-OD统一考试(E卷)
  • C语言学习笔记-进阶(1)深入理解指针3
  • Ollama+AnythingLLM安装
  • 期权有哪些用处?期权和期货比优势在哪?
  • CentOS 7 安装 Redis6.2.6
  • R语言绘图:韦恩图
  • 06. View工作原理
  • 《HarmonyOS赋能的智能影像诊断系统安全架构与临床实践》
  • 杨辉三角解法
  • kotlin的val声明的变量是常量吗
  • vscode 都有哪些大模型编程插件
  • Raven: 2靶场渗透测试
  • 如何在Android中实现自定义视图