在进行机顶盒ROM开发时,HDMI相关功能是常见的功能模块,本篇文章就简单介绍一些常见的HDMI相关需求开发。
常见的HDMI相关功能可分为三大块:(通过HDMI线获取/设置)分辨率、(通过HDMI线获取)电视机信息、(HDMI)待机。
机顶盒通过HDMI线与TV相连时,是通过HDMI线(获取TV的EDID)来获取TV支持的分辨率情况的,所以与此相关的功能有获取/设置分辨率、初始化默认分辨率等。
该功能的相关代码代码看上去和HDMI“没多大关系”,属于Android通用性功能,其实不然,该功能与HDMI息息相关。接下来就以Hi3798MV300和Amlogic905两种平台来简单介绍。
先以Hi3798MV300为例,获取分辨率用到的接口和Android原生流程是一样的,主要来自于frameworks/base/core/java/android/os/display/DisplayManager.java,关键接口如下:
/*所有分辨率*/
private int[] mAllDisplayStandard = {
DISPLAY_STANDARD_1080P_60,
DISPLAY_STANDARD_1080P_50,
DISPLAY_STANDARD_1080P_30,
DISPLAY_STANDARD_1080P_25,
DISPLAY_STANDARD_1080P_24,
DISPLAY_STANDARD_1080I_60,
DISPLAY_STANDARD_1080I_50,
DISPLAY_STANDARD_720P_60,
DISPLAY_STANDARD_720P_50,
DISPLAY_STANDARD_576P_50,
DISPLAY_STANDARD_480P_60,
DISPLAY_STANDARD_PAL,
DISPLAY_STANDARD_NTSC,
DISPLAY_STANDARD_3840_2160P_24,
DISPLAY_STANDARD_3840_2160P_25,
DISPLAY_STANDARD_3840_2160P_30,
DISPLAY_STANDARD_3840_2160P_50,
DISPLAY_STANDARD_3840_2160P_60,
DISPLAY_STANDARD_4096_2160P_24,
DISPLAY_STANDARD_4096_2160P_25,
DISPLAY_STANDARD_4096_2160P_30,
DISPLAY_STANDARD_4096_2160P_50,
DISPLAY_STANDARD_4096_2160P_60,
};
/*DisplayManager构造函数,该函数中会初始化当前TV所支持分辨率数组mStandard*/
public DisplayManager(IDisplayManager server) {
mdisplay = server;
//see display.c::get_hdmi_capability
// hisi format value fmt cap index
mMapEncFmtToIndex.put(ENC_FMT_1080P_60 , 1 );
mMapEncFmtToIndex.put(ENC_FMT_1080P_50 , 2 );
mMapEncFmtToIndex.put(ENC_FMT_1080P_30 , 3 );
mMapEncFmtToIndex.put(ENC_FMT_1080P_25 , 4 );
mMapEncFmtToIndex.put(ENC_FMT_1080P_24 , 5 );
mMapEncFmtToIndex.put(ENC_FMT_1080i_60 , 6 );
mMapEncFmtToIndex.put(ENC_FMT_1080i_50 , 7 );
mMapEncFmtToIndex.put(ENC_FMT_720P_60 , 8 );
mMapEncFmtToIndex.put(ENC_FMT_720P_50 , 9 );
mMapEncFmtToIndex.put(ENC_FMT_576P_50 , 10);
mMapEncFmtToIndex.put(ENC_FMT_480P_60 , 11);
mMapEncFmtToIndex.put(ENC_FMT_PAL , 12);
mMapEncFmtToIndex.put(ENC_FMT_NTSC , 15);
mMapEncFmtToIndex.put(ENC_FMT_3840X2160_24 , 44);
mMapEncFmtToIndex.put(ENC_FMT_3840X2160_25 , 45);
mMapEncFmtToIndex.put(ENC_FMT_3840X2160_30 , 46);
mMapEncFmtToIndex.put(ENC_FMT_3840X2160_50 , 47);
mMapEncFmtToIndex.put(ENC_FMT_3840X2160_60 , 48);
mMapEncFmtToIndex.put(ENC_FMT_4096X2160_24 , 49);
mMapEncFmtToIndex.put(ENC_FMT_4096X2160_25 , 50);
mMapEncFmtToIndex.put(ENC_FMT_4096X2160_30 , 51);
mMapEncFmtToIndex.put(ENC_FMT_4096X2160_50 , 52);
mMapEncFmtToIndex.put(ENC_FMT_4096X2160_60 , 53);
try{
int[] dispCapability = mdisplay.getDisplayCapability();
for(int i = 0; i < dispCapability.length; i++){
Log.d("DisplayManager.java", "dispCapability[" + i + "]=" + dispCapability[i]);
}
/*过滤分辨率列表,把当前TV支持的分辨率赋值给mStandard*/
if(dispCapability != null){
int supportFmtCnt = 0;
int[] supportFmt = new int[mAllDisplayStandard.length];
for(int i = 0; i < mAllDisplayStandard.length; i++){
if(dispCapability[mMapEncFmtToIndex.get(covertCMCCFmtToHisi(mAllDisplayStandard[i]))] == 1){
supportFmt[supportFmtCnt] = mAllDisplayStandard[i];
supportFmtCnt++;
Log.d("DisplayManager.java", "supportFmt:" + mAllDisplayStandard[i]);
}
Log.d("DisplayManager.java", "supportFmtCnt:" + supportFmtCnt);
}
mStandard = new int[supportFmtCnt];
System.arraycopy(supportFmt, 0, mStandard, 0, supportFmtCnt);
}else{
mStandard = new int[0];
}
}
catch(Exception ex){
mStandard = new int[0];
}
}
/*获取当前TV支持的所有分辨率*/
public int[] getAllSupportStandards() {
return mStandard;
}
/*是否支持某一分辨率*/
public boolean isSupportStandard(int standard) {
boolean ret = false;
for (int i = 0; i < mStandard.length; i++) {
if(standard == mStandard[i]){
ret = true;
break;
}
}
return ret;
}
/*设置某一分辨率*/
public void setDisplayStandard(int standard) {
int hisiFmt = -1;
int ret = -1;
/*设置分辨率前,要检测当前TV是否支持该分辨率*/
if(isSupportStandard(standard)){
try {
hisiFmt = covertCMCCFmtToHisi(standard);
if (hisiFmt >= ENC_FMT_1080P_60) {
ret = mdisplay.setFmt(hisiFmt);
}
} catch(Exception ex) {
Log.e(TAG,"setDisplayStandard: " + ex);
}
} else {
Log.e(TAG, "setDisplayStandard: unsupport(" + standard + ")");
}
Log.i(TAG, "setDisplayStandard: standard=" + standard + ", ret=" + ret);
}
/*获取当前分辨率*/
public int getCurrentStandard() {
int hisiFmt = -1;
int cmccFmt = -1;
try {
hisiFmt = mdisplay.getFmt();
cmccFmt = covertHisiFmtToCMCC(hisiFmt);
} catch (RemoteException e) {
Log.e(TAG, "getCurrentStandard: " + e);
}
if(isSupportStandard(cmccFmt)){
return cmccFmt;
} else {
Log.e(TAG, "getCurrentStandard: CMCC unsupport(" + hisiFmt + ")");
return -1;
}
}
在Hi3798MV300上,使用以上接口,再结合一些其他的HDMI功能,就可以实现一个较为复杂的需求,比如:将机顶盒上的HDMI线连接到某一TV,判断该TV是否支持4K分辨率,如果支持,则切换到4K分辨率,否则不处理。关键代码示例如下:
/*检测、设置4K分辨率*/
private BroadcastReceiver mHdmiReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
/*监听HDMI插拔广播*/
if (action.equals("android.intent.action.HDMI_PLUGGED")) {
boolean state = intent.getBooleanExtra("state", false);
Log.d(TAG,"HDMI status:"+state+",getHdmiSwitchSet():"+getHdmiSwitchSet());
if (state && getHdmiSwitchSet()) {
checkTVResolution();
}
}
}
};
/*获取当前TV所支持的所有分辨率,如果支持4K,则设置为2060P50hz*/
private void checkTVResolution(){
int DISPLAY_STANDARD_3840_2160P_24 = 256;
boolean is4kResolutionSupported = false;
int[] supportList = mDisplayManager.getAllSupportStandards();
Arrays.sort(supportList);
for(int i=0;i<supportList.length;i++){
Log.d(TAG,"supportList["+i+"]:"+supportList[i]);
/*所支持的分辨率列表中包含大于DISPLAY_STANDARD_3840_2160P_24的数值,则代表支持2160P,即假4K分辨率*/
if(supportList[i] >= DISPLAY_STANDARD_3840_2160P_24){
is4kResolutionSupported = true;
break;
}
}
if(is4kResolutionSupported){
int best4kResolution = 260;
if(supportList[supportList.length-1] >= 260)
best4kResolution = 260;
mDisplayManager.setDisplayStandard(best4kResolution);
mDisplayManager.saveParams();
}
}
/*检测HDMI插拔状态*/
private static boolean getHdmiSwitchSet() {
File switchFile = new File("/sys/devices/virtual/switch/hdmi/state");
if (!switchFile.exists()) {
switchFile = new File("/sys/class/switch/hdmi/state");
}
try {
Scanner switchFileScanner = new Scanner(switchFile);
int switchValue = switchFileScanner.nextInt();
switchFileScanner.close();
return switchValue > 0;
} catch (Exception e) {
return false;
}
}
在Amlogic905上,用的不是原生的DisplayManager来处理分辨率,有一套别的实现方式,主要实现代码在frameworks/base/services/java/com/android/server/MboxOutputModeService.java,关键接口如下:
/*获取当前TV支持的分辨率列表,供上层调用*/
public String getSupportResoulutionList() {
if (isHDMIPlugged()) {
ArrayList<OutputMode> mOutputModeList = readSupportList();
Slog.w(TAG, "getSupportResoulutionList error, output list is null!");
return null;
}
}
/*读取当前TV支持的分辨率列表*/
private ArrayList<OutputMode> readSupportList() {
String str = null;
ArrayList<OutputMode> mOutputModeList = new ArrayList<OutputMode>();
try {
/*从相关节点读取分辨率*/
FileReader fr = new FileReader(HDMI_SUPPORT_LIST_SYSFS);//HDMI_SUPPORT_LIST_SYSFS = /sys/class/amhdmitx/amhdmitx0/disp_cap
BufferedReader br = new BufferedReader(fr);
try {
while ((str = br.readLine()) != null) {
if(str != null){
//if(DEBUG) Slog.i(TAG, "Output: " + str);
boolean filter = false;
OutputMode output = new OutputMode();
if(str.contains("null edid")) {
Slog.w(TAG, "readSupportList error, disp_cap: " + str);
return null;
}
if(str.contains("*")) {
output.mode = new String(str.substring(0, str.length()-1));
output.isBestMode = true;
} else {
output.mode = new String(str);
output.isBestMode = false;
}
//if(DEBUG) Slog.i(TAG, "readSupportList, Output: " + output.mode + ", isBestMode: " + output.isBestMode);
if(isOutputFilter(output.mode)) {
Slog.w(TAG, "readSupportList, filter this mode: " + output.mode);
} else {
mOutputModeList.add(output);
}
}
};
fr.close();
br.close();
return resolutionSort(mOutputModeList);
} catch (IOException e) {
e.printStackTrace();
return null;
}
} catch (FileNotFoundException e) {
e.printStackTrace();
return null;
}
}
/*获取当前分辨率*/
public String getCurrentOutPutMode() {
String curMode = readSysfs(OutputModeFile);//OutputModeFile = "/sys/class/display/mode"
Slog.e(TAG,"getCurrentOutPutMode:" + curMode);
return curMode;
}
从上面的代码可以大致看出Amlogic905在分辨率方面的代码特征,即把相关信息存储在不同的节点中。
机顶盒设置的初始分辨率策略,一般有两种:最优分辨率与最大分辨率。
此处的最优并不是TV支持的最大分辨率,而是ROM评估出来的、适合的一个分辨率,一般为720P或1080P,该策略一般也是ROM中的默认设置。
先以Hi3798MV300为例,该策略的主要实现代码在device/hisilicon/bigfish/frameworks/hidisplaymanager/hal/hi_adp_hdmi.c,以Hdmicap_NativeFormat_Strategy方法为例,关键代码如下:
else if (strcmp("p50hz", perfer) == 0)
{
if (HI_TRUE == is_format_support(&stSinkCap,HI_UNF_ENC_FMT_4096X2160_50))
{
stSinkCap.enNativeFormat = HI_UNF_ENC_FMT_4096X2160_50;
}
else if (HI_TRUE == is_format_support(&stSinkCap,HI_UNF_ENC_FMT_4096X2160_25))
{
stSinkCap.enNativeFormat = HI_UNF_ENC_FMT_4096X2160_25;
}
//since tv capability is upgrade now , add max fmt lever to 3840X2160 P50
else if (HI_TRUE == is_format_support(&stSinkCap,HI_UNF_ENC_FMT_3840X2160_50))
{
stSinkCap.enNativeFormat = HI_UNF_ENC_FMT_3840X2160_50;
}
else if (HI_TRUE == is_format_support(&stSinkCap,HI_UNF_ENC_FMT_3840X2160_25))
{
stSinkCap.enNativeFormat = HI_UNF_ENC_FMT_3840X2160_25;
}
else if (HI_TRUE == is_format_support(&stSinkCap,HI_UNF_ENC_FMT_1080P_50))
{
stSinkCap.enNativeFormat = HI_UNF_ENC_FMT_1080P_50;
}
else if (HI_TRUE == is_format_support(&stSinkCap,HI_UNF_ENC_FMT_720P_50))
{
stSinkCap.enNativeFormat = HI_UNF_ENC_FMT_720P_50;
}
else //if(stSinkCap.enNativeFormat = HI_UNF_ENC_FMT_576P_50)
{
ALOGI("Lowest default to 576p");
stSinkCap.enNativeFormat = HI_UNF_ENC_FMT_576P_50;
}
}
该段代码中,is_format_support函数是芯片的最优分辨率策略实现函数,一般当persist.sys.optimalfmt.perfer设置为"p50hz"时,默认分辨率是720P50hz。
Amlogic905的最优分辨率的实现方式也较为底层,framework层代码(frameworks/base/services/java/com/android/server/MboxOutputModeService.java)是根据节点的值来实现的,具体如下:
/*获取最适合分辨率*/
public String getBestMatchResolution() {
/*获取支持的分辨率列表*/
ArrayList<OutputMode> mOutputModeList = readSupportList();
if (mOutputModeList != null && isHDMIPlugged()){
int size = mOutputModeList.size();
if(DEBUG) Slog.i(TAG, "getBestMatchResolution, output size: " + size);
for (int index = 0; index < size; index++) {
OutputMode output = mOutputModeList.get(index);
if (DEBUG) Slog.i(TAG,"getBestMatchResolution, output: " + output.mode + " isBestMode: " + output.isBestMode);
if (output.isBestMode) {
Slog.i(TAG, "getBestMatchResolution, return best mode: " + output.mode);
return output.mode;
}
}
}else if(!isHDMIPlugged()){
/*插AV线时返回固定分辨率*/
return "576cvbs";
}
/*以上读取均出问题时,采用默认分辨率*/
String default_mode = getPropertyString("ro.platform.best_outputmode", DEFAULT_OUTPUT_MODE);
Slog.w(TAG, "getBestMatchResolution, return defalut outputmode: " + default_mode);
return default_mode;
}
Amlogic905方案中,当前TV所支持的分辨率列表是写在/sys/class/amhdmitx/amhdmitx0/disp_cap节点的,该节点值示例如下:
480i60hz
480p60hz
576i50hz
576p50hz
720p60hz
1080i60hz
1080p60hz*
720p50hz
1080i50hz
1080p50hz
1080p24hz
从上述内容可以看出所有支持的分辨率。其中后面加"*"符号的分辨率(该例子中的1080p60hz)就是系统默认的最适合分辨率,对应到代码中就是该分辨率对应的output.isBestMode为true。
除了系统的默认最优分辨率策略,现在也常常使用最大分辨率策略,即自适应到当前TV支持的最大分辨率,该功能主要针对的是4K电视,在连接TV时,可以自适应到4K分辨率。
先以Hi3798MV300为例,自适应到最大分辨率,设置两个属性即可:
persist.sys.optimalfmt.enable=1
persist.sys.optimalfmt.perfer=max_fmt
persist.sys.optimalfmt.enable属性代表的意思是分辨率自适应开关,当该属性设置为1时,代表自适应分辨率功能打开;设置为0时,代表关闭。persist.sys.optimalfmt.perfer属性设置为max_fmt,即代表自适应到最高分辨率。该功能涉及的代码为device/hisilicon/bigfish/frameworks/hidisplaymanager/hal/hi_adp_hdmi.c,相关代码如下:
/*persist.sys.optimalfmt.perfer属性值为max_fmt时,获取最大分辨率*/
else if (0 == strcmp("max_fmt", perfer))
{
stSinkCap.enNativeFormat = getCurrentMaxSupportFmt();
}
/*获取最大分辨率*/
HI_S32 getCurrentMaxSupportFmt()
{
HI_S32 listCount = 0;
HI_S32 MaxFmtListLen = 0;
HI_BOOL bSupport = HI_FALSE;
MaxFmtListLen = sizeof(HDMI_TV_MAX_SUPPORT_FMT) / sizeof(HDMI_TV_MAX_SUPPORT_FMT[0]);
ALOGI("MaxFmtListLen = %d", MaxFmtListLen);
for (listCount = 0; listCount < MaxFmtListLen; listCount++)
{
if (HI_TRUE == is_format_support(&stSinkCap,HDMI_TV_MAX_SUPPORT_FMT[listCount]))
{
ALOGI("max support fmt is:%d",HDMI_TV_MAX_SUPPORT_FMT[listCount]);
return HDMI_TV_MAX_SUPPORT_FMT[listCount];
}
}
ALOGI("Can't Find Max Support Format, getCurrentMaxFormat return:720P_50 !");
return HI_UNF_ENC_FMT_720P_50;
}
在Hi3798MV300上,除了直接设置上述两个属性外,还可以在上层应用实现自适应4K功能,相关代码参考1.1章节。
在Amlogic905上,可以在frameworks/base/services/java/com/android/server/MboxOutputModeService.java中的getBestMatchResolution中直接遍历所有分辨率,返回最大分辨率即可。
除了第一节提到的分辨率相关功能,还可以获取到TV相关信息,如型号、品牌等。
以Hi3798MV300为例,对于TV信息的获取是在底层做的,对于的代码为device/hisilicon/bigfish/frameworks/hidisplaymanager/hal/hi_adp_hdmi.c,关键代码如下:
void setTVproperty(display_format_e format)
{
int w = 0;
int h = 0;
int newhdrsuport = 2;
int oldhdrsport = 2;
char hei[5] ={0};
char dpi[15] = {0};
char size[10] = {0};
char buffer[BUFLEN] = {0};
framebuffer_get_max_screen_resolution(format,&w,&h);
ALOGI("format %d , w: %d ,h: %d,",format, w, h);
sprintf(dpi, "%d", w);
strcat(dpi,"*");
sprintf(hei, "%d", h);
strcat(dpi,hei);
ALOGI("dpi: %s", dpi);
sprintf(size,"%d",(int)(sqrt(TVHMax*TVHMax +TVWMax*TVWMax)/2.54 +0.5));
ALOGE("TVWidth:%d,Height:%d,TVSzie:%s", TVWMax, TVHMax, size);
property_set("persist.sys.tv.name",stSinkCap.stMfrsInfo.u8MfrsName); //代表电视机的品牌
property_set("persist.sys.tv.type",stSinkCap.stMfrsInfo.u8pSinkName); //代表电视机的具体型号
property_set("persist.sys.tv.size",size); //代表电视机的尺寸,即电视机对角线长度
property_set("persist.sys.tv.dpi",dpi); //代表电视机DPI,即每英寸像素点数
memset(buffer, 0, sizeof(buffer));
property_get("persist.sys.tv.Supporthdr", buffer , "2");
oldhdrsport = atoi(buffer);
if(HI_TRUE == stSinkCap.bHdrSupport && stSinkCap.stHdr.stEotf.bEotfSmpteSt2084)
newhdrsuport = 1;//yes 1
else if (HI_FALSE == stSinkCap.bHdrSupport)
newhdrsuport = 2;//no 2
else
newhdrsuport = 0;//other 0
//HSCP2018042722609
if(newhdrsuport != oldhdrsport)
{
sprintf(buffer, "%d", newhdrsuport);
property_set("persist.sys.tv.Supporthdr",buffer);
}
}
在Amlogic905上,电视机相关的信息是写在节点sys/class/amhdmitx/amhdmitx0/edid里的,示例内容如下:
Rx Brand Name: PHL
Rx Product Name: PHILIPS
Physcial size(cm): 52 x 29
Manufacture Week: 1
Manufacture Year: 2015
EDID Verison: 1.3
EDID block number: 0x1
blk0 chksum: 0x2c
Source Physical Address[a.b.c.d]: 1.0.0.0
native Mode f1, VIC (native 16):
ColorDeepSupport 2
31 16 20 5 19 4 2 3 32 22 18 6 7 1
Audio {format, channel, freq, cce}
{1, 1, 7, 7}
{10, 7, 6, 0}
Speaker Allocation: 1
Vendor: 0xc03
MaxTMDSClock1 290 MHz
SCDC: 0
RR_Cap: 0
LTE_340M_Scramble: 0
checkvalue: 0x2cc80000
Rx Brand Name代表的是电视机品牌;Rx Product Name代表的是电视机型号;Physcial size(cm)代表的是电视机长宽。
此处主要的指的是"HDMI待机"功能,即电视机关机,机顶盒也跟着待机。
在Hi3798MV300上,一般设置两个属性即可:
persist.hdmi.suspend.enable = 1
persist.hdmi.suspend.time = 5
persist.hdmi.suspend.enable属性值代表HDMI待机开关,0代表关闭,1代表打开。persist.hdmi.suspend.time属性值代表待机时间,即电视机关机后多久,机顶盒进入待机,单位为分钟。该功能的实现也在device/hisilicon/bigfish/frameworks/hidisplaymanager/hal/hi_adp_hdmi.c,待机代码如下:
void HDMI_Suspend_Timeout()
{
ALOGI("HDMI_Suspend_Timeout: hdmi connect status flag hdmi_enable =%d",hdmi_enable);
char buffer[BUFLEN] = {0};
HI_UNF_HDMI_STATUS_S hdmiStatus;
//icsl init
hdmiStatus.bConnected = HI_FALSE;
HI_U8 is_under_cec_suspend = get_HDMI_CEC_Suspend_Status();
HI_UNF_HDMI_GetStatus(0,&hdmiStatus);
ALOGI("hdmiStatus.bConnected =%d",hdmiStatus.bConnected);
//hdmi uplug or hdmi rsen disconnect event suspend time out, send a power key to system
//end by lizheng 20190326 to solve 32a suspend
HDMI_Suspend_ReportKeyEvent(KEY_POWER, KPC_EV_KEY_PRESS);
HDMI_Suspend_ReportKeyEvent(KEY_POWER, KPC_EV_KEY_RELEASE);
ALOGW("\033[31mHDMI_Suspend_Timeout: send power key to suspend \33[0m\n");
property_get("ro.product.target", buffer, "0");
if(strcmp(buffer,"shcmcc")==0)
{
sleep(4);//unit : second
HDMI_Suspend_ReportKeyEvent(KEY_RIGHT, KPC_EV_KEY_PRESS);
HDMI_Suspend_ReportKeyEvent(KEY_RIGHT, KPC_EV_KEY_RELEASE);
sleep(2);
HDMI_Suspend_ReportKeyEvent(KEY_ENTER, KPC_EV_KEY_PRESS);
HDMI_Suspend_ReportKeyEvent(KEY_ENTER, KPC_EV_KEY_RELEASE);
}
}
从上面代码可以看出,进行机顶盒待机功能是通过模拟电源键按键操作来实现的。
在Amlogic905上,HDMI待机功能是在frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java中实现的。简单流程为,在init函数中,初始化HDMI相关的信息,代码如下:
void initializeHdmiState() {
boolean plugged = false;
// watch for HDMI plug messages if the hdmi switch exists
if (new File("/sys/class/switch/hdmi_hpd/state").exists()) {
if (SystemProperties.getBoolean("ro.platform.has.mbxuimode", false)){
SystemProperties.set("sys.boot.logo", "android");
if(SystemProperties.getBoolean("ro.hw.cvbs.onboard", true) && !SystemProperties.getBoolean("ro.hdmiplugdetect.dis", false)){
mMboxOutputModeManager.initOutputMode();
mHDMIObserver.startObserving(HDMI_TX_PLUG_UEVENT);
}
} else {
mHDMIObserver.startObserving(HDMI_TX_PLUG_UEVENT);
}
final String filename = "/sys/class/switch/hdmi_hpd/state";
FileReader reader = null;
try {
reader = new FileReader(filename);
char[] buf = new char[15];
int n = reader.read(buf);
if (n > 1) {
plugged = 0 != Integer.parseInt(new String(buf, 0, n-1));
}
} catch (IOException ex) {
Slog.w(TAG, "Couldn't read hdmi state from " + filename + ": " + ex);
} catch (NumberFormatException ex) {
Slog.w(TAG, "Couldn't read hdmi state from " + filename + ": " + ex);
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException ex) {
}
}
}
}
mHdmiHwPlugged = plugged;
if (!SystemProperties.getBoolean("ro.vout.dualdisplay", false)) {
if (getCurDisplayMode().equals("panel") || !plugged || SystemProperties.getBoolean("ro.platform.has.mbxuimode", false)) {
plugged = false;
}
}
if (SystemProperties.getBoolean("ro.vout.dualdisplay", false)) {
setDualDisplay(plugged);
}
if (SystemProperties.getBoolean("ro.vout.dualdisplay2", false)) {
plugged = false;
setDualDisplay(plugged);
}
Intent it = new Intent(WindowManagerPolicy.ACTION_HDMI_PLUGGED);
it.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
it.putExtra(WindowManagerPolicy.EXTRA_HDMI_PLUGGED_STATE, plugged);
mContext.sendStickyBroadcastAsUser(it, UserHandle.OWNER);
}
此函数中有mHDMIObserver.startObserving(HDMI_TX_PLUG_UEVENT),开始监听了HDMI的状态, UEventObserver代码如下:
private UEventObserver mHDMIObserver = new UEventObserver() {
@Override
public void onUEvent(UEventObserver.UEvent event) {
Log.d(TAG , "mHDMIObserver");
setHdmiHwPlugged("1".equals(event.get("SWITCH_STATE")));
}
};
setHdmiHwPlugged中有段关键代码为:
if(SystemProperties.getBoolean("persist.sys.autosuspend.hdmi", false)) {
if (plugged && !isTvSuspend) {
disableAutoSuspend();
} else {
enableAutoSuspend();
}
}
persist.sys.autosuspend.hdmi属性为HDMI待机开关,然后当判断HDMI线拔出时,就调用enableAutoSuspend进入待机流程,代码如下:
public void enableAutoSuspend() {
disableAutoSuspend();
int def_timeout = 2 * 60 * 1000; //default 2min
if (timeout > 0) {
Slog.d(TAG, "enable auto suspend");
mAutoSuspendTimer = new Timer();
TimerTask task = new TimerTask(){
public void run() {
Slog.d(TAG, "goto auto suspend");
String proj_type = SystemProperties.get("sys.proj.type", "ott");
String tender_type = SystemProperties.get("sys.proj.tender.type", null);
Log.i(TAG, "Auto suspend sys.proj.type: " + proj_type + ", sys.proj.tender.type: " + tender_type);
if ("telecom".equals(proj_type) && "jicai".equals(tender_type)) {
sendKeyEvent(KeyEvent.KEYCODE_HOME);
}
String rx_sense = mSystemWriteManager.readSysfs("/sys/class/switch/hdmi_rxsense/state");
if("0".equals(rx_sense))
mPowerManager.goToSleep(SystemClock.uptimeMillis());
}
};
mAutoSuspendTimer.schedule(task, timeout);
isTvSuspend = false;
}
}
至此,常用HDMI功能开发已介绍完毕。