在Android 12中,RootWindowContainer是一个重要的类,它是WindowManagerService的主要组成部分之一,它是一个管理窗口的容器,其主要作用是管理整个系统中所有窗口的层级关系和布局。它负责跟踪和管理所有窗口,包括应用程序窗口、系统窗口、通知窗口和浮动窗口等。
RootWindowContainer还负责管理窗口的生命周期,包括创建、销毁和显示窗口。它会在窗口被添加或删除时更新窗口的层级关系,以确保所有窗口都能正确地显示在屏幕上。
此外,RootWindowContainer还负责处理多个屏幕和显示设备的情况,以确保多个窗口正确地显示在不同的屏幕上。
1、RootWindowContainer是在WindowManagerService的构造方法中被创建的。
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
//屏幕设备窗口的根容器管理者
RootWindowContainer mRoot;
private WindowManagerService(Context context, InputManagerService inputManager,
boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy,
ActivityTaskManagerService atm, DisplayWindowSettingsProvider
displayWindowSettingsProvider, Supplier<SurfaceControl.Transaction> transactionFactory,
Supplier<Surface> surfaceFactory,
Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {
...代码省略...
mRoot = new RootWindowContainer(this);
...代码省略...
}
}
1、RootWindowContainerd的源码位置如下所示。
frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
class RootWindowContainer extends WindowContainer<DisplayContent>
implements DisplayManager.DisplayListener {
private final Handler mHandler;
// Only a separate transaction until we separate the apply surface changes
// transaction from the global transaction.
private final SurfaceControl.Transaction mDisplayTransaction;
/** The token acquirer to put root tasks on the displays to sleep */
final ActivityTaskManagerInternal.SleepTokenAcquirer mDisplayOffTokenAcquirer;
ActivityTaskManagerService mService;
ActivityTaskSupervisor mTaskSupervisor;
RootWindowContainer(WindowManagerService service) {
super(service);
mDisplayTransaction = service.mTransactionFactory.get();
mHandler = new MyHandler(service.mH.getLooper());
mService = service.mAtmService;
mTaskSupervisor = mService.mTaskSupervisor;
mTaskSupervisor.mRootWindowContainer = this;
mDisplayOffTokenAcquirer = mService.new SleepTokenAcquirerImpl(DISPLAY_OFF_SLEEP_TOKEN_TAG);
}
}
2、RootWindowContainerd类的主要属性如下所示。
class RootWindowContainer extends WindowContainer<DisplayContent>
implements DisplayManager.DisplayListener {
private static final String TAG = TAG_WITH_CLASS_NAME ? "RootWindowContainer" : TAG_WM;
private static final int SET_SCREEN_BRIGHTNESS_OVERRIDE = 1;
private static final int SET_USER_ACTIVITY_TIMEOUT = 2;
static final String TAG_TASKS = TAG + POSTFIX_TASKS;
static final String TAG_STATES = TAG + POSTFIX_STATES;
private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
private Object mLastWindowFreezeSource = null;
private Session mHoldScreen = null;
private float mScreenBrightnessOverride = PowerManager.BRIGHTNESS_INVALID_FLOAT;
private long mUserActivityTimeout = -1;
private boolean mUpdateRotation = false;
// Following variables are for debugging screen wakelock only.
// Last window that requires screen wakelock
WindowState mHoldScreenWindow = null;
// Last window that obscures all windows below
WindowState mObscuringWindow = null;
// Only set while traversing the default display based on its content.
// Affects the behavior of mirroring on secondary displays.
private boolean mObscureApplicationContentOnSecondaryDisplays = false;
private boolean mSustainedPerformanceModeEnabled = false;
private boolean mSustainedPerformanceModeCurrent = false;
// During an orientation change, we track whether all windows have rendered
// at the new orientation, and this will be false from changing orientation until that occurs.
// For seamless rotation cases this always stays true, as the windows complete their orientation
// changes 1 by 1 without disturbing global state.
boolean mOrientationChangeComplete = true;
boolean mWallpaperActionPending = false;
private final Handler mHandler;
//关闭系统弹窗的原因
private String mCloseSystemDialogsReason;
// The ID of the display which is responsible for receiving display-unspecified key and pointer
// events.
//最顶部获取焦点的屏幕设备对象id
private int mTopFocusedDisplayId = INVALID_DISPLAY;
// Map from the PID to the top most app which has a focused window of the process.
//以进程号为key,ActivityRecord为value,存储了当前应用最上层获取焦点的窗口
final ArrayMap<Integer, ActivityRecord> mTopFocusedAppByProcess = new ArrayMap<>();
// Only a separate transaction until we separate the apply surface changes
// transaction from the global transaction.
//屏幕设备对应的事务
private final SurfaceControl.Transaction mDisplayTransaction;
// The tag for the token to put root tasks on the displays to sleep.
private static final String DISPLAY_OFF_SLEEP_TOKEN_TAG = "Display-off";
/** The token acquirer to put root tasks on the displays to sleep */
final ActivityTaskManagerInternal.SleepTokenAcquirer mDisplayOffTokenAcquirer;
/**
* The modes which affect which tasks are returned when calling
* {@link RootWindowContainer#anyTaskForId(int)}.
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({
MATCH_ATTACHED_TASK_ONLY,
MATCH_ATTACHED_TASK_OR_RECENT_TASKS,
MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE
})
public @interface AnyTaskForIdMatchTaskMode {
}
// Match only tasks that are attached to the hierarchy
static final int MATCH_ATTACHED_TASK_ONLY = 0;
// Match either attached tasks, or in the recent tasks if the tasks are detached
static final int MATCH_ATTACHED_TASK_OR_RECENT_TASKS = 1;
// Match either attached tasks, or in the recent tasks, restoring it to the provided task id
static final int MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE = 2;
ActivityTaskManagerService mService;
ActivityTaskSupervisor mTaskSupervisor;
WindowManagerService mWindowManager;
DisplayManager mDisplayManager;
private DisplayManagerInternal mDisplayManagerInternal;
/** Reference to default display so we can quickly look it up. */
private DisplayContent mDefaultDisplay;
private final SparseArray<IntArray> mDisplayAccessUIDs = new SparseArray<>();
/** The current user */
int mCurrentUser;
/** Root task id of the front root task when user switched, indexed by userId. */
SparseIntArray mUserRootTaskInFront = new SparseIntArray(2);
/**
* A list of tokens that cause the top activity to be put to sleep.
* They are used by components that may hide and block interaction with underlying
* activities.
*/
final SparseArray<SleepToken> mSleepTokens = new SparseArray<>();
// The default minimal size that will be used if the activity doesn't specify its minimal size.
// It will be calculated when the default display gets added.
int mDefaultMinSizeOfResizeableTaskDp = -1;
// Whether tasks have moved and we need to rank the tasks before next OOM scoring
private boolean mTaskLayersChanged = true;
private int mTmpTaskLayerRank;
private final RankTaskLayersRunnable mRankTaskLayersRunnable = new RankTaskLayersRunnable();
private boolean mTmpBoolean;
private RemoteException mTmpRemoteException;
private String mDestroyAllActivitiesReason;
private final Runnable mDestroyAllActivitiesRunnable = new Runnable() {
@Override
public void run() {
synchronized (mService.mGlobalLock) {
try {
mTaskSupervisor.beginDeferResume();
final PooledConsumer c = PooledLambda.obtainConsumer(
RootWindowContainer::destroyActivity, RootWindowContainer.this,
PooledLambda.__(ActivityRecord.class));
forAllActivities(c);
c.recycle();
} finally {
mTaskSupervisor.endDeferResume();
resumeFocusedTasksTopActivities();
}
}
}
};
private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
static class FindTaskResult implements Function<Task, Boolean> {
ActivityRecord mIdealRecord;
ActivityRecord mCandidateRecord;
private int mActivityType;
private String mTaskAffinity;
private Intent mIntent;
private ActivityInfo mInfo;
private ComponentName cls;
private int userId;
private boolean isDocument;
private Uri documentData;
void init(int activityType, String taskAffinity, Intent intent, ActivityInfo info) {
mActivityType = activityType;
mTaskAffinity = taskAffinity;
mIntent = intent;
mInfo = info;
mIdealRecord = null;
mCandidateRecord = null;
}
/**
* Returns the top activity in any existing task matching the given Intent in the input
* result. Returns null if no such task is found.
*/
void process(WindowContainer parent) {
cls = mIntent.getComponent();
if (mInfo.targetActivity != null) {
cls = new ComponentName(mInfo.packageName, mInfo.targetActivity);
}
userId = UserHandle.getUserId(mInfo.applicationInfo.uid);
isDocument = mIntent != null & mIntent.isDocument();
// If documentData is non-null then it must match the existing task data.
documentData = isDocument ? mIntent.getData() : null;
ProtoLog.d(WM_DEBUG_TASKS, "Looking for task of %s in %s", mInfo,
parent);
parent.forAllLeafTasks(this);
}
@Override
public Boolean apply(Task task) {
if (!ConfigurationContainer.isCompatibleActivityType(mActivityType,
task.getActivityType())) {
ProtoLog.d(WM_DEBUG_TASKS, "Skipping task: (mismatch activity/task) %s", task);
return false;
}
if (task.voiceSession != null) {
// We never match voice sessions; those always run independently.
ProtoLog.d(WM_DEBUG_TASKS, "Skipping %s: voice session", task);
return false;
}
if (task.mUserId != userId) {
// Looking for a different task.
ProtoLog.d(WM_DEBUG_TASKS, "Skipping %s: different user", task);
return false;
}
if (matchingCandidate(task)) {
return true;
}
// Looking for the embedded tasks (if any)
return !task.isLeafTaskFragment() && task.forAllLeafTaskFragments(
this::matchingCandidate);
}
boolean matchingCandidate(TaskFragment taskFragment) {
final Task task = taskFragment.asTask();
if (task == null) {
return false;
}
// Overlays should not be considered as the task's logical top activity.
// Activities of the tasks that embedded from this one should not be used.
final ActivityRecord r = task.getTopNonFinishingActivity(false /* includeOverlays */,
false /* includingEmbeddedTask */);
if (r == null || r.finishing || r.mUserId != userId
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
ProtoLog.d(WM_DEBUG_TASKS, "Skipping %s: mismatch root %s", task, r);
return false;
}
if (!ConfigurationContainer.isCompatibleActivityType(r.getActivityType(),
mActivityType)) {
ProtoLog.d(WM_DEBUG_TASKS, "Skipping %s: mismatch activity type", task);
return false;
}
final Intent taskIntent = task.intent;
final Intent affinityIntent = task.affinityIntent;
final boolean taskIsDocument;
final Uri taskDocumentData;
if (taskIntent != null && taskIntent.isDocument()) {
taskIsDocument = true;
taskDocumentData = taskIntent.getData();
} else if (affinityIntent != null && affinityIntent.isDocument()) {
taskIsDocument = true;
taskDocumentData = affinityIntent.getData();
} else {
taskIsDocument = false;
taskDocumentData = null;
}
ProtoLog.d(WM_DEBUG_TASKS, "Comparing existing cls=%s /aff=%s to new cls=%s /aff=%s",
r.getTask().rootAffinity, mIntent.getComponent().flattenToShortString(),
mInfo.taskAffinity, (task.realActivity != null
? task.realActivity.flattenToShortString() : ""));
// TODO Refactor to remove duplications. Check if logic can be simplified.
if (task.realActivity != null && task.realActivity.compareTo(cls) == 0
&& Objects.equals(documentData, taskDocumentData)) {
ProtoLog.d(WM_DEBUG_TASKS, "Found matching class!");
//dump();
ProtoLog.d(WM_DEBUG_TASKS, "For Intent %s bringing to top: %s", mIntent, r.intent);
mIdealRecord = r;
return true;
} else if (affinityIntent != null && affinityIntent.getComponent() != null
&& affinityIntent.getComponent().compareTo(cls) == 0 &&
Objects.equals(documentData, taskDocumentData)) {
ProtoLog.d(WM_DEBUG_TASKS, "Found matching class!");
ProtoLog.d(WM_DEBUG_TASKS, "For Intent %s bringing to top: %s", mIntent, r.intent);
mIdealRecord = r;
return true;
} else if (!isDocument && !taskIsDocument
&& mIdealRecord == null && mCandidateRecord == null
&& task.rootAffinity != null) {
if (task.rootAffinity.equals(mTaskAffinity)) {
ProtoLog.d(WM_DEBUG_TASKS, "Found matching affinity candidate!");
// It is possible for multiple tasks to have the same root affinity especially
// if they are in separate root tasks. We save off this candidate, but keep
// looking to see if there is a better candidate.
mCandidateRecord = r;
}
} else {
ProtoLog.d(WM_DEBUG_TASKS, "Not a match: %s", task);
}
return false;
}
}
private final Consumer<WindowState> mCloseSystemDialogsConsumer = w -> {
if (w.mHasSurface) {
try {
w.mClient.closeSystemDialogs(mCloseSystemDialogsReason);
} catch (RemoteException e) {
}
}
};
private static final Consumer<WindowState> sRemoveReplacedWindowsConsumer = w -> {
final ActivityRecord activity = w.mActivityRecord;
if (activity != null) {
activity.removeReplacedWindowIfNeeded(w);
}
};
}
3、RootWindowContainerd类的主要方法如下所示。
class RootWindowContainer extends WindowContainer<DisplayContent>
implements DisplayManager.DisplayListener {
// 当窗口焦点发生变化的时候,更新窗口焦点的变化 add by syl
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
Log.d("SystemBar", "RootWindowContainer_updateFocusedWindowLocked");
mTopFocusedAppByProcess.clear();
boolean changed = false;
int topFocusedDisplayId = INVALID_DISPLAY;
for (int i = mChildren.size() - 1; i >= 0; --i) {
final DisplayContent dc = mChildren.get(i);
changed |= dc.updateFocusedWindowLocked(mode, updateInputWindows, topFocusedDisplayId);
final WindowState newFocus = dc.mCurrentFocus;
if (newFocus != null) {
final int pidOfNewFocus = newFocus.mSession.mPid;
if (mTopFocusedAppByProcess.get(pidOfNewFocus) == null) {
mTopFocusedAppByProcess.put(pidOfNewFocus, newFocus.mActivityRecord);
}
if (topFocusedDisplayId == INVALID_DISPLAY) {
topFocusedDisplayId = dc.getDisplayId();
}
} else if (topFocusedDisplayId == INVALID_DISPLAY && dc.mFocusedApp != null) {
// The top-most display that has a focused app should still be the top focused
// display even when the app window is not ready yet (process not attached or
// window not added yet).
topFocusedDisplayId = dc.getDisplayId();
}
}
if (topFocusedDisplayId == INVALID_DISPLAY) {
topFocusedDisplayId = DEFAULT_DISPLAY;
}
if (mTopFocusedDisplayId != topFocusedDisplayId) {
mTopFocusedDisplayId = topFocusedDisplayId;
mWmService.mInputManager.setFocusedDisplay(topFocusedDisplayId);
mWmService.mPolicy.setTopFocusedDisplay(topFocusedDisplayId);
mWmService.mAccessibilityController.setFocusedDisplay(topFocusedDisplayId);
ProtoLog.d(WM_DEBUG_FOCUS_LIGHT, "New topFocusedDisplayId=%d", topFocusedDisplayId);
}
return changed;
}
}