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

Commit 8b096b2d authored by Garfield Tan's avatar Garfield Tan
Browse files

Pass activity stack when saving launch params.

When setParent() is called on a task with null as a step to remove it
mParent in WindowContainer is already set to null before
onParentChanged() is called. Therefore getStack() returns null and we'll
get an NPE. Thus pass ActivityStack into it to avoid that.

Bug: 147301825
Test: NPE disappears when a task is closed.
Test: atest TestRecordTest
Change-Id: I94e81f7f22d348b73eac70edd0998be721ad2597
parent 2c12aac7
Loading
Loading
Loading
Loading
+9 −6
Original line number Original line Diff line number Diff line
@@ -197,6 +197,10 @@ class LaunchParamsPersister {
    }
    }


    void saveTask(Task task) {
    void saveTask(Task task) {
        saveTask(task, task.getDisplayContent());
    }

    void saveTask(Task task, DisplayContent display) {
        final ComponentName name = task.realActivity;
        final ComponentName name = task.realActivity;
        final int userId = task.mUserId;
        final int userId = task.mUserId;
        PersistableLaunchParams params;
        PersistableLaunchParams params;
@@ -211,7 +215,7 @@ class LaunchParamsPersister {
            params = new PersistableLaunchParams();
            params = new PersistableLaunchParams();
            map.put(name, params);
            map.put(name, params);
        }
        }
        final boolean changed = saveTaskToLaunchParam(task, params);
        final boolean changed = saveTaskToLaunchParam(task, display, params);


        if (changed) {
        if (changed) {
            mPersisterQueue.updateLastOrAddItem(
            mPersisterQueue.updateLastOrAddItem(
@@ -220,17 +224,16 @@ class LaunchParamsPersister {
        }
        }
    }
    }


    private boolean saveTaskToLaunchParam(Task task, PersistableLaunchParams params) {
    private boolean saveTaskToLaunchParam(
        final ActivityStack stack = task.getStack();
            Task task, DisplayContent display, PersistableLaunchParams params) {
        final DisplayContent display = stack.getDisplayContent();
        final DisplayInfo info = new DisplayInfo();
        final DisplayInfo info = new DisplayInfo();
        display.mDisplay.getDisplayInfo(info);
        display.mDisplay.getDisplayInfo(info);


        boolean changed = !Objects.equals(params.mDisplayUniqueId, info.uniqueId);
        boolean changed = !Objects.equals(params.mDisplayUniqueId, info.uniqueId);
        params.mDisplayUniqueId = info.uniqueId;
        params.mDisplayUniqueId = info.uniqueId;


        changed |= params.mWindowingMode != stack.getWindowingMode();
        changed |= params.mWindowingMode != task.getWindowingMode();
        params.mWindowingMode = stack.getWindowingMode();
        params.mWindowingMode = task.getWindowingMode();


        if (task.mLastNonFullscreenBounds != null) {
        if (task.mLastNonFullscreenBounds != null) {
            changed |= !Objects.equals(params.mBounds, task.mLastNonFullscreenBounds);
            changed |= !Objects.equals(params.mBounds, task.mLastNonFullscreenBounds);
+16 −7
Original line number Original line Diff line number Diff line
@@ -574,13 +574,19 @@ class Task extends WindowContainer<WindowContainer> {
        mAtmService.getTaskChangeNotificationController().notifyTaskCreated(_taskId, realActivity);
        mAtmService.getTaskChangeNotificationController().notifyTaskCreated(_taskId, realActivity);
    }
    }


    private void cleanUpResourcesForDestroy() {
    private void cleanUpResourcesForDestroy(ConfigurationContainer oldParent) {
        if (hasChild()) {
        if (hasChild()) {
            return;
            return;
        }
        }


        // TODO(xutan): Removed type check after stack and task is merged.
        // Before the real merge of stack and task, we need to avoid saving state of stacks. Once
        // the merge is finished we can just pass DisplayContent because both windowing mode and
        // bounds are set in the merged task.
        if (oldParent instanceof ActivityStack) {
            // This task is going away, so save the last state if necessary.
            // This task is going away, so save the last state if necessary.
        saveLaunchingStateIfNeeded();
            saveLaunchingStateIfNeeded(((WindowContainer) oldParent).getDisplayContent());
        }


        // TODO: VI what about activity?
        // TODO: VI what about activity?
        final boolean isVoiceSession = voiceSession != null;
        final boolean isVoiceSession = voiceSession != null;
@@ -1053,9 +1059,8 @@ class Task extends WindowContainer<WindowContainer> {


        mPrevDisplayId = (oldDisplay != null) ? oldDisplay.mDisplayId : INVALID_DISPLAY;
        mPrevDisplayId = (oldDisplay != null) ? oldDisplay.mDisplayId : INVALID_DISPLAY;


        // Task is going to be removed, clean it up before detaching from hierarchy.
        if (oldParent != null && newParent == null) {
        if (oldParent != null && newParent == null) {
            cleanUpResourcesForDestroy();
            cleanUpResourcesForDestroy(oldParent);
        }
        }


        if (display != null) {
        if (display != null) {
@@ -1884,7 +1889,11 @@ class Task extends WindowContainer<WindowContainer> {
     * It only saves state if this task has been shown to user and it's in fullscreen or freeform
     * It only saves state if this task has been shown to user and it's in fullscreen or freeform
     * mode on freeform displays.
     * mode on freeform displays.
     */
     */
    void saveLaunchingStateIfNeeded() {
    private void saveLaunchingStateIfNeeded() {
        saveLaunchingStateIfNeeded(getDisplayContent());
    }

    private void saveLaunchingStateIfNeeded(DisplayContent display) {
        if (!hasBeenVisible) {
        if (!hasBeenVisible) {
            // Not ever visible to user.
            // Not ever visible to user.
            return;
            return;
@@ -1904,7 +1913,7 @@ class Task extends WindowContainer<WindowContainer> {
        }
        }


        // Saves the new state so that we can launch the activity at the same location.
        // Saves the new state so that we can launch the activity at the same location.
        mStackSupervisor.mLaunchParamsPersister.saveTask(this);
        mStackSupervisor.mLaunchParamsPersister.saveTask(this, display);
    }
    }


    /**
    /**
+1 −1
Original line number Original line Diff line number Diff line
@@ -421,7 +421,7 @@ public class LaunchParamsControllerTests extends ActivityTestsBase {
        }
        }


        @Override
        @Override
        void saveTask(Task task) {
        void saveTask(Task task, DisplayContent display) {
            final int userId = task.mUserId;
            final int userId = task.mUserId;
            final ComponentName realActivity = task.realActivity;
            final ComponentName realActivity = task.realActivity;
            mTmpParams.mPreferredDisplayId = task.getDisplayId();
            mTmpParams.mPreferredDisplayId = task.getDisplayId();
+78 −0
Original line number Original line Diff line number Diff line
@@ -20,6 +20,8 @@ import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
@@ -34,6 +36,7 @@ import static android.view.Surface.ROTATION_90;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.policy.WindowManagerPolicy.USER_ROTATION_FREE;
import static com.android.server.policy.WindowManagerPolicy.USER_ROTATION_FREE;
@@ -51,10 +54,13 @@ import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;


import android.app.ActivityManager;
import android.app.ActivityManager;
import android.app.TaskInfo;
import android.app.TaskInfo;
import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.ComponentName;
import android.content.Intent;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityInfo;
@@ -859,6 +865,78 @@ public class TaskRecordTests extends ActivityTestsBase {
        verify(task).setIntent(eq(activity0));
        verify(task).setIntent(eq(activity0));
    }
    }


    @Test
    public void testSaveLaunchingStateWhenConfigurationChanged() {
        LaunchParamsPersister persister = mService.mStackSupervisor.mLaunchParamsPersister;
        spyOn(persister);

        final Task task = getTestTask();
        task.hasBeenVisible = false;
        task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
        task.getStack().setWindowingMode(WINDOWING_MODE_FULLSCREEN);

        task.hasBeenVisible = true;
        task.onConfigurationChanged(task.getParent().getConfiguration());

        verify(persister).saveTask(task, task.getDisplayContent());
    }

    @Test
    public void testSaveLaunchingStateWhenClearingParent() {
        LaunchParamsPersister persister = mService.mStackSupervisor.mLaunchParamsPersister;
        spyOn(persister);

        final Task task = getTestTask();
        task.hasBeenVisible = false;
        task.getDisplayContent().setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
        task.getStack().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
        final DisplayContent oldDisplay = task.getDisplayContent();

        LaunchParamsController.LaunchParams params = new LaunchParamsController.LaunchParams();
        params.mWindowingMode = WINDOWING_MODE_UNDEFINED;
        persister.getLaunchParams(task, null, params);
        assertEquals(WINDOWING_MODE_UNDEFINED, params.mWindowingMode);

        task.hasBeenVisible = true;
        task.removeImmediately();

        verify(persister).saveTask(task, oldDisplay);

        persister.getLaunchParams(task, null, params);
        assertEquals(WINDOWING_MODE_FULLSCREEN, params.mWindowingMode);
    }

    @Test
    public void testNotSaveLaunchingStateNonFreeformDisplay() {
        LaunchParamsPersister persister = mService.mStackSupervisor.mLaunchParamsPersister;
        spyOn(persister);

        final Task task = getTestTask();
        task.hasBeenVisible = false;
        task.getStack().setWindowingMode(WINDOWING_MODE_FULLSCREEN);

        task.hasBeenVisible = true;
        task.onConfigurationChanged(task.getParent().getConfiguration());

        verify(persister, never()).saveTask(same(task), any());
    }

    @Test
    public void testNotSaveLaunchingStateWhenNotFullscreenOrFreeformWindow() {
        LaunchParamsPersister persister = mService.mStackSupervisor.mLaunchParamsPersister;
        spyOn(persister);

        final Task task = getTestTask();
        task.hasBeenVisible = false;
        task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
        task.getStack().setWindowingMode(WINDOWING_MODE_PINNED);

        task.hasBeenVisible = true;
        task.onConfigurationChanged(task.getParent().getConfiguration());

        verify(persister, never()).saveTask(same(task), any());
    }

    private Task getTestTask() {
    private Task getTestTask() {
        final ActivityStack stack = new StackBuilder(mRootWindowContainer).build();
        final ActivityStack stack = new StackBuilder(mRootWindowContainer).build();
        return stack.getBottomMostTask();
        return stack.getBottomMostTask();