在進行機頂盒ROM開發時,HDMI相關功能是常見的功能模塊,本篇文章就簡單介紹一些常見的HDMI相關需求開發。
常見的HDMI相關功能可分爲三大塊:(通過HDMI線獲取/設置)分辨率、(通過HDMI線獲取)電視機信息、(HDMI)待機。
一、分辨率
機頂盒通過HDMI線與TV相連時,是通過HDMI線(獲取TV的EDID)來獲取TV支持的分辨率情況的,所以與此相關的功能有獲取/設置分辨率、初始化默認分辨率等。
1.1、獲取/設置分辨率
該功能的相關代碼代碼看上去和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在分辨率方面的代碼特徵,即把相關信息存儲在不同的節點中。
1.2、初始化默認分辨率
機頂盒設置的初始分辨率策略,一般有兩種:最優分辨率與最大分辨率。
1.2.1、最優分辨率
此處的最優並不是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。
1.2.2、最大分辨率
除了系統的默認最優分辨率策略,現在也常常使用最大分辨率策略,即自適應到當前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功能開發已介紹完畢。