// frameworks/base/services/core/java/com/android/server/wm/ParallelSession.java
package com.android.server.wm;
import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.content.res.XmlResourceParser;
import android.graphics.Rect;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
import android.util.Xml;
import android.view.DisplayInfo;
import android.view.WindowManager;
import android.view.InputChannel;
import com.android.server.input.InputManagerService;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.HashSet;
import java.util.Set;
import com.android.server.wm.ActivityRecord.State;
/**
* 管理平行视界会话,处理主副屏Activity的生命周期同步和布局
*/
public class ParallelSession {
private static final String TAG = "ParallelSession_wangw";
private static final boolean DEBUG = true;
// 全局会话存储(按任务ID索引)
private static final SparseArray<ParallelSession> sSessions = new SparseArray<>();
// 平行应用映射(包名 -> 副屏Activity类名)
private static final ArrayMap<String, String> sParallelApps = new ArrayMap<>();
private final int mTaskId;
private ActivityRecord mPrimaryActivity; // 主屏活动
private ActivityRecord mSecondaryActivity; // 副屏活动
private float mSplitRatio = 0.4f; // 分屏比例(默认50%)
private WindowState mPrimaryWindow; // 主屏窗口
private WindowState mSecondaryWindow; // 副屏窗口
// 生命周期监听器接口(用于状态同步)
public interface LifecycleListener {
void onStateChanged(ActivityRecord activity, State state);
void onDestroyed(ActivityRecord activity);
void onConfigurationChanged(ActivityRecord activity, Configuration newConfig);
}
// 线程安全的生命周期监听器列表
private final CopyOnWriteArrayList<LifecycleListener> mLifecycleListeners =
new CopyOnWriteArrayList<>();
public ParallelSession(ActivityRecord primary) {
mTaskId = primary.getTask().mTaskId;
mPrimaryActivity = primary;
sSessions.put(primary.getTask().mTaskId, this);
if (DEBUG) Log.d(TAG, "Created new ParallelSession for task: " + primary.getTask().mTaskId +", primary = "+primary.toString());
}
public ParallelSession(int taskId, ActivityRecord primary) {
mTaskId = taskId;
mPrimaryActivity = primary;
sSessions.put(taskId, this);
Log.i(TAG, "创建平行会话,任务ID: " + taskId);
}
// 设置副屏活动
public void setSecondaryActivity(ActivityRecord secondary) {
if (mSecondaryActivity != null) {
Log.w(TAG, "Secondary activity already set!");
return;
}
mSecondaryActivity = secondary;
linkActivities();
if (DEBUG) Log.d(TAG, "Secondary activity set: " + secondary);
}
private void linkActivities() {
// 绑定生命周期事件
if (DEBUG) Log.d(TAG, "linkActivities");
mPrimaryActivity.addLifecycleListener(new LifecycleListener() {
@Override
public void onStateChanged(ActivityRecord activity, State state) {
if (DEBUG) Log.d(TAG, "linkActivities mPrimaryActivity onStateChanged");
if (state == ActivityRecord.State.PAUSED && mSecondaryActivity != null) {
if (DEBUG) Log.d(TAG, "Primary paused, pausing secondary");
// 使用任务调度暂停副屏活动
//mSecondaryActivity.pause();
//mSecondaryActivity.getTask().schedulePauseActivity(mSecondaryActivity);
} else if (state == ActivityRecord.State.RESUMED && mSecondaryActivity != null) {
if (DEBUG) Log.d(TAG, "Primary resumed, resuming secondary");
// 使用系统服务恢复副屏活动
mSecondaryActivity.getTask().resumeTopActivityUncheckedLocked(null, null);
//mSecondaryActivity.resume();
}
handleFocusChange(mPrimaryWindow);
}
@Override
public void onConfigurationChanged(ActivityRecord activity, Configuration newConfig) {
if (DEBUG) Log.d(TAG, "linkActivities mPrimaryActivity onConfigurationChanged");
}
@Override
public void onDestroyed(ActivityRecord activity) {
if (DEBUG) Log.d(TAG, "linkActivities mPrimaryActivity onDestroyed");
if (mSecondaryActivity != null && !mSecondaryActivity.finishing) {
if (DEBUG) Log.d(TAG, "Primary destroyed, finishing secondary");
// 使用安全销毁方法
mSecondaryActivity.destroyIfPossible("Primary destroyed");
//mSecondaryActivity.finishIfPossible("Primary destroyed", true);
}
sSessions.remove(activity.getTask().mTaskId);
}
});
// 副屏关闭时同步主屏
mSecondaryActivity.addLifecycleListener(new LifecycleListener() {
@Override
public void onStateChanged(ActivityRecord activity, State state) {
if (DEBUG) Log.d(TAG, "linkActivities mSecondaryActivity onStateChanged");
handleFocusChange(mSecondaryWindow);
}
@Override
public void onConfigurationChanged(ActivityRecord activity, Configuration newConfig) {
if (DEBUG) Log.d(TAG, "linkActivities mSecondaryActivity onConfigurationChanged");
}
@Override
public void onDestroyed(ActivityRecord activity) {
if (DEBUG) Log.d(TAG, "linkActivities mSecondaryActivity onDestroyed");
if (mPrimaryActivity != null && !mPrimaryActivity.finishing) {
if (DEBUG) Log.d(TAG, "Secondary destroyed, finishing primary");
// 使用安全销毁方法
mPrimaryActivity.destroyIfPossible("Secondary destroyed");
//mPrimaryActivity.finishIfPossible("Secondary destroyed", true);
}
sSessions.remove(mPrimaryActivity.getTask().mTaskId);
}
});
}
// 计算主屏显示区域
public Rect getPrimaryBounds(DisplayContent display) {
if (DEBUG) Log.d(TAG, "getPrimaryBounds");
if (display == null) {
Log.w(TAG, "Null display for primary bounds");
return new Rect(0, 0, 1080, 1920); // 默认值
}
DisplayInfo info = display.getDisplayInfo();
Rect bounds = new Rect(0, 0, info.logicalWidth, info.logicalHeight);
bounds.left = (int) (bounds.width() * mSplitRatio); // 按比例分割
return bounds;
}
// 计算副屏显示区域
public Rect getSecondaryBounds(DisplayContent display) {
if (DEBUG) Log.d(TAG, "getSecondaryBounds");
if (display == null) {
Log.w(TAG, "Null display for secondary bounds");
return new Rect(0, 0, 1080, 1920); // 默认值
}
DisplayInfo info = display.getDisplayInfo();
Rect bounds = new Rect(0, 0, info.logicalWidth, info.logicalHeight);
bounds.right = (int) (bounds.width() * mSplitRatio); // 按比例分割
return bounds;
}
public void setSplitRatio(float ratio) {
if (DEBUG) Log.d(TAG, "setSplitRatio");
mSplitRatio = Math.max(0.2f, Math.min(0.8f, ratio));
if (mPrimaryActivity != null && mPrimaryActivity.getWindow() != null) {
mPrimaryActivity.getTask().reparent(null, true);
}
if (mSecondaryActivity != null && mSecondaryActivity.getWindow() != null) {
mSecondaryActivity.getTask().reparent(null, true);
}
}
public static boolean shouldOpenInSecondary(ActivityRecord caller, ActivityInfo targetInfo) {//控制是否在第二界面打开
if (DEBUG) Log.d(TAG, "shouldOpenInSecondary, caller.packageName = "+caller.packageName+", targetInfo.targetActivity = "+targetInfo.targetActivity);
return "com.jingdong.app.mall".equals(caller.packageName) || "com.alibaba.android.rimet".equals(caller.packageName);
/* if (caller == null || !sParallelApps.containsKey(caller.packageName)) {
return false;
}
String expectedActivity = sParallelApps.get(caller.packageName);
return expectedActivity.equals(targetInfo.targetActivity) ||
expectedActivity.equals(targetInfo.name); */
}
/* public static void loadConfigFromFile(File configFile) {
if (!configFile.exists()) {
Log.w(TAG, "Parallel config file not found: " + configFile);
return;
}
try (FileInputStream fis = new FileInputStream(configFile)) {
XmlResourceParser parser = Xml.newPullParser();
parser.setInput(fis, "UTF-8");
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) continue;
final String tagName = parser.getName();
if ("parallel-apps".equals(tagName)) {
parseParallelApps(parser);
}
}
} catch (IOException | XmlPullParserException e) {
Log.e(TAG, "Error loading parallel config", e);
}
}
private static void parseParallelApps(XmlPullParser parser)
throws IOException, XmlPullParserException {
sParallelApps.clear();
int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT &&
(type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type != XmlPullParser.START_TAG) continue;
if ("app".equals(parser.getName())) {
String pkg = parser.getAttributeValue(null, "package");
String secondary = parser.getAttributeValue(null, "secondary");
if (pkg != null && secondary != null) {
sParallelApps.put(pkg, secondary);
if (DEBUG) Log.i(TAG, "Added parallel app: " + pkg + " -> " + secondary);
}
}
}
} */
// 注册主屏窗口
public void registerPrimaryWindow(WindowState win) {
mPrimaryWindow = win;
if (DEBUG) Log.d(TAG, "注册主屏窗口: " + win);
}
public void registerSecondaryWindow(WindowState win) {
mSecondaryWindow = win;
if (DEBUG) Log.d(TAG, "Registered secondary window: " + win);
}
// 处理焦点变化(确保只有一个窗口获得焦点)
public void handleFocusChange(WindowState focused) {
if (DEBUG) Log.d(TAG, "handleFocusChange");
if (focused == null) return;
if (focused == mPrimaryWindow && mSecondaryWindow != null) {
//mSecondaryWindow.onSetFocused(false); // 取消副屏焦点
//focused.mWmService.mInputMonitor.setInputFocusLw(mSecondaryWindow, false);
mSecondaryWindow.getDisplayContent().getInputMonitor().setInputFocusLw(mSecondaryWindow, false);
} else if (focused == mSecondaryWindow && mPrimaryWindow != null) {
//mPrimaryWindow.onSetFocused(false); // 取消主屏焦点
//focused.mWmService.mInputMonitor.setInputFocusLw(mPrimaryWindow, false);
mPrimaryWindow.getDisplayContent().getInputMonitor().setInputFocusLw(mPrimaryWindow, false);
}
focused.mWmService.updateFocusedWindowLocked(
WindowManagerService.UPDATE_FOCUS_NORMAL,
true
);
}
// 添加窗口令牌映射
/* private final ArrayMap<IBinder, ActivityRecord> mTokenToRecord = new ArrayMap<>();
public void registerWindowToken(IBinder token, ActivityRecord ar) {
mTokenToRecord.put(token, ar);
}
public ActivityRecord getRecordForToken(IBinder token) {
return mTokenToRecord.get(token);
} */
// 添加生命周期监听器
public void addLifecycleListener(LifecycleListener listener) {
if (DEBUG) Log.d(TAG, "addLifecycleListener");
mLifecycleListeners.add(listener);
}
// 状态变化通知
public void onStateChanged(ActivityRecord activity, State state) {
if (DEBUG) Log.d(TAG, "onStateChanged");
for (LifecycleListener listener : mLifecycleListeners) {
listener.onStateChanged(activity, state);
}
}
public void onDestroyed(ActivityRecord activity) {
if (DEBUG) Log.d(TAG, "onDestroyed");
for (LifecycleListener listener : mLifecycleListeners) {
listener.onDestroyed(activity);
}
if (activity == mPrimaryActivity || activity == mSecondaryActivity) {
sSessions.remove(mTaskId);
}
}
public void onConfigurationChanged(ActivityRecord activity, Configuration newConfig) {
if (DEBUG) Log.d(TAG, "onConfigurationChanged");
for (LifecycleListener listener : mLifecycleListeners) {
listener.onConfigurationChanged(activity, newConfig);
}
}
// 销毁会话
public void destroy() {
sSessions.remove(mTaskId);
Log.i(TAG, "销毁平行会话,任务ID: " + mTaskId);
}
// 获取指定任务的会话
public static ParallelSession getSessionForTask(int taskId) {
if (DEBUG) Log.d(TAG, "getSessionForTask");
return sSessions.get(taskId);
}
public static boolean isParallelSessionActive(int taskId) {
if (DEBUG) Log.d(TAG, "isParallelSessionActive");
return sSessions.get(taskId) != null;
}
public ActivityRecord getPrimaryActivity() {
if (DEBUG) Log.d(TAG, "getPrimaryActivity = "+mPrimaryActivity.toString());
return mPrimaryActivity;
}
public ActivityRecord getSecondaryActivity() {
if (DEBUG) Log.d(TAG, "getSecondaryActivity = "+mSecondaryActivity.toString());
return mSecondaryActivity;
}
public WindowState getPrimaryWindow() {
if (DEBUG) Log.d(TAG, "getPrimaryWindow = "+mPrimaryWindow.toString());
return mPrimaryWindow;
}
public WindowState getSecondaryWindow() {
if (DEBUG) Log.d(TAG, "getSecondaryWindow = "+mSecondaryWindow.toString());
return mSecondaryWindow;
}
/* public interface ActivityLifecycleListener {
void onStateChanged(ActivityRecord activity, State state);
default void onDestroyed(ActivityRecord activity);
void onConfigurationChanged(ActivityRecord activity, Configuration newConfig);
} */
/**
* 平行视界完整工作流程
入口触发:当主Activity启动副Activity时,ActivityStarter 检测到白名单匹配
会话创建:创建 ParallelSession 并关联主Activity
副屏创建:创建副屏任务栈,将目标Activity放入副屏
布局阶段:DisplayPolicy 为两个Activity计算分屏布局
输入路由:InputDispatcher 根据触摸位置分发到正确窗口
生命周期同步:通过监听器实现主副屏状态同步
新增 ParallelSession.java 新增类 会话管理核心 管理活动、窗口、布局和生命周期同步
DisplayPolicy.java 修改 layoutWindowLw() 方法 覆盖布局逻辑,应用平行视界布局
WindowManagerService.java 修改 添加 ParallelInputMonitor 处理触摸事件重定向
WindowState.java 修改 onSetFocused() 和 attach() 处理焦点管理和窗口注册
ActivityRecord.java 修改 生命周期方法覆盖 实现状态同步和配置变更通知
ActivityStarter.java 修改 execute() 方法 添加平行会话启动逻辑
新增 /system/etc/parallel_apps.xml 配置文件 应用配置列表 定义支持平行视界的应用及其分屏比例
*/
}
这是平行视界的工具类
public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) {
if (win.skipLayout()) {
return;
}
android.util.Log.d("ParallelSession_wangw", "layoutWindowLw ");
// 获取当前Display
if(win != null) {
DisplayContent display = win.getDisplayContent();
if (display != null) {
// 平行视界处理
ActivityRecord ar = win.getActivityRecord();
if (ar != null) {
ParallelSession session = ParallelSession.getSessionForTask(ar.getTask().mTaskId);
if (session != null) {
android.util.Log.d("ParallelSession_wangw", "layoutWindowLw win.setFrames");
// 设置窗口边界
Rect bounds = (ar == session.getPrimaryActivity()) ?
session.getPrimaryBounds(display) :
session.getSecondaryBounds(display);
/* // 设置所有相关布局区域
frame.set(bounds);
// 调整窗口特性
win.setForceHideNonSystemOverlayWindowIfNeede(true);
win.setCanBeSeen(false);
// 设置窗口位置
win.setFrame(bounds);
win.setParallelSession(true); */
/* // === 修改点1: 使用正确的WindowFrames对象 ===
// 获取窗口当前的WindowFrames对象
WindowFrames winFrames = win.getWindowFrames();
// 设置所有相关布局区域
winFrames.mFrame.set(bounds); // 主窗口区域
winFrames.mDisplayFrame.set(bounds); // 显示区域
winFrames.mParentFrame.set(bounds); // 父容器区域 */
// 替代 setWindowFrames()
ClientWindowFrames tmpFrames = new ClientWindowFrames();
tmpFrames.frame.set(bounds);
tmpFrames.displayFrame.set(bounds);
tmpFrames.parentFrame.set(bounds);
//win.mWmService.mWindowPlacerLocked.requestTraversal();
//win.mHidden = true;
win.setFrames(tmpFrames, bounds.width(), bounds.height());
// === 修改点2: 使用标准API设置窗口状态 ===
// 1. 强制隐藏非系统覆盖层窗口 - 通过设置私有标志位
//win.mAttrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
// 2. 设置窗口隐藏状态 - 通过系统服务API
//win.mWmService.mWindowPlacerLocked.requestTraversal();
//win.mHidden = true; // 使用setHidden替代setCanBeSeen
// === 修改点3: 使用setFrames设置窗口位置 ===
//win.setFrames(winFrames, winFrames.mFrame.width(), winFrames.mFrame.height());
//win.setWindowFrames(winFrames);
// === 修改点4: 添加自定义标记的替代方案 ===
// 如果需要标记平行视界状态,应在WindowState中添加自定义字段
// 这里使用临时方案:在WindowToken中存储状态
//win.mToken.parallelSession = true;
Rect bounds_other = (ar != session.getPrimaryActivity()) ?
session.getPrimaryBounds(display) :
session.getSecondaryBounds(display);
ClientWindowFrames tmpFrames_other = new ClientWindowFrames();
tmpFrames_other.frame.set(bounds_other);
tmpFrames_other.displayFrame.set(bounds_other);
tmpFrames_other.parentFrame.set(bounds_other);
//win.mWmService.mWindowPlacerLocked.requestTraversal();
//win.mHidden = true;
win.setFrames(tmpFrames_other, bounds_other.width(), bounds_other.height());
return;
}
}
}
}
这是设置平行视界左右窗口的位置
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, ActivityOptions options, Task inTask,
TaskFragment inTaskFragment, @BalCode int balCode,
NeededUriGrants intentGrants, int realCallingUid) {
int result = START_CANCELED;
Task startedActivityRootTask = null;
//wfs add 20250712 @{
if((sourceRecord==null||(sourceRecord!=null&&!"com.tmall.wireless".equals(sourceRecord.mActivityComponent.getClassName())))&&"com.ali.user.mobile.login.ui.UserLoginActivity".equals(r.mActivityComponent.getClassName())&&!"com.tmall.wireless".equals(getTopPkg())&&!"com.taobao.taobao".equals(getTopPkg())){
android.util.Log.d("wfswfs333"," r.mActivityComponent.getClassName()::"+r.mActivityComponent.getClassName());
return START_CANCELED;
}
//wfs add 20250712 @}
// ===== 平行视界拦截逻辑 =====
android.util.Log.d("ParallelSession_wangw", "startActivityUnchecked ");
try {
if (shouldStartParallel(mStartActivity)) {
ActivityRecord callerRecord = (sourceRecord != null) ? sourceRecord : mStartActivity;
android.util.Log.d("ParallelSession_wangw", "startActivityUnchecked callerRecord = "+sourceRecord != null ? "sourceRecord" : "mStartActivity");
if (mStartActivity != null) android.util.Log.d("ParallelSession_wangw", "startActivityUnchecked mStartActivity.packageName = "+mStartActivity.packageName);
if (callerRecord != null && mService != null) {
android.util.Log.d("ParallelSession_wangw", "startActivityUnchecked callerRecord != null; callerRecord.packageName = "+callerRecord.packageName);
int callerTaskId = callerRecord.getTask().mTaskId;
// 检查是否已有平行会话
ParallelSession session = ParallelSession.getSessionForTask(callerTaskId);
TaskDisplayArea taskDisplayArea = mPreferredTaskDisplayArea != null ?
mPreferredTaskDisplayArea :
mRootWindowContainer.getDefaultTaskDisplayArea();
// 检查是否应该启动到副屏
if (session == null && ParallelSession.shouldOpenInSecondary(callerRecord, r.info)) {
android.util.Log.d("ParallelSession_wangw", "startActivityUnchecked new ParallelSession");
// 创建新的平行会话
session = new ParallelSession(callerRecord);
//mService.setParallelSession(session);
//// 创建副屏任务栈
Task secondaryTask = taskDisplayArea.createRootTask(
WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, // 分屏副屏模式
ACTIVITY_TYPE_STANDARD, // 标准Activity类型
true // 置于顶部
);
// 设置任务属性
secondaryTask.setIntent(r);
r.reparent(secondaryTask, secondaryTask.getChildCount() /* position */, "parallel_launch");
/* // ===== 修改点1:使用Task.Builder创建任务 =====
// 创建Task.Builder实例
final Task.Builder builder = new Task.Builder(mService);
// 设置关键属性:
builder.setParent(taskDisplayArea) // 父容器
.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) // 分屏副屏模式
.setActivityType(ACTIVITY_TYPE_STANDARD) // 标准Activity类型
.setOnTop(true) // 置于顶部
//.setActivity(r) // 关联的ActivityRecord
.setIntent(r.intent); // Intent
// 构建任务实例
Task secondaryTask = builder.build();
// 将ActivityRecord添加到新任务中
secondaryTask.addChild(r, secondaryTask.getChildCount());
// 设置ActivityRecord的任务
r.reparent(secondaryTask, secondaryTask.getChildCount(), "parallel_launch");
// ===== 修改结束 ===== */
// 关联到平行会话
session.setSecondaryActivity(r);
//if (DEBUG) {
android.util.Log.d(TAG, "Launching activity in parallel mode: " + r +
" in task: " + secondaryTask);
//}
}
// 已有会话时处理
else if (session != null && session.getSecondaryActivity() == null) {
android.util.Log.d("ParallelSession_wangw", "startActivityUnchecked session != null");
// 已有主屏但无副屏的情况
// 创建副屏根任务
Task secondaryTask = taskDisplayArea.createRootTask(
WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
ACTIVITY_TYPE_STANDARD,
true
);
// 设置任务属性
secondaryTask.setIntent(r);
// ===== 关键修改:使用 reparent() 将 Activity 移动到新任务 =====
r.reparent(secondaryTask, secondaryTask.getChildCount(), "parallel_launch");
/* // ===== 修改点2:同样使用Task.Builder创建任务 =====
final Task.Builder builder = new Task.Builder(mService);
builder.setParent(taskDisplayArea)
.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)
.setActivityType(ACTIVITY_TYPE_STANDARD)
.setOnTop(true)
//.setActivity(r)
.setIntent(r.intent);
Task secondaryTask = builder.build();
// 将ActivityRecord添加到新任务中
secondaryTask.addChild(r, secondaryTask.getChildCount());
// 设置ActivityRecord的任务
r.reparent(secondaryTask, secondaryTask.getChildCount(), "parallel_launch");
// ===== 修改结束 ===== */
session.setSecondaryActivity(r);
} else {
android.util.Log.d("ParallelSession_wangw", "startActivityUnchecked Failed to Launching activity in parallel mode");
}
}
}
} catch (Exception e) {
android.util.Log.e("ParallelSession_wangw", "Failed to create parallel session", e);
}
这是启动平行视界的地方
但这样改了之后,mPrimaryActivity不显示了,只显示mSecondaryActivity是什么原因怎么解决
最新发布