天天看點

【RK3288 Android 7.1 / KEN】雙屏異顯流程異顯功能使能觸發異顯異顯流程

RK3288_Android 7.1 雙屏異顯流程

  • 異顯功能使能
        • 控制代碼位置:
  • 觸發異顯
        • 控制代碼位置:
            • 方法 1 主要流程:
            • 方法 2 關鍵步驟:
  • 異顯流程
        • 控制代碼位置:
        • setOnlyShowInExtendDisplay() 主要流程:
          • 1
          • 2
          • 3
          • 4
        • moveTransitionToSecondDisplay() 主要流程:
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14

異顯功能使能

—— 即控制 異顯功能 能否 使用,以及異顯功能是否 啟用

控制代碼位置:

packages/apps/Settings/src/com/android/settings/HdmiSettings.java

  • 屬性 1:

    android.provider.Settings.DUAL_SCREEN_MODE

    異顯功能全局開關 —— 0 表示 關閉;1 表示 打開
  • 屬性 2:

    android.provider.Settings.DUAL_SCREEN_ICON_USED

    異顯功能狀态 —— 0 表示 同顯;1 表示 異顯

以上部分并不是雙屏異顯的關鍵

觸發異顯

控制代碼位置:

framework/base/core/java/com/android/internal/policy/DecorView.java

  • 關鍵方法 1:

    initDualScreenConfig()

    進行異顯觸發配置的初始化

方法 1 主要流程:

  1. String dualScreenKeyCodes = SystemProperties.get("sys.dual_screen.keycodes");

    擷取 sys.dual_screen.keycodes 屬性值(該屬性值标記 異顯觸發鍵)

    該屬性值預設是 24,25,分别對應 音量鍵 + 和 音量鍵 -

  2. mDualScreenCodeInterval = SystemProperties.getLong("sys.dual_screen.interval", 2000L);

    擷取 sys.dual_screen_interval 屬性值(該屬性标記 觸發時間間隔)
  • 關鍵方法 2:

    trigerDualScreen()

    觸發異顯

方法 2 關鍵步驟:

  1. getRootWindowSession().setOnlyShowInExtendDisplay(getWindow(),-1);

    通過 Session 調用 WMS(WindowManagerService) 的 setOnlyShowInExtendDisplay 方法,進入異顯流程
  • 關鍵方法 3:

    trigerSyncScreen()

    觸發同顯

    (暫時不展開)

異顯流程

主要就是上一環節中調用的 WMS 的 setOnlyShowInExtendDisplay

控制代碼位置:

frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

setOnlyShowInExtendDisplay() 主要流程:

1
if (mDisplayContents == null || mDisplayContents.size() <= 1) {
    return;
}
           
  • 如果展示内容為 null 或小于等于 1 時,退出函數
  • 通常為副屏不存在時才會發生上述情況(即隻有一個屏)
2
final int displayCount = mDisplayContents.size();
DisplayContent defaultContent = getDefaultDisplayContentLocked();
int displayId = 0;
DisplayContent secondDisplayContent = null;
for (int i = 0; i < displayCount;i++) {
	final DisplayContent content = mDisplayContents.valueAt(i);
	if (content != defaultContent) {
		secondDisplayContent = content;
		displayId = secondDisplayContent.getDisplayId();
		break;
	}
}
           
3
if(secondDisplayContent == null){
	return;
}
if(!okToDisplay()){
	return;
}
WindowState current = windowForClientLocked(session, client, false); 
if(isHomeWindow(current)){
	return;
}
AppWindowToken wtoken = current.mAppToken;
if(wtoken == null){
	return;
}
           
  • 如果存在副屏需要顯示的内容為 null,副屏未就緒(包括副屏未開啟、處于 Frozen 狀态等)或主屏視窗處于 Home 等情況時,return 退出方法,結束異顯流程
4
int groupId = wtoken.mTask.mTaskId;
mH.sendMessage(mH.obtainMessage(H.DO_TASK_DISPLAY_CHANGED, groupId, -1));
           
  • 擷取主屏目前應用的 taskId
  • 發送 “DO_TASK_DISPLAY_CHANGED” Message
  • 随後 handlerMessage 方法接收到 Message 之後,調用 moveTransitionToSecondDisplay() 方法

