瑞芯微Rockchip开发者社区
直播中

绝代双骄

11年用户 1094经验值
私信 关注
[问答]

请问一下RK3288 Android 7.1双屏异显功能该怎样去实现呢

请问一下RK3288 Android 7.1双屏异显功能该怎样去实现呢?

回帖(1)

卿洁

2022-3-4 11:31:59

  • 异显功能使能


—— 即控制 异显功能 能否 使用,以及异显功能是否 启用

控制代码位置:

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 主要流程:



  • String dualScreenKeyCodes = SystemProperties.get("sys.dual_screen.keycodes");
    获取 sys.dual_screen.keycodes 属性值(该属性值标记 异显触发键
    该属性值默认是 24,25,分别对应 音量键 +音量键 -
  • mDualScreenCodeInterval = SystemProperties.getLong("sys.dual_screen.interval", 2000L);
    获取 sys.dual_screen_interval 属性值(该属性标记 触发时间间隔




  • 关键方法 2:
    trigerDualScreen()
    触发异显


方法 2 关键步骤:



  • 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


Settings.System.putInt(mContext.getContentResolver(), Settings.DUAL_SCREEN_ICON_USED, 0);



  • 先将 DUAL_SCREEN_ICON_USED 属性值设置为同显状态


3


List 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);



  • 获取待机时间
  • 设置待机时间
举报

更多回帖

发帖
×
20
完善资料,
赚取积分