Activity的预览窗口StartingWindow添加
- 1、Activity组件启动
- 2、ActivityStarter.java#startActivityInner() => 主要查看Task.java#startActivityLocked
- 3、ActivityRecord.java#addStartingWindow到WindowManagerService.java#addWindow
- 3.1 ActivityRecord.java#addStartingWindow
- 3.2 StartingSurfaceController.java#createStartingSurface
- 3.3 PhoneWindowManager.java#addSplashScreen
- 3.4 WindowManagerGlobal.java#addView
- 3.5 WindowManagerService.java#addWindow
- 4、简易时序图
1、Activity组件启动
Activity组件启动后,窗口并非马上显示,而是先显示starting window,作为Activity的预览窗口。
查看 AMS:startActivity桌面启动应用 流程,接下来从ActivityStarter.java#startActivityInner()
开始
2、ActivityStarter.java#startActivityInner() => 主要查看Task.java#startActivityLocked
positionChildAtTop(rTask)
当Task中的最后一个活动已被删除,或者活动管理器服务正在重用任务,将该task移到栈顶- 如果allowMoveToFront=false,则不需要过渡动画和启动窗口,因为活动将不可见。
SHOW_APP_STARTING_PREVIEW
设置为 false 可禁用启动新活动时显示的预览。
r.showStartingWindow()
创建添加界面,最终调用到ActivityRecord.java#addStartingWindow ==> PhoneWindowManager.java#addSplashScreen
void startActivityLocked(ActivityRecord r, @Nullable ActivityRecord focusedTopActivity,boolean newTask, boolean keepCurTransition, ActivityOptions options,@Nullable ActivityRecord sourceRecord) {Task rTask = r.getTask();final boolean allowMoveToFront = options == null || !options.getAvoidMoveToFront();final boolean isOrhasTask = rTask == this || hasChild(rTask);// mLaunchTaskBehind tasks get placed at the back of the task stack.if (!r.mLaunchTaskBehind && allowMoveToFront && (!isOrhasTask || newTask)) {// Last activity in task had been removed or ActivityManagerService is reusing task.// Insert or replace.// Might not even be in.positionChildAtTop(rTask);}Task task = null;if (!newTask && isOrhasTask) {// Starting activity cannot be occluding activity, otherwise starting window could be// remove immediately without transferring to starting activity.final ActivityRecord occludingActivity = getOccludingActivityAbove(r);if (occludingActivity != null) {// Here it is! Now, if this is not yet visible (occluded by another task) to the// user, then just add it without starting; it will get started when the user// navigates back to it.ProtoLog.i(WM_DEBUG_ADD_REMOVE, "Adding activity %s to task %s "+ "callers: %s", r, task,new RuntimeException("here").fillInStackTrace());rTask.positionChildAtTop(r);ActivityOptions.abort(options);return;}}// Place a new activity at top of root task, so it is next to interact with the user.// If we are not placing the new activity frontmost, we do not want to deliver the// onUserLeaving callback to the actual frontmost activityfinal Task activityTask = r.getTask();if (task == activityTask && mChildren.indexOf(task) != (getChildCount() - 1)) {mTaskSupervisor.mUserLeaving = false;if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,"startActivity() behind front, mUserLeaving=false");}task = activityTask;// Slot the activity into the history root task and proceedProtoLog.i(WM_DEBUG_ADD_REMOVE, "Adding activity %s to task %s "+ "callers: %s", r, task, new RuntimeException("here").fillInStackTrace());task.positionChildAtTop(r);// The transition animation and starting window are not needed if {@code allowMoveToFront}// is false, because the activity won't be visible.if ((!isHomeOrRecentsRootTask() || hasActivity()) && allowMoveToFront) {final DisplayContent dc = mDisplayContent;if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,"Prepare open transition: starting " + r);// TODO(shell-transitions): record NO_ANIMATION flag somewhere.if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {dc.prepareAppTransition(TRANSIT_NONE);mTaskSupervisor.mNoAnimActivities.add(r);} else {int transit = TRANSIT_OLD_ACTIVITY_OPEN;if (newTask) {if (r.mLaunchTaskBehind) {transit = TRANSIT_OLD_TASK_OPEN_BEHIND;} else {// If a new task is being launched, then mark the existing top activity as// supporting picture-in-picture while pausing only if the starting activity// would not be considered an overlay on top of the current activity// (eg. not fullscreen, or the assistant)if (canEnterPipOnTaskSwitch(focusedTopActivity,null /* toFrontTask */, r, options)) {focusedTopActivity.supportsEnterPipOnTaskSwitch = true;}transit = TRANSIT_OLD_TASK_OPEN;}}dc.prepareAppTransition(TRANSIT_OPEN);mTaskSupervisor.mNoAnimActivities.remove(r);}boolean doShow = true;if (newTask) {// Even though this activity is starting fresh, we still need// to reset it to make sure we apply affinities to move any// existing activities from other tasks in to it.// If the caller has requested that the target task be// reset, then do so.if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {resetTaskIfNeeded(r, r);doShow = topRunningNonDelayedActivityLocked(null) == r;}} else if (options != null && options.getAnimationType()== ActivityOptions.ANIM_SCENE_TRANSITION) {doShow = false;}if (r.mLaunchTaskBehind) {// Don't do a starting window for mLaunchTaskBehind. More importantly make sure we// tell WindowManager that r is visible even though it is at the back of the root// task.r.setVisibility(true);ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);// Go ahead to execute app transition for this activity since the app transition// will not be triggered through the resume channel.mDisplayContent.executeAppTransition();} else if (SHOW_APP_STARTING_PREVIEW && doShow) {// Figure out if we are transitioning from another activity that is// "has the same starting icon" as the next one. This allows the// window manager to keep the previous window it had previously// created, if it still had one.Task prevTask = r.getTask();ActivityRecord prev = prevTask.topActivityWithStartingWindow();if (prev != null) {// We don't want to reuse the previous starting preview if:// (1) The current activity is in a different task.if (prev.getTask() != prevTask) {prev = null;}// (2) The current activity is already displayed.else if (prev.nowVisible) {prev = null;}}r.showStartingWindow(prev, newTask, isTaskSwitch(r, focusedTopActivity),true /* startActivity */, sourceRecord);}} else {// If this is the first activity, don't do any fancy animations,// because there is nothing for it to animate on top of.ActivityOptions.abort(options);}
}
3、ActivityRecord.java#addStartingWindow到WindowManagerService.java#addWindow
3.1 ActivityRecord.java#addStartingWindow
- 参数:
prev
– 包含起始窗口的上一个活动。
startActivity
– 此活动是否只是从启动器创建的。
sourceRecord
– 启动此活动的源活动。type = getStartingWindowType()
获取为STARTING_WINDOW_TYPE_SPLASH_SCREEN
new SplashScreenStartingData()
为StartingWindow窗口创建相关信息scheduleAddStartingWindow()
通过Handler执行AddStartingWindow创建StartingSurfaceController.javam#createSplashScreenStartingSurface => Service.mPolicy.addSplashScreen()
/*** @param prev Previous activity which contains a starting window.* @param startActivity Whether this activity is just created from starter.* @param sourceRecord The source activity which start this activity.*/
void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch,boolean startActivity, ActivityRecord sourceRecord) {if (mTaskOverlay) {// We don't show starting window for overlay activities.return;}if (mPendingOptions != null&& mPendingOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {// Don't show starting window when using shared element transition.return;}final CompatibilityInfo compatInfo =mAtmService.compatibilityInfoForPackageLocked(info.applicationInfo);mSplashScreenStyleEmpty = shouldUseEmptySplashScreen(sourceRecord, startActivity);final int splashScreenTheme = startActivity ? getSplashscreenTheme() : 0;final int resolvedTheme = evaluateStartingWindowTheme(prev, packageName, theme,splashScreenTheme);final boolean activityCreated =mState.ordinal() >= STARTED.ordinal() && mState.ordinal() <= STOPPED.ordinal();// If this activity is just created and all activities below are finish, treat this// scenario as warm launch.final boolean newSingleActivity = !newTask && !activityCreated&& task.getActivity((r) -> !r.finishing && r != this) == null;final boolean shown = addStartingWindow(packageName, resolvedTheme,compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,prev != null ? prev.appToken : null,newTask || newSingleActivity, taskSwitch, isProcessRunning(),allowTaskSnapshot(), activityCreated, mSplashScreenStyleEmpty);if (shown) {mStartingWindowState = STARTING_WINDOW_SHOWN;}
}
3.2 StartingSurfaceController.java#createStartingSurface
- 属性
static final boolean DEBUG_ENABLE_SHELL_DRAWER = SystemProperties.getBoolean("persist.debug.shell_starting_surface", true);
mService.mPolicy.addSplashScreen()
代理类Policy->PhoneWindowManager.java#addSplashScreen
static final boolean DEBUG_ENABLE_SHELL_DRAWER =SystemProperties.getBoolean("persist.debug.shell_starting_surface", true);StartingSurface createSplashScreenStartingSurface(ActivityRecord activity, String packageName,int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,int icon, int logo, int windowFlags, Configuration overrideConfig, int displayId) {if (!DEBUG_ENABLE_SHELL_DRAWER) {return mService.mPolicy.addSplashScreen(activity.token, activity.mUserId, packageName,theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,overrideConfig, displayId);}synchronized (mService.mGlobalLock) {final Task task = activity.getTask();if (task != null && mService.mAtmService.mTaskOrganizerController.addStartingWindow(task, activity, theme, null /* taskSnapshot */)) {return new ShellStartingSurface(task);}}return null;
}
3.3 PhoneWindowManager.java#addSplashScreen
- 创建
new PhoneWindow(context)
并设置win.setType(WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);
view = win.getDecorView()
获取DecorView,并通过WindowManager设置添加界面wm.addView(view, params)
,这里获取的是WindowManagerImpl.java
;最终调用到WindowManagerGlobal.java#addView -> ViewRootImpl.java#setView -> Session extends IWindowSession.Stub#addToDisplayAsUser -> WindowManagerService.java#addWindow
public StartingSurface addSplashScreen(IBinder appToken, int userId, String packageName,int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,int icon, int logo, int windowFlags, Configuration overrideConfig, int displayId) {if (!SHOW_SPLASH_SCREENS) {return null;}if (packageName == null) {return null;}WindowManager wm = null;View view = null;try {Context context = mContext;if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "addSplashScreen " + packageName+ ": nonLocalizedLabel=" + nonLocalizedLabel + " theme="+ Integer.toHexString(theme));// Obtain proper context to launch on the right display.final Context displayContext = getDisplayContext(context, displayId);if (displayContext == null) {// Can't show splash screen on requested display, so skip showing at all.return null;}context = displayContext;if (theme != context.getThemeResId() || labelRes != 0) {try {context = context.createPackageContextAsUser(packageName, CONTEXT_RESTRICTED,UserHandle.of(userId));context.setTheme(theme);} catch (PackageManager.NameNotFoundException e) {Slog.w(TAG, "Failed creating package context with package name "+ packageName + " for user " + userId, e);}}if (overrideConfig != null && !overrideConfig.equals(EMPTY)) {if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "addSplashScreen: creating context based"+ " on overrideConfig" + overrideConfig + " for splash screen");final Context overrideContext = context.createConfigurationContext(overrideConfig);overrideContext.setTheme(theme);final TypedArray typedArray = overrideContext.obtainStyledAttributes(com.android.internal.R.styleable.Window);final int resId = typedArray.getResourceId(R.styleable.Window_windowBackground, 0);if (resId != 0 && overrideContext.getDrawable(resId) != null) {// We want to use the windowBackground for the override context if it is// available, otherwise we use the default one to make sure a themed starting// window is displayed for the app.if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "addSplashScreen: apply overrideConfig"+ overrideConfig + " to starting window resId=" + resId);context = overrideContext;}typedArray.recycle();}final PhoneWindow win = new PhoneWindow(context);win.setIsStartingWindow(true);CharSequence label = context.getResources().getText(labelRes, null);// Only change the accessibility title if the label is localizedif (label != null) {win.setTitle(label, true);} else {win.setTitle(nonLocalizedLabel, false);}win.setType(WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);synchronized (mWindowManagerFuncs.getWindowManagerLock()) {// Assumes it's safe to show starting windows of launched apps while// the keyguard is being hidden. This is okay because starting windows never show// secret information.// TODO(b/113840485): Occluded may not only happen on default displayif (displayId == DEFAULT_DISPLAY && mKeyguardOccluded) {windowFlags |= FLAG_SHOW_WHEN_LOCKED;}}// Force the window flags: this is a fake window, so it is not really// touchable or focusable by the user. We also add in the ALT_FOCUSABLE_IM// flag because we do know that the next window will take input// focus, so we want to get the IME window up on top of us right away.win.setFlags(windowFlags|WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE|WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,windowFlags|WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE|WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);win.setDefaultIcon(icon);win.setDefaultLogo(logo);win.setLayout(WindowManager.LayoutParams.MATCH_PARENT,WindowManager.LayoutParams.MATCH_PARENT);final WindowManager.LayoutParams params = win.getAttributes();params.token = appToken;params.packageName = packageName;params.windowAnimations = win.getWindowStyle().getResourceId(com.android.internal.R.styleable.Window_windowAnimationStyle, 0);params.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;// Setting as trusted overlay to let touches pass through. This is safe because this// window is controlled by the system.params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;if (!compatInfo.supportsScreen()) {params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;}params.setTitle("Splash Screen " + packageName);addSplashscreenContent(win, context);wm = (WindowManager) context.getSystemService(WINDOW_SERVICE);view = win.getDecorView();if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "Adding splash screen window for "+ packageName + " / " + appToken + ": " + (view.getParent() != null ? view : null));wm.addView(view, params);// Only return the view if it was successfully added to the// window manager... which we can tell by it having a parent.return view.getParent() != null ? new SplashScreenSurface(view, appToken) : null;} catch (WindowManager.BadTokenException e) {// ignoreLog.w(TAG, appToken + " already running, starting window not displayed. " +e.getMessage());} catch (RuntimeException e) {// don't crash if something else bad happens, for example a// failure loading resources because we are loading from an app// on external storage that has been unmounted.Log.w(TAG, appToken + " failed creating starting window", e);} finally {if (view != null && view.getParent() == null) {Log.w(TAG, "view not successfully added to wm, removing view");wm.removeViewImmediate(view);}}return null;
}
3.4 WindowManagerGlobal.java#addView
- 创建
new ViewRootImpl(view.getContext(), display)
root.setView(view, wparams, panelParentView, userId)
设置添加界面
public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow, int userId) {if (view == null) {throw new IllegalArgumentException("view must not be null");}if (display == null) {throw new IllegalArgumentException("display must not be null");}if (!(params instanceof WindowManager.LayoutParams)) {throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");}final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;if (parentWindow != null) {parentWindow.adjustLayoutParamsForSubWindow(wparams);} else {// If there's no parent, then hardware acceleration for this view is// set from the application's hardware acceleration setting.final Context context = view.getContext();if (context != null&& (context.getApplicationInfo().flags& ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;}}ViewRootImpl root;View panelParentView = null;synchronized (mLock) {// Start watching for system property changes.if (mSystemPropertyUpdater == null) {mSystemPropertyUpdater = new Runnable() {@Override public void run() {synchronized (mLock) {for (int i = mRoots.size() - 1; i >= 0; --i) {mRoots.get(i).loadSystemProperties();}}}};SystemProperties.addChangeCallback(mSystemPropertyUpdater);}int index = findViewLocked(view, false);if (index >= 0) {if (mDyingViews.contains(view)) {// Don't wait for MSG_DIE to make it's way through root's queue.mRoots.get(index).doDie();} else {throw new IllegalStateException("View " + view+ " has already been added to the window manager.");}// The previous removeView() had not completed executing. Now it has.}// If this is a panel window, then find the window it is being// attached to for future reference.if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {final int count = mViews.size();for (int i = 0; i < count; i++) {if (mRoots.get(i).mWindow.asBinder() == wparams.token) {panelParentView = mViews.get(i);}}}root = new ViewRootImpl(view.getContext(), display);view.setLayoutParams(wparams);mViews.add(view);mRoots.add(root);mParams.add(wparams);// do this last because it fires off messages to start doing thingstry {root.setView(view, wparams, panelParentView, userId);} catch (RuntimeException e) {// BadTokenException or InvalidDisplayException, clean up.if (index >= 0) {removeViewLocked(index, true);}throw e;}}
}
3.5 WindowManagerService.java#addWindow
type == TYPE_APPLICATION_STARTING
通过type检测activity.mStartingWindow
、activity.mStartingData
- 创建
win = new WindowState()
addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,int displayId, int requestUserId, InsetsState requestedVisibility,InputChannel outInputChannel, InsetsState outInsetsState,InsetsSourceControl[] outActiveControls) {Arrays.fill(outActiveControls, null);int[] appOp = new int[1];final boolean isRoundedCornerOverlay = (attrs.privateFlags& PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName,appOp);if (res != ADD_OKAY) {return res;}WindowState parentWindow = null;final int callingUid = Binder.getCallingUid();final int callingPid = Binder.getCallingPid();final long origId = Binder.clearCallingIdentity();final int type = attrs.type;synchronized (mGlobalLock) {if (!mDisplayReady) {throw new IllegalStateException("Display has not been initialialized");}final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);if (displayContent == null) {ProtoLog.w(WM_ERROR, "Attempted to add window to a display that does "+ "not exist: %d. Aborting.", displayId);return WindowManagerGlobal.ADD_INVALID_DISPLAY;}if (!displayContent.hasAccess(session.mUid)) {ProtoLog.w(WM_ERROR,"Attempted to add window to a display for which the application "+ "does not have access: %d. Aborting.",displayContent.getDisplayId());return WindowManagerGlobal.ADD_INVALID_DISPLAY;}if (mWindowMap.containsKey(client.asBinder())) {ProtoLog.w(WM_ERROR, "Window %s is already added", client);return WindowManagerGlobal.ADD_DUPLICATE_ADD;}if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {parentWindow = windowForClientLocked(null, attrs.token, false);if (parentWindow == null) {ProtoLog.w(WM_ERROR, "Attempted to add window with token that is not a window: "+ "%s. Aborting.", attrs.token);return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;}if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW&& parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {ProtoLog.w(WM_ERROR, "Attempted to add window with token that is a sub-window: "+ "%s. Aborting.", attrs.token);return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;}}if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) {ProtoLog.w(WM_ERROR,"Attempted to add private presentation window to a non-private display. "+ "Aborting.");return WindowManagerGlobal.ADD_PERMISSION_DENIED;}if (type == TYPE_PRESENTATION && !displayContent.getDisplay().isPublicPresentation()) {ProtoLog.w(WM_ERROR,"Attempted to add presentation window to a non-suitable display. "+ "Aborting.");return WindowManagerGlobal.ADD_INVALID_DISPLAY;}int userId = UserHandle.getUserId(session.mUid);if (requestUserId != userId) {try {mAmInternal.handleIncomingUser(callingPid, callingUid, requestUserId,false /*allowAll*/, ALLOW_NON_FULL, null, null);} catch (Exception exp) {ProtoLog.w(WM_ERROR, "Trying to add window with invalid user=%d",requestUserId);return WindowManagerGlobal.ADD_INVALID_USER;}// It's fine to use this userIduserId = requestUserId;}ActivityRecord activity = null;final boolean hasParent = parentWindow != null;// Use existing parent window token for child windows since they go in the same token// as there parent window so we can apply the same policy on them.WindowToken token = displayContent.getWindowToken(hasParent ? parentWindow.mAttrs.token : attrs.token);// If this is a child window, we want to apply the same type checking rules as the// parent window type.final int rootType = hasParent ? parentWindow.mAttrs.type : type;boolean addToastWindowRequiresToken = false;final IBinder windowContextToken = attrs.mWindowContextToken;if (token == null) {if (!unprivilegedAppCanCreateTokenWith(parentWindow, callingUid, type,rootType, attrs.token, attrs.packageName)) {return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}if (hasParent) {// Use existing parent window token for child windows.token = parentWindow.mToken;} else if (mWindowContextListenerController.hasListener(windowContextToken)) {// Respect the window context token if the user provided it.final IBinder binder = attrs.token != null ? attrs.token : windowContextToken;final Bundle options = mWindowContextListenerController.getOptions(windowContextToken);token = new WindowToken.Builder(this, binder, type).setDisplayContent(displayContent).setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow).setRoundedCornerOverlay(isRoundedCornerOverlay).setFromClientToken(true).setOptions(options).build();} else {final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();token = new WindowToken.Builder(this, binder, type).setDisplayContent(displayContent).setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow).setRoundedCornerOverlay(isRoundedCornerOverlay).build();}} else if (rootType >= FIRST_APPLICATION_WINDOW&& rootType <= LAST_APPLICATION_WINDOW) {activity = token.asActivityRecord();if (activity == null) {ProtoLog.w(WM_ERROR, "Attempted to add window with non-application token "+ ".%s Aborting.", token);return WindowManagerGlobal.ADD_NOT_APP_TOKEN;} else if (activity.getParent() == null) {ProtoLog.w(WM_ERROR, "Attempted to add window with exiting application token "+ ".%s Aborting.", token);return WindowManagerGlobal.ADD_APP_EXITING;} else if (type == TYPE_APPLICATION_STARTING) {if (activity.mStartingWindow != null) {ProtoLog.w(WM_ERROR, "Attempted to add starting window to "+ "token with already existing starting window");return WindowManagerGlobal.ADD_DUPLICATE_ADD;}if (activity.mStartingData == null) {ProtoLog.w(WM_ERROR, "Attempted to add starting window to "+ "token but already cleaned");return WindowManagerGlobal.ADD_DUPLICATE_ADD;}}} else if (rootType == TYPE_INPUT_METHOD) {if (token.windowType != TYPE_INPUT_METHOD) {ProtoLog.w(WM_ERROR, "Attempted to add input method window with bad token "+ "%s. Aborting.", attrs.token);return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}} else if (rootType == TYPE_VOICE_INTERACTION) {if (token.windowType != TYPE_VOICE_INTERACTION) {ProtoLog.w(WM_ERROR, "Attempted to add voice interaction window with bad token "+ "%s. Aborting.", attrs.token);return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}} else if (rootType == TYPE_WALLPAPER) {if (token.windowType != TYPE_WALLPAPER) {ProtoLog.w(WM_ERROR, "Attempted to add wallpaper window with bad token "+ "%s. Aborting.", attrs.token);return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}} else if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {if (token.windowType != TYPE_ACCESSIBILITY_OVERLAY) {ProtoLog.w(WM_ERROR,"Attempted to add Accessibility overlay window with bad token "+ "%s. Aborting.", attrs.token);return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}} else if (type == TYPE_TOAST) {// Apps targeting SDK above N MR1 cannot arbitrary add toast windows.addToastWindowRequiresToken = doesAddToastWindowRequireToken(attrs.packageName,callingUid, parentWindow);if (addToastWindowRequiresToken && token.windowType != TYPE_TOAST) {ProtoLog.w(WM_ERROR, "Attempted to add a toast window with bad token "+ "%s. Aborting.", attrs.token);return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}} else if (type == TYPE_QS_DIALOG) {if (token.windowType != TYPE_QS_DIALOG) {ProtoLog.w(WM_ERROR, "Attempted to add QS dialog window with bad token "+ "%s. Aborting.", attrs.token);return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}} else if (token.asActivityRecord() != null) {ProtoLog.w(WM_ERROR, "Non-null activity for system window of rootType=%d",rootType);// It is not valid to use an app token with other system types; we will// instead make a new token for it (as if null had been passed in for the token).attrs.token = null;token = new WindowToken.Builder(this, client.asBinder(), type).setDisplayContent(displayContent).setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow).build();}final WindowState win = new WindowState(this, session, client, token, parentWindow,appOp[0], attrs, viewVisibility, session.mUid, userId,session.mCanAddInternalSystemWindow);if (win.mDeathRecipient == null) {// Client has apparently died, so there is no reason to// continue.ProtoLog.w(WM_ERROR, "Adding window client %s"+ " that is dead, aborting.", client.asBinder());return WindowManagerGlobal.ADD_APP_EXITING;}if (win.getDisplayContent() == null) {ProtoLog.w(WM_ERROR, "Adding window to Display that has been removed.");return WindowManagerGlobal.ADD_INVALID_DISPLAY;}final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();displayPolicy.adjustWindowParamsLw(win, win.mAttrs);win.updateRequestedVisibility(requestedVisibility);res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid);if (res != ADD_OKAY) {return res;}final boolean openInputChannels = (outInputChannel != null&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);if (openInputChannels) {win.openInputChannel(outInputChannel);}// If adding a toast requires a token for this app we always schedule hiding// toast windows to make sure they don't stick around longer then necessary.// We hide instead of remove such windows as apps aren't prepared to handle// windows being removed under them.//// If the app is older it can add toasts without a token and hence overlay// other apps. To be maximally compatible with these apps we will hide the// window after the toast timeout only if the focused window is from another// UID, otherwise we allow unlimited duration. When a UID looses focus we// schedule hiding all of its toast windows.if (type == TYPE_TOAST) {if (!displayContent.canAddToastWindowForUid(callingUid)) {ProtoLog.w(WM_ERROR, "Adding more than one toast window for UID at a time.");return WindowManagerGlobal.ADD_DUPLICATE_ADD;}// Make sure this happens before we moved focus as one can make the// toast focusable to force it not being hidden after the timeout.// Focusable toasts are always timed out to prevent a focused app to// show a focusable toasts while it has focus which will be kept on// the screen after the activity goes away.if (addToastWindowRequiresToken|| (attrs.flags & FLAG_NOT_FOCUSABLE) == 0|| displayContent.mCurrentFocus == null|| displayContent.mCurrentFocus.mOwnerUid != callingUid) {mH.sendMessageDelayed(mH.obtainMessage(H.WINDOW_HIDE_TIMEOUT, win),win.mAttrs.hideTimeoutMilliseconds);}}// Switch to listen to the {@link WindowToken token}'s configuration changes when// adding a window to the window context. Filter sub window type here because the sub// window must be attached to the parent window, which is attached to the window context// created window token.if (!win.isChildWindow()&& mWindowContextListenerController.hasListener(windowContextToken)) {final int windowContextType = mWindowContextListenerController.getWindowType(windowContextToken);if (type != windowContextType) {ProtoLog.w(WM_ERROR, "Window types in WindowContext and"+ " LayoutParams.type should match! Type from LayoutParams is %d,"+ " but type from WindowContext is %d", type, windowContextType);return WindowManagerGlobal.ADD_INVALID_TYPE;}final Bundle options = mWindowContextListenerController.getOptions(windowContextToken);mWindowContextListenerController.registerWindowContainerListener(windowContextToken, token, callingUid, type, options);}// From now on, no exceptions or errors allowed!res = ADD_OKAY;if (mUseBLAST) {res |= WindowManagerGlobal.ADD_FLAG_USE_BLAST;}if (displayContent.mCurrentFocus == null) {displayContent.mWinAddedSinceNullFocus.add(win);}if (excludeWindowTypeFromTapOutTask(type)) {displayContent.mTapExcludedWindows.add(win);}win.attach();mWindowMap.put(client.asBinder(), win);win.initAppOpsState();final boolean suspended = mPmInternal.isPackageSuspended(win.getOwningPackage(),UserHandle.getUserId(win.getOwningUid()));win.setHiddenWhileSuspended(suspended);final boolean hideSystemAlertWindows = !mHidingNonSystemOverlayWindows.isEmpty();win.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows);final ActivityRecord tokenActivity = token.asActivityRecord();if (type == TYPE_APPLICATION_STARTING && tokenActivity != null) {tokenActivity.mStartingWindow = win;ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "addWindow: %s startingWindow=%s",activity, win);}boolean imMayMove = true;win.mToken.addWindow(win);displayPolicy.addWindowLw(win, attrs);if (type == TYPE_INPUT_METHOD) {displayContent.setInputMethodWindowLocked(win);imMayMove = false;} else if (type == TYPE_INPUT_METHOD_DIALOG) {displayContent.computeImeTarget(true /* updateImeTarget */);imMayMove = false;} else {if (type == TYPE_WALLPAPER) {displayContent.mWallpaperController.clearLastWallpaperTimeoutTime();displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;} else if (win.hasWallpaper()) {displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;} else if (displayContent.mWallpaperController.isBelowWallpaperTarget(win)) {// If there is currently a wallpaper being shown, and// the base layer of the new window is below the current// layer of the target window, then adjust the wallpaper.// This is to avoid a new window being placed between the// wallpaper and its target.displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;}}final WindowStateAnimator winAnimator = win.mWinAnimator;winAnimator.mEnterAnimationPending = true;winAnimator.mEnteringAnimation = true;// Check if we need to prepare a transition for replacing window first.if (activity != null && activity.isVisible()&& !prepareWindowReplacementTransition(activity)) {// If not, check if need to set up a dummy transition during display freeze// so that the unfreeze wait for the apps to draw. This might be needed if// the app is relaunching.prepareNoneTransitionForRelaunching(activity);}if (displayPolicy.getLayoutHint(win.mAttrs, token, outInsetsState,win.isClientLocal())) {res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS;}if (mInTouchMode) {res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;}if (win.mActivityRecord == null || win.mActivityRecord.isClientVisible()) {res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;}displayContent.getInputMonitor().setUpdateInputWindowsNeededLw();boolean focusChanged = false;if (win.canReceiveKeys()) {focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,false /*updateInputWindows*/);if (focusChanged) {imMayMove = false;}}if (imMayMove) {displayContent.computeImeTarget(true /* updateImeTarget */);}// Don't do layout here, the window must call// relayout to be displayed, so we'll do it there.win.getParent().assignChildLayers();if (focusChanged) {displayContent.getInputMonitor().setInputFocusLw(displayContent.mCurrentFocus,false /*updateInputWindows*/);}displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/);ProtoLog.v(WM_DEBUG_ADD_REMOVE, "addWindow: New client %s"+ ": window=%s Callers=%s", client.asBinder(), win, Debug.getCallers(5));if (win.isVisibleOrAdding() && displayContent.updateOrientation()) {displayContent.sendNewConfiguration();}// This window doesn't have a frame yet. Don't let this window cause the insets change.displayContent.getInsetsStateController().updateAboveInsetsState(win, false /* notifyInsetsChanged */);getInsetsSourceControls(win, outActiveControls);}Binder.restoreCallingIdentity(origId);return res;
}