Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 45e6d2dc authored by Chong Zhang's avatar Chong Zhang
Browse files

Clean up surfaces when app is resumed without being stopped.

When quickly toggling between two apps, app could be resumed while
it's stopping but not yet stopped. Upon resuming, it could have
surfaces that's marked mDestroying and waiting for the stopped
to be destroyed.

We need to dispose these surfaces properly. If the window is already
removed, we destroy them. Otherwise, clear mDestroying flag so that
the window is ready to be used again. Leaving mDestroying=true makes
the window ineligible for certain things such as receiving wallpaper.

bug: 30255354
Change-Id: Id881653550595ab8e702d6950949bf202ac5a0d9
parent f8bc27d9
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -173,7 +173,8 @@ interface IWindowManager
            in CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
            int icon, int logo, int windowFlags, IBinder transferFrom, boolean createIfNeeded);
    void setAppVisibility(IBinder token, boolean visible);
    void notifyAppStopped(IBinder token, boolean stopped);
    void notifyAppResumed(IBinder token, boolean wasStopped);
    void notifyAppStopped(IBinder token);
    void startAppFreezingScreen(IBinder token, int configChanges);
    void stopAppFreezingScreen(IBinder token, boolean force);
    void removeAppToken(IBinder token);
+2 −2
Original line number Diff line number Diff line
@@ -1249,7 +1249,7 @@ final class ActivityStack {
            r.stopped = true;
            r.state = ActivityState.STOPPED;

            mWindowManager.notifyAppStopped(r.appToken, true);
            mWindowManager.notifyAppStopped(r.appToken);

            if (getVisibleBehindActivity() == r) {
                mStackSupervisor.requestVisibleBehindLocked(r, false);
@@ -2490,7 +2490,7 @@ final class ActivityStack {

                // Well the app will no longer be stopped.
                // Clear app token stopped state in window manager if needed.
                mWindowManager.notifyAppStopped(next.appToken, false);
                mWindowManager.notifyAppResumed(next.appToken, next.stopped);

                EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, next.userId,
                        System.identityHashCode(next), next.task.taskId, next.shortComponentName);
+46 −15
Original line number Diff line number Diff line
@@ -337,21 +337,41 @@ class AppWindowToken extends WindowToken {
        }
    }

    // Here we destroy surfaces which have been marked as eligible by the animator, taking care
    // to ensure the client has finished with them. If the client could still be using them
    // we will skip destruction and try again when the client has stopped.
    void destroySurfaces() {
        destroySurfaces(false /*cleanupOnResume*/);
    }

    /**
     * Destroy surfaces which have been marked as eligible by the animator, taking care to ensure
     * the client has finished with them.
     *
     * @param cleanupOnResume whether this is done when app is resumed without fully stopped. If
     * set to true, destroy only surfaces of removed windows, and clear relevant flags of the
     * others so that they are ready to be reused. If set to false (common case), destroy all
     * surfaces that's eligible, if the app is already stopped.
     */

    private void destroySurfaces(boolean cleanupOnResume) {
        final ArrayList<WindowState> allWindows = (ArrayList<WindowState>) allAppWindows.clone();
        final DisplayContentList displayList = new DisplayContentList();
        for (int i = allWindows.size() - 1; i >= 0; i--) {
            final WindowState win = allWindows.get(i);

            if (!(mAppStopped || win.mWindowRemovalAllowed)) {
            if (!(mAppStopped || win.mWindowRemovalAllowed || cleanupOnResume)) {
                continue;
            }

            win.mWinAnimator.destroyPreservedSurfaceLocked();

            if (cleanupOnResume) {
                // If the window has an unfinished exit animation, consider that animation
                // done and mark the window destroying so that it goes through the cleanup.
                if (win.mAnimatingExit) {
                    win.mDestroying = true;
                    win.mAnimatingExit = false;
                }
            }

            if (!win.mDestroying) {
                continue;
            }
@@ -361,7 +381,9 @@ class AppWindowToken extends WindowToken {
                    + " win.mWindowRemovalAllowed=" + win.mWindowRemovalAllowed
                    + " win.mRemoveOnExit=" + win.mRemoveOnExit);

            if (!cleanupOnResume || win.mRemoveOnExit) {
                win.destroyOrSaveSurface();
            }
            if (win.mRemoveOnExit) {
                service.removeWindowInnerLocked(win);
            }
@@ -379,19 +401,28 @@ class AppWindowToken extends WindowToken {
    }

    /**
     * If the application has stopped it is okay to destroy any surfaces which were keeping alive
     * in case they were still being used.
     * Notify that the app is now resumed, and it was not stopped before, perform a clean
     * up of the surfaces
     */
    void notifyAppStopped(boolean stopped) {
        if (DEBUG_ADD_REMOVE) Slog.v(TAG, "notifyAppStopped: stopped=" + stopped + " " + this);
        mAppStopped = stopped;
    void notifyAppResumed(boolean wasStopped) {
        if (DEBUG_ADD_REMOVE) Slog.v(TAG, "notifyAppResumed: wasStopped=" + wasStopped + " " + this);
        mAppStopped = false;
        if (!wasStopped) {
            destroySurfaces(true /*cleanupOnResume*/);
        }
    }

        if (stopped) {
    /**
     * Notify that the app has stopped, and it is okay to destroy any surfaces which were
     * keeping alive in case they were still being used.
     */
    void notifyAppStopped() {
        if (DEBUG_ADD_REMOVE) Slog.v(TAG, "notifyAppStopped: " + this);
        mAppStopped = true;
        destroySurfaces();
        // Remove any starting window that was added for this app if they are still around.
        mTask.mService.scheduleRemoveStartingWindowLocked(this);
    }
    }

    /**
     * Checks whether we should save surfaces for this app.
+21 −3
Original line number Diff line number Diff line
@@ -4476,7 +4476,25 @@ public class WindowManagerService extends IWindowManager.Stub
    }

    @Override
    public void notifyAppStopped(IBinder token, boolean stopped) {
    public void notifyAppResumed(IBinder token, boolean wasStopped) {
        if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                "notifyAppResumed()")) {
            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
        }

        synchronized(mWindowMap) {
            final AppWindowToken wtoken;
            wtoken = findAppWindowToken(token);
            if (wtoken == null) {
                Slog.w(TAG_WM, "Attempted to notify resumed of non-existing app token: " + token);
                return;
            }
            wtoken.notifyAppResumed(wasStopped);
        }
    }

    @Override
    public void notifyAppStopped(IBinder token) {
        if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                "notifyAppStopped()")) {
            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
@@ -4486,10 +4504,10 @@ public class WindowManagerService extends IWindowManager.Stub
            final AppWindowToken wtoken;
            wtoken = findAppWindowToken(token);
            if (wtoken == null) {
                Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: " + token);
                Slog.w(TAG_WM, "Attempted to notify stopped of non-existing app token: " + token);
                return;
            }
            wtoken.notifyAppStopped(stopped);
            wtoken.notifyAppStopped();
        }
    }

+6 −1
Original line number Diff line number Diff line
@@ -351,7 +351,12 @@ public class IWindowManagerImpl implements IWindowManager {
    }

    @Override
    public void notifyAppStopped(IBinder token, boolean stopped) throws RemoteException {
    public void notifyAppResumed(IBinder token, boolean wasStopped) throws RemoteException {
        // TODO Auto-generated method stub
    }

    @Override
    public void notifyAppStopped(IBinder token) throws RemoteException {
        // TODO Auto-generated method stub
    }