【RK3288 Android 7.1 / KEN】双屏异显流程


异显功能使能

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

控制代码位置:

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
Settings.System.putInt(mContext.getContentResolver(), Settings.DUAL_SCREEN_ICON_USED, 0);
  • 先将 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);
  • 获取待机时间
  • 设置待机时间
  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要将RK3288设备升级到Android 7.1并root,您可以按照以下步骤进行操作: 1. 下载适用于RK3288Android 7.1固件,确保固件是适用于您的设备型号的版本。 2. 将设备连接到电脑,并将固件文件复制到设备的内部存储或SD卡中。 3. 打开设备设置,找到“关于设备”选项,然后点击“软件版本”多次,直到开启开发者选项。 4. 返回上级菜单,找到“开发者选项”,然后进入并启用“USB调试”选项。 5. 断开设备与电脑的连接,并将设备关机。 6. 进入设备的恢复模式。不同设备的进入方法可能有所不同,通常是按住音量减和电源键直到进入恢复模式。 7. 在恢复模式下,使用音量键导航到“安装Zip文件”或类似选项,并使用电源键确认选择。 8. 导航到存储设备上的固件文件,选择并确认安装。 9. 安装完成后,在恢复模式下重新启动设备。 10. 下载适用于Android 7.1的root工具,如Magisk或SuperSU。 11. 确保设备已连接到电脑,将root工具复制到设备的内部存储或SD卡中。 12. 断开设备与电脑的连接,并将设备关机。 13. 重新进入恢复模式,导航到“安装Zip文件”或类似选项。 14. 选择并确认安装root工具。 15. 完成root工具的安装后,重新启动设备。 现在,您的RK3288设备应该已成功升级到Android 7.1并root。请注意,root操作可能会使设备的保修失效,并可能存在安全风险,请谨慎操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值