moveTransitionToSecondDisplay() 主要流程:

1
if (!isShowDualScreen()) {
	mSecondTaskIds.clear();
} else {
    if (mSecondDisplayTaskId != -1) {
		return;
    }
}
           
  • if 語句判斷是否處于異顯狀态
  • 當不處于異顯狀态時,先清空副屏的 mSecondTaskIds 清單,準備接收副屏需要顯示的内容
  • 當處于異顯狀态時,執行 else 語句,若判斷副屏的 taskId 不等于 -1,即任務棧正常,則 return 退出方法,保持異顯的狀态
2
  • 先将 DUAL_SCREEN_ICON_USED 屬性值設定為同顯狀态
3
List<Integer> allTaskIds = null;
try {
	allTaskIds = mActivityManager.getAllTaskIds();
} catch (Exception e) {
	if(DEBUG) Log.i(TAG_DUALSCREEN, "WindowManagerService->getAllTaskIds->e:" + e);
}

if (allTaskIds == null || allTaskIds.size() < 2)
	return;
           
  • 通過 ActivityManager 擷取所有任務的 taskId
  • if 語句判斷 allTaskIds 為 null 或者 allTaskIds 任務 Id 數小于 2 時,return 退出方法
4
if(mDisplayContents == null || mDisplayContents.size() <= 1){
	return;
}
final int displayCount = mDisplayContents.size();
DisplayContent defaultContent = getDefaultDisplayContentLocked();
int displayId = 0;
DisplayContent secondDisplayContent = null;
for(int i = 0; i < displayCount;i++){
	final DisplayContent content = mDisplayContents.valueAt(i);
	if(content != defaultContent){
	    secondDisplayContent = content;
        displayId = secondDisplayContent.getDisplayId();
		if(DEBUG) Log.d(TAG_DUALSCREEN, "moveTransitionToSecondDisplay->secondDisplayId:" + displayId);
		break;
    }
}
if(secondDisplayContent == null){
	return;
}
if(!okToDisplay()){
	return;
}
           
  • 這部分跟 setOnlyShowInExtendDisplay() 方法的内容類似
5
SurfaceControl.openTransaction();
WindowState win = null;
WindowList defaultWindows = defaultContent.getWindowList();
           
  • openTransaction() 和 endTransaction() 之間是對 Surface 的操作
  • 擷取主屏的 windows 清單(主屏所有的 windows)
6
int topTaskId = -1;
if (allTaskIds != null && allTaskIds.size() > 0) {
	topTaskId = allTaskIds.get(0);
	mSecondTaskIds.add(topTaskId);
}
           
  • 擷取頂層應用的 taskId,添加到 mSecondTaskIds 清單中
7
for(int i= defaultWindows.size()-1;i>=0;i--){
	win = defaultWindows.get(i);
					
	if(win == null){
		continue;
	}
	if (win.mAppToken == null){
		continue;
	}
    boolean isSurface=false;
	int windowTaskId=-1;
    if(win.taskId==-1&&win.mAttachedWindow!=null && win.mAttachedWindow.mAppToken.mTask.mTaskId==topTaskId){
		isSurface=true;
        Log.i("DualScreenIs","isSurface = "+isSurface);
	} else if(win.mAppToken.mTask == null){
		continue;
	}else{
		windowTaskId = win.mAppToken.mTask.mTaskId;
	}
    if(windowTaskId == topTaskId||isSurface){
		if(DEBUG) Log.i(TAG_DUALSCREEN, "moveTransitionToSecondDisplay->add win:" + win);
		defaultWindows.remove(win);
		mTempWindowList.add(win);
		win.mDisplayContent = secondDisplayContent;
		if(DEBUG) Log.i(TAG_DUALSCREEN,"win.mDisplayContent = "+win.mDisplayContent+ "   secondDisplayContent = "+secondDisplayContent);
		if(win.mWinAnimator != null){
			int layerStack = secondDisplayContent.getDisplay().getLayerStack();
			if(win.mWinAnimator.mSurfaceController!= null){
				win.mWinAnimator.mSurfaceController.mSurfaceControl.setLayerStack(layerStack);
			}
		}
        secondDisplayAddList.add(0,win);
        mSecondTopPackageName = win.getOwningPackage();
	}
}
           
  • 該 for 循環會對主屏的所有 windows 都周遊一遍
  • 首先擷取 windows,若 win 為 null,或者 mAppToken 為 null,則跳出該次循環,直接進入下一次循環
  • 判斷 win 是否處于頂層,如果是,将标志位 isSurface 指派為 true
  • 若 win 不是處于頂層,且 win.mAppToken.mTask 是空,則跳出該次循環,直接進入下一次循環
  • 如果不是上述兩種情況,則将 windowTaskId 指派為 win 的 mTaskId
  • 判斷目前 win 是否處于頂層,如果是,則從主屏的 WindowList 中移除該視窗,并将該視窗添加進一個臨時的 WindowList。設定該視窗的 mDisplayContent 為副屏的 secondDisplayContent。設定該視窗的 win.layerStack 為副屏的 display.layerStack。然後将該 win 添加進副屏的 windowlist 中,并擷取到該 win 對應的包名,儲存為副屏的頂層包名 mSecondTopPackageName
8
DisplayContent displayContent = getDefaultDisplayContentLocked();   
if (displayContent != null) {
    final DisplayInfo displayInfo = displayContent.getDisplayInfo();
    int rotation = 0;
    if(displayInfo.logicalWidth > displayInfo.logicalHeight) {
        rotation = Surface.ROTATION_90;
    } else {
        rotation = Surface.ROTATION_0;
    }
    Settings.System.putInt(mContext.getContentResolver(), Settings.System.USER_ROTATION, rotation);
}
           
  • 這部分主要是根據裝置的長寬比,确定螢幕的方向
9
secondDisplayWindows.clear();
secondDisplayWindows.addAll(secondDisplayAddList);	
           
  • 清空副屏的 secondDisplayWindows.clear()
  • 将前面第 7 步 for 循環中,secondDisplayAddList 儲存的 windows,全部添加到 secondDisplayWindows(這個才是正式的副屏的 WindowList)
10
for (int i = 0; i < displayCount; i++) {
	final DisplayContent content = mDisplayContents.valueAt(i);
	mLayersController.assignLayersLocked(content.getWindowList());
	content.layoutNeeded = true;
}
           
11
mSecondDisplayTaskId = topTaskId;
misMovingToSecond = true;
Settings.System.putInt(mContext.getContentResolver(), Settings.DUAL_SCREEN_ICON_USED, 1);
           
  • 将副屏的顯示 taskId 設定為頂層應用的 topTaskId
  • 将表示 window 正在移動到副屏的标志位 misMovingToSecond 設定為 true
  • 設定屬性值 DUAL_SCREEN_ICON_USED 為 1,表示處于異顯狀态
12
curMoveTaskId = getLaunchTaskId();
if (curMoveTaskId == -1) {
    curMoveTaskId = allTaskIds.get(1); 
}
if (DEBUG) Log.i(TAG_DUALSCREEN, "WindowManagerService->curMoveTaskId:" + curMoveTaskId );
switchFocusWindow(curMoveTaskId);
           
  • 擷取目前應用的 taskId,如果該 taskId 等于 -1,擷取第二個 taskId,指派給 curMoveTaskId
  • 将 Focus 的 window 設定為 curMoveTaskId 所對應的 window
13
updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES, false);
mAppTransition.setReady();
mWindowPlacerLocked.performSurfacePlacement();
           
  • 調用 performSurfacePlacement(),重新整理每個 display 及視窗顯示的大小和位置
14
currentTimeout = Settings.System.getLong(mContext.getContentResolver(),Settings.System.SCREEN_OFF_TIMEOUT,3000);
Settings.System.putInt(mContext.getContentResolver(), Settings.System.SCREEN_OFF_TIMEOUT,2147483647);
Binder.restoreCallingIdentity(origId);
           
  • 擷取待機時間
  • 設定待機時間