Activity的预览窗口StartingWindow添加

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、简易时序图

android12-release

1、Activity组件启动

Activity组件启动后,窗口并非马上显示,而是先显示starting window,作为Activity的预览窗口。
查看 AMS:startActivity桌面启动应用 流程,接下来从ActivityStarter.java#startActivityInner()开始
在这里插入图片描述

2、ActivityStarter.java#startActivityInner() => 主要查看Task.java#startActivityLocked

  1. positionChildAtTop(rTask)当Task中的最后一个活动已被删除,或者活动管理器服务正在重用任务,将该task移到栈顶
  2. 如果allowMoveToFront=false,则不需要过渡动画和启动窗口,因为活动将不可见。
  3. SHOW_APP_STARTING_PREVIEW设置为 false 可禁用启动新活动时显示的预览。
    在这里插入图片描述
  4. 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 activity
    final 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 proceed
    ProtoLog.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

  1. 参数:prev – 包含起始窗口的上一个活动。
    startActivity – 此活动是否只是从启动器创建的。
    sourceRecord – 启动此活动的源活动。
  2. type = getStartingWindowType()获取为STARTING_WINDOW_TYPE_SPLASH_SCREEN
  3. new SplashScreenStartingData() 为StartingWindow窗口创建相关信息
  4. 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

  1. 属性static final boolean DEBUG_ENABLE_SHELL_DRAWER = SystemProperties.getBoolean("persist.debug.shell_starting_surface", true);
  2. 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

  1. 创建new PhoneWindow(context)并设置win.setType(WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);
  2. 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 localized
        if (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 display
            if (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) {
        // ignore
        Log.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

  1. 创建new ViewRootImpl(view.getContext(), display)
  2. 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 things
        try {
            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

  1. type == TYPE_APPLICATION_STARTING通过type检测activity.mStartingWindowactivity.mStartingData
  2. 创建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 userId
            userId = 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;
}

4、简易时序图

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/24520.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C/C++开发,libiec61850库学习及运用

目录 一、libiec61850库下载编译 1.1 下载 1.2 linux编译&#xff1a; 1.3 win编译 二、案例编译测试 2.1 CMakeLists.txt调整(server_example_goose) 2.2 模型static_model.h/static_model.cpp生成 2.3 案例编译(server_goose) 2.4 客户端编译 2.5 运行测试 一、libiec61850…

【Python开发】FastAPI 03:请求参数—请求体

除了路径参数和查询参数&#xff0c;还有请求体&#xff0c;其用于传递 JSON、XML 或其他格式的数据&#xff0c;以便服务器能够读取并做出相应的处理&#xff0c;可以说请求体的作用更为强大。试想一下&#xff0c;如果存在七八个参数&#xff0c;路径参数和查询是不是就招架不…

Android播放器拖动进度条的小图预览

Android播放器拖动进度条的小图预览 背景效果图关键代码1. 获取指定位置的视频帧2. 预览图的显示和隐藏 完整代码1. xml布局文件activity_video.xml2. Activity文件VideoActivity.java 背景 我们在使用一些播放器时&#xff0c;拖动进度条会有一个预览框&#xff0c;上一篇博客…

Docker容器 和 Kubernetes容器集群管理系统

一、快速了解Docker 1. 什么是Docker的定义 Docker 是一个开源的应用容器引擎&#xff0c;基于Go语言并遵从 Apache2.0 协议开源。Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中&#xff0c;然后发布到任何流行的 Linux 机器上&#xff0c;也可以…

javaScript 给图片加水印

背景 在很多地方&#xff0c;我们都可以看到&#xff0c;上传图片的时候&#xff0c;图片都会被加上默认的水印&#xff0c;水印的作用主要体现在以下几个方面&#xff1a; 1.版权保护&#xff1a;在商业用途的照片中添加水印可以帮助保护作者的版权&#xff0c;防止他人未经…

IOS复杂震动AHAP文件编辑指南

简介 目前部分游戏会在播放一些特定的音乐音效时&#xff0c;令设备产生贴合音效的复杂震动&#xff0c;给玩家一个更好的游戏体验。这种复杂震动就是通过苹果的CoreHaptics库实现的。 下面是关于CoreHaptics的官方文档 ​​​​​​​Core Haptics | Apple Developer Docum…

C++ Qt项目实战:构建高效的代码管理器

C Qt项目实战&#xff1a;构建高效的代码管理器 一、项目概述&#xff08;Introduction&#xff09;1.1 项目背景&#xff08;Project Background&#xff09;1.2 项目目标&#xff08;Project Goals&#xff09;1.3 项目应用场景&#xff08;Project Application Scenarios&am…

《操作系统》期末主观题梳理

操作系统简答题 文章目录 操作系统简答题第一章第二章第三章第四章第五章第六章第七章第八章第九章 第一章 在计算机系统上配置OS(operating system, 操作系统)的目标是什么?作用主要表现在哪几个方面? 在计算机系统上配置OS, 主要目标是实现&#xff1a;方便性、有效性、可…

加速数实融合,数据交易3.0模式上新

数据交易市场将迎来真正的突破&#xff1f; 目前看的确如此。随着去年底“数据二十条”的颁布&#xff0c;业界普遍认为数据基础制度将加速走向落地与完善&#xff0c;数据要素化今年有望迎来全面提速&#xff0c;将极大促进数据交易市场走向规模化。 IDC预测&#xff0c;到2…

css3新增特性

1. 初始化 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-width, …

【Cpp】哈希之手撕闭散列/开散列

文章目录 unorderedunordered系列关联式容器unordered_map和unordered_set概述unordered_map的文档介绍unordered_map的接口说明 底层结构 哈希哈希/散列表 概念哈希冲突哈希函数哈希函数设计原则&#xff1a;常见哈希函数 哈希冲突解决闭散列线性探测二次探测 开散列 哈希表的…

mysql学习

DISTINCT 检索不同行 该关键字的作用就是用来去重&#xff0c;可以将你所要展示的数据中完全相同的去重&#xff0c;只展示一个&#xff1b; LIMIT 限制结果 该关键字的作用就是你限制它返回几条数据&#xff0c;比如你想要获得前面5行的据&#xff0c;就可以使用limit 5&…

java 区分缺陷Defects/感染Infections/失败Failure

java 区分缺陷Defects/感染Infections/失败Failure 缺陷Defects 软件故障总是从代码中一个或多个缺陷的执行开始。 缺陷只是一段有缺陷、不正确的代码。 缺陷可能是程序语句的一部分或完整部分&#xff0c;也可能对应于不存在但应该存在的语句。 尽管程序员要对代码中的缺陷负…

利用Servlet编写第一个“hello world“(续)

利用Servlet编写第一个“hello world“ &#x1f50e;通过插件 Smart Tomcat 简化 打包代码 与 部署 操作下载Smart Tomcat配置Smart Tomcat &#x1f50e;Servlet 中的常见错误404(Not Found)&#x1f36d;请求路径出错&#x1f36d;war 包未被正确加载 405(Method Not Allowe…

【ChatGPT】ChatGPT自动生成思维导图

参考视频&#xff1a;https://edu.csdn.net/learn/38346/613917 应用场景&#xff1a;自学&#xff0c;“研一学生如何学习机器学习”的思维导图 问&#xff1a;写一个“研一学生如何学习机器学习”的思维导图内容&#xff0c;以markdown代码块格式输出 # 研一学生如何学习…

统计学的假设检验/置信区间计算

假设检验的核心其实就是反证法。反证法是数学中的一个概念&#xff0c;就是你要证明一个结论是正确的&#xff0c;那么先假设这个结论是错误的&#xff0c;然后以这个结论是错误的为前提条件进行推理&#xff0c;推理出来的结果与假设条件矛盾&#xff0c;这个时候就说明这个假…

《JavaEE》HTTPS

文章目录 HTTPS起源HTTPS对称加密非对称加密两者的区别 HTTPS的安全问题使用对称加密正常交互黑客入侵解决方案 非对称加密引入非对称加密后的流程 中间人攻击黑客的入侵方案加入后的流程解决方案黑客再次加注解决方案 ​&#x1f451;作者主页&#xff1a;Java冰激凌 &#x1…

毫米波雷达信号处理中的通道间相干与非相干积累问题

说明 相干和非相干积累是雷达信号处理中的常用方法&#xff0c;这两个概念一般是用在多脉冲积累这个问题上&#xff1a;积累可以提高信号的SNR&#xff0c;从而提高检出概率。不过本文内容与脉冲积累无关&#xff0c;本文讨论的话题是将这两个概念(non-coherent combination、c…

HCIA-MSTP替代技术之链路捆绑(LACP模式)

目录 手工链路聚合的不足&#xff1a; LACP链路聚合的原理 LACP模式&#xff1a; LACPDU&#xff1a; 1&#xff0c;设备优先级&#xff1a; 设备优先级的比较是&#xff1a;先比较优先级大小&#xff0c;0到32768&#xff0c;越小优先级越高&#xff0c;如果优先级相同&a…

OpenAI再出新作,AIGC时代,3D建模师的饭碗危险了!

大家好&#xff0c;我是千与千寻&#xff0c;也可以叫我千寻哥&#xff0c;说起来&#xff0c;自从ChatGPT发布之后&#xff0c;我就开始焦虑&#xff0c;担心自己程序员的饭碗会不会哪天就被AIGC取代了。 有人说我是过度焦虑了&#xff0c;但是我总觉有点危机感肯定没有坏处。…