// frameworks/base/services/core/java/com/android/server/wm/ParallelSession.java
package com.android.server.wm;
import android.app.ActivityOptions;
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.os.Handler;
import android.os.Looper;
import android.os.Message;
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.atomic.AtomicBoolean;
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类名)
public static final ArrayMap<String, String> sParallelApps = new ArrayMap<>();
static {
Log.i(TAG, "sParallelApps init !");
sParallelApps.put("com.taobao.taobao", "com.taobao.shop.activity.DetailActivity");
sParallelApps.put("com.jingdong.app.mall", "com.jd.lib.productdetail.ProductDetailActivity");
sParallelApps.put("com.alibaba.android.rimet", "com.alibaba.android.rimet.biz.web.WebActivity");
}
private final int mTaskId;
private ActivityRecord mPrimaryActivity; // 主屏活动
private ActivityRecord mSecondaryActivity; // 副屏活动
private float mSplitRatio = 0.4f; // 分屏比例(默认50%)
private WindowState mPrimaryWindow; // 主屏窗口
private WindowState mSecondaryWindow; // 副屏窗口
// 使用原子操作标记防止递归
final AtomicBoolean isDestroying = new AtomicBoolean(false);
// 生命周期监听器接口(用于状态同步)
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) {
//if(primary.getTask() == null) return;
mTaskId = primary.getTask().mTaskId;
//mPrimaryActivity = primary;
sSessions.put(primary.getTask().mTaskId, this);
setPrimaryActivity(primary);
if (DEBUG) Log.d(TAG, "Created new ParallelSession for task: " + primary.getTask().mTaskId +", primary = "+primary.toString());
}
public ParallelSession(int taskId, ActivityRecord primary) {
//if(primary.getTask() == null) return;
mTaskId = taskId;
//mPrimaryActivity = primary;
sSessions.put(taskId, this);
setPrimaryActivity(primary);
Log.i(TAG, "Created new ParallelSession for task:" + taskId);
}
// 设置主屏活动
public void setPrimaryActivity(ActivityRecord primary) {
//if (mPrimaryActivity != null && sParallelApps.containsKey(primary.packageName)) {
if (primary != null) {
Log.w(TAG, "Primary activity != null");
if (!sParallelApps.containsKey(primary.packageName)) {
Log.w(TAG, "Primary activity set failed!");
return;
}
}
mPrimaryActivity = primary;
//getPrimaryWindow().attach();
if(getWindowStateForRecord(mPrimaryActivity) != null) getWindowStateForRecord(mPrimaryActivity).attach();
linkPrimaryActivities();
if (DEBUG) Log.d(TAG, "Primary activity set: " + primary);
}
// 设置副屏活动
public void setSecondaryActivity(ActivityRecord secondary) {
if (mSecondaryActivity != null) {
Log.w(TAG, "Secondary activity already set!");
return;
}
mSecondaryActivity = secondary;
linkSecondaryActivities();
if (DEBUG) Log.d(TAG, "Secondary activity set: " + secondary);
}
private void linkPrimaryActivities() {
// 绑定生命周期事件
if (DEBUG) Log.d(TAG, "linkPrimaryActivities");
mPrimaryActivity.addLifecycleListener(new LifecycleListener() {
@Override
public void onStateChanged(ActivityRecord activity, State state) {
if (DEBUG) Log.d(TAG, "linkPrimaryActivities 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");
if (mSecondaryActivity.isState(ActivityRecord.State.RESUMED)) return;
// 使用系统服务恢复副屏活动
//if (mSecondaryActivity.getTask() != null) mSecondaryActivity.getTask().resumeTopActivityUncheckedLocked(null,null);
// 异步恢复副屏Activity
handler.post(() -> {
if (mSecondaryActivity.getTask() != null) {
mSecondaryActivity.getTask().resumeTopActivityUncheckedLocked(null, null);
}
});
//mSecondaryActivity.resume();
}
handleFocusChange(mPrimaryWindow);
}
@Override
public void onConfigurationChanged(ActivityRecord activity, Configuration newConfig) {
if (DEBUG) Log.d(TAG, "linkPrimaryActivities mPrimaryActivity onConfigurationChanged");
}
@Override
public void onDestroyed(ActivityRecord activity) {
if (isDestroying.getAndSet(true)) {
return; // 已经在销毁过程中
}
/* if (DEBUG) Log.d(TAG, "linkPrimaryActivities 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); */
try {
if (DEBUG) Log.d(TAG, "linkPrimaryActivities mPrimaryActivity onDestroyed");
if (mSecondaryActivity != null && !mSecondaryActivity.finishing) {
// 移除监听器防止回调
mSecondaryActivity.removeLifecycleListener(this);
// 延迟销毁副屏Activity
handler.postDelayed(() -> {
if (!mSecondaryActivity.finishing) {
mSecondaryActivity.destroyImmediately("Primary destroyed");
}
}, 100); // 100ms延迟确保主Activity完全销毁
}
} finally {
if(sSessions != null && mSecondaryActivity != null && mSecondaryActivity.getTask() != null) sSessions.remove(activity.getTask().mTaskId);
isDestroying.set(false);
}
}
});
}
private void linkSecondaryActivities() {
// 绑定生命周期事件
if (DEBUG) Log.d(TAG, "linkSecondaryActivities");
// 副屏关闭时同步主屏
mSecondaryActivity.addLifecycleListener(new LifecycleListener() {
@Override
public void onStateChanged(ActivityRecord activity, State state) {
if (DEBUG) Log.d(TAG, "linkSecondaryActivities mSecondaryActivity onStateChanged");
/* if (state == ActivityRecord.State.RESUMED && mPrimaryActivity != null) {
if (DEBUG) Log.d(TAG, "Primary resumed, resuming secondary");
if (mPrimaryActivity.isState(ActivityRecord.State.RESUMED)) return;
// 使用系统服务恢复副屏活动
if (mPrimaryActivity.getTask() != null) mPrimaryActivity.getTask().resumeTopActivityUncheckedLocked(null,null);
//mSecondaryActivity.resume();
} */
handleFocusChange(mSecondaryWindow);
}
@Override
public void onConfigurationChanged(ActivityRecord activity, Configuration newConfig) {
if (DEBUG) Log.d(TAG, "linkSecondaryActivities mSecondaryActivity onConfigurationChanged");
}
@Override
public void onDestroyed(ActivityRecord activity) {
if (isDestroying.getAndSet(true)) {
return;
}
/* if (DEBUG) Log.d(TAG, "linkSecondaryActivities 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); */
try {
if (DEBUG) Log.d(TAG, "Secondary destroyed");
if (mPrimaryActivity != null && !mPrimaryActivity.finishing) {
// 移除监听器防止回调
mPrimaryActivity.removeLifecycleListener(this);
// 延迟销毁主Activity
handler.postDelayed(() -> {
if (!mPrimaryActivity.finishing) {
mPrimaryActivity.destroyImmediately("Secondary destroyed");
}
}, 100);
}
} finally {
if(sSessions != null && mPrimaryActivity != null && mPrimaryActivity.getTask() != null) sSessions.remove(mPrimaryActivity.getTask().mTaskId);
isDestroying.set(false);
}
}
});
}
private Handler handler = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
//todo somethings
//if (msg.what == MSG_WHAT) {
// 处理消息
//Log.e("CentralSurfacesImpl_wangw", "mHandler2 1 Message received");
//}
}
};
// 计算主屏显示区域
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.right = (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.left = (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, ActivityRecord target) {//控制是否在第二界面打开
if (DEBUG) Log.d(TAG, "shouldOpenInSecondary, caller = "+caller+", target = "+target);
if (caller == null || !(sParallelApps.containsKey(caller.packageName))) {
return false;
}
return sParallelApps.containsKey(target.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);
}
}
}
} */
// 在 ActivityRecord 类中添加获取 WindowState 的方法
/* public WindowState getWindowState(ActivityRecord record) {
// 1. 获取 AppWindowToken
final AppWindowToken appWindowToken = record.appWindowToken;
if (appWindowToken == null) {
return null;
}
// 2. 获取 WindowToken
final WindowToken windowToken = appWindowToken.getWindowToken();
if (windowToken == null) {
return null;
}
// 3. 获取 WindowState
for (int i = windowToken.getChildCount() - 1; i >= 0; i--) {
final WindowState win = (WindowState) windowToken.getChildAt(i);
if (DEBUG) Log.d(TAG, "getWindowState window = " + win);
if (win != null && win.mActivityRecord == record) {
return win;
}
}
return null;
} */
// 正确获取与 ActivityRecord 关联的 WindowState
public WindowState getWindowStateForRecord(ActivityRecord record) {
WindowManagerService wms = record.mWmService;
if (wms == null) {
return null;
}
synchronized (wms.mGlobalLock) {
// 方法1:直接通过 token 获取(推荐)
WindowState win = wms.mWindowMap.get(record.token);
if (win != null && win.mActivityRecord == record) {
if (DEBUG) Log.d(TAG, "getWindowState win = " + win.mActivityRecord);
return win;
}
// 方法2:遍历所有窗口(备选方案)
for (WindowState ws : wms.mWindowMap.values()) {
if (DEBUG) Log.d(TAG, "getWindowState ws = " + ws.mActivityRecord);
if (ws.mActivityRecord == record) {
return ws;
}
}
return null;
}
}
// 注册主屏窗口
public void registerPrimaryWindow(WindowState win) {
mPrimaryWindow = win;
if (DEBUG) Log.d(TAG, "Registered primary window: " + 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 dumpVisibilityState() {
Log.d(TAG, "Primary Activity Visibility: " +
(mPrimaryActivity != null ? mPrimaryActivity.isVisible() : "null"));
Log.d(TAG, "Secondary Activity Visibility: " +
(mSecondaryActivity != null ? mSecondaryActivity.isVisible() : "null"));
Log.d(TAG, "Primary Window Visibility: " +
(mPrimaryWindow != null ? mPrimaryWindow.isVisible() : "null"));
Log.d(TAG, "Secondary Window Visibility: " +
(mSecondaryWindow != null ? mSecondaryWindow.isVisible() : "null"));
}
// 销毁会话
public void destroy() {
sSessions.remove(mTaskId);
Log.i(TAG, "销毁平行会话,任务ID: " + mTaskId);
}
// 获取指定任务的会话
public static ParallelSession getSessionForTask(int taskId) {
if (DEBUG) Log.d(TAG, "getSessionForTask = "+taskId);
return sSessions.get(taskId);
}
public static void addSession(int taskId, ParallelSession session) {
sSessions.put(taskId, session);
}
public static boolean isParallelSessionActive(int taskId) {
if (DEBUG) Log.d(TAG, "isParallelSessionActive = "+taskId);
return sSessions.get(taskId) != null;
}
public ActivityRecord getPrimaryActivity() {
if (DEBUG) Log.d(TAG, "getPrimaryActivity = "+mPrimaryActivity);
dumpVisibilityState();
return mPrimaryActivity;
}
public ActivityRecord getSecondaryActivity() {
if (DEBUG) Log.d(TAG, "getSecondaryActivity = "+mSecondaryActivity);
return mSecondaryActivity;
}
public WindowState getPrimaryWindow() {
if (DEBUG) Log.d(TAG, "getPrimaryWindow = "+mPrimaryWindow);
return mPrimaryWindow;
}
public WindowState getSecondaryWindow() {
if (DEBUG) Log.d(TAG, "getSecondaryWindow = "+mSecondaryWindow);
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 配置文件 应用配置列表 定义支持平行视界的应用及其分屏比例
*/
}这是安卓14修改类似中兴平行视界的类,改完后副屏打开之后,主屏会消失不显示是什么原因,怎么解决;以及副屏幕返回之后,主屏幕一起退出了,如何修改
最新发布