From f81c1d164ab5999ec554c9cb2659e1b2ed93a8d5 Mon Sep 17 00:00:00 2001 From: Wale Ogunwale Date: Tue, 12 Jan 2016 12:20:18 -0800 Subject: [PATCH] Fixed some activity visiblility issues in picture-in-picutre mode - Previously when don't re-launch an activity due to configuration change if the activity is currently pausing. And, once the pause is complete we destroy the activity. This logic is based on the assumption that all activities are fullscreen and pausing is the same as stopping which means the activity is no longer visible and can be destoried. This assumption is not true in multi-window mode where you can have visible activities in the paused state. We now relaunch the activity once it is done pausing. - Previously we set the return type of the top task in a stack to home if the previously focused stack is home while add the task to the stack. This logic is based on the assumption that the focus stack is the front stack which isn't true for pinned stack. This causes an activity behind the top translucent activity in the pinned stack to be marked as invisible and stopped since the top task is over the home task so we should be showing the home task behind it and not other tasks in the stack. We now set the return to task type to application type for task added to the pinned stack. Bug: 26273032 Change-Id: I0ffac81f46c57e2d0d900db3417381f059aee7ea --- core/java/android/app/ActivityManager.java | 8 +++ .../com/android/server/am/ActivityRecord.java | 14 +++-- .../com/android/server/am/ActivityStack.java | 58 +++++++++---------- .../server/am/ActivityStackSupervisor.java | 6 +- 4 files changed, 46 insertions(+), 40 deletions(-) diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 9540ae1cfbf3f..2175a9eed90f7 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -592,6 +592,14 @@ public class ActivityManager { public static boolean isAlwaysOnTop(int stackId) { return stackId == PINNED_STACK_ID; } + + /** + * Returns true if the top task in the task is allowed to return home when finished and + * there are other tasks in the stack. + */ + public static boolean allowTopTaskToReturnHome(int stackId) { + return stackId != PINNED_STACK_ID; + } } /** diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 4fb87c3a2660f..eb0945bfe5f1e 100755 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -171,7 +171,9 @@ final class ActivityRecord { boolean stopped; // is activity pause finished? boolean delayedResume; // not yet resumed because of stopped app switches? boolean finishing; // activity in pending finish list? - boolean configDestroy; // need to destroy due to config change? + boolean deferRelaunchUntilPaused; // relaunch of activity is being deferred until pause is + // completed + boolean preserveWindowOnDeferredRelaunch; // activity windows are preserved on deferred relaunch int configChangeFlags; // which config values have changed boolean keysPaused; // has key dispatching been paused for it? int launchMode; // the launch mode activity attribute. @@ -184,9 +186,9 @@ final class ActivityRecord { boolean immersive; // immersive mode (don't interrupt if possible) boolean forceNewConfig; // force re-create with new config next time int launchCount; // count of launches since last state - long lastLaunchTime; // time of last lauch of this activity + long lastLaunchTime; // time of last launch of this activity boolean isVrActivity; // is the activity running in VR mode? - ArrayList mChildContainers = new ArrayList(); + ArrayList mChildContainers = new ArrayList<>(); String stringName; // for caching of toString(). @@ -341,8 +343,8 @@ final class ActivityRecord { else TimeUtils.formatDuration(lastVisibleTime, now, pw); pw.println(); } - if (configDestroy || configChangeFlags != 0) { - pw.print(prefix); pw.print("configDestroy="); pw.print(configDestroy); + if (deferRelaunchUntilPaused || configChangeFlags != 0) { + pw.print(prefix); pw.print("deferRelaunchUntilPaused="); pw.print(deferRelaunchUntilPaused); pw.print(" configChangeFlags="); pw.println(Integer.toHexString(configChangeFlags)); } @@ -551,7 +553,7 @@ final class ActivityRecord { stopped = false; delayedResume = false; finishing = false; - configDestroy = false; + deferRelaunchUntilPaused = false; keysPaused = false; inHistory = false; visible = false; diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index c44b4cfa7b263..312e309e39507 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -1088,7 +1088,7 @@ final class ActivityStack { if (r.finishing) { r.clearOptionsLocked(); } else { - if (r.configDestroy) { + if (r.deferRelaunchUntilPaused) { destroyActivityLocked(r, true, "stop-config"); mStackSupervisor.resumeFocusedStackTopActivityLocked(); } else { @@ -1114,14 +1114,11 @@ final class ActivityStack { if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Complete pause, no longer waiting: " + prev); } - if (prev.configDestroy) { - // The previous is being paused because the configuration - // is changing, which means it is actually stopping... - // To juggle the fact that we are also starting a new - // instance right now, we need to first completely stop - // the current instance before starting the new one. - if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Destroying after pause: " + prev); - destroyActivityLocked(prev, true, "pause-config"); + if (prev.deferRelaunchUntilPaused) { + // Complete the deferred relaunch that was waiting for pause to complete. + if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Re-launching after pause: " + prev); + relaunchActivityLocked(prev, prev.configChangeFlags, false, + prev.preserveWindowOnDeferredRelaunch); } else if (wasStopping) { // We are also stopping, the stop request must have gone soon after the pause. // We can't clobber it, because the stop confirmation will not be handled. @@ -2318,11 +2315,12 @@ final class ActivityStack { ActivityStack lastStack = mStackSupervisor.getLastStack(); final boolean fromHome = lastStack.isHomeStack(); if (!isHomeStack() && (fromHome || topTask() != task)) { - task.setTaskToReturnTo(fromHome - ? lastStack.topTask() == null - ? HOME_ACTIVITY_TYPE - : lastStack.topTask().taskType - : APPLICATION_ACTIVITY_TYPE); + int returnToType = APPLICATION_ACTIVITY_TYPE; + if (fromHome && StackId.allowTopTaskToReturnHome(mStackId)) { + returnToType = lastStack.topTask() == null + ? HOME_ACTIVITY_TYPE : lastStack.topTask().taskType; + } + task.setTaskToReturnTo(returnToType); } } else { task.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE); @@ -2972,7 +2970,7 @@ final class ActivityStack { r.stopped = true; if (DEBUG_STATES) Slog.v(TAG_STATES, "Stop failed; moving to STOPPED: " + r); r.state = ActivityState.STOPPED; - if (r.configDestroy) { + if (r.deferRelaunchUntilPaused) { destroyActivityLocked(r, true, "stop-except"); } } @@ -3403,7 +3401,7 @@ final class ActivityStack { } mService.resetFocusedActivityIfNeededLocked(r); - r.configDestroy = false; + r.deferRelaunchUntilPaused = false; r.frozenBeforeDestroy = false; if (setState) { @@ -4175,38 +4173,33 @@ final class ActivityStack { r.configChangeFlags |= changes; r.startFreezingScreenLocked(r.app, globalChanges); r.forceNewConfig = false; + preserveWindow &= isResizeOnlyChange(changes); if (r.app == null || r.app.thread == null) { if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Config is destroying non-running " + r); destroyActivityLocked(r, true, "config"); } else if (r.state == ActivityState.PAUSING) { - // A little annoying: we are waiting for this activity to - // finish pausing. Let's not do anything now, but just - // flag that it needs to be restarted when done pausing. + // A little annoying: we are waiting for this activity to finish pausing. Let's not + // do anything now, but just flag that it needs to be restarted when done pausing. if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Config is skipping already pausing " + r); - r.configDestroy = true; + r.deferRelaunchUntilPaused = true; + r.preserveWindowOnDeferredRelaunch = preserveWindow; return true; } else if (r.state == ActivityState.RESUMED) { - // Try to optimize this case: the configuration is changing - // and we need to restart the top, resumed activity. - // Instead of doing the normal handshaking, just say + // Try to optimize this case: the configuration is changing and we need to restart + // the top, resumed activity. Instead of doing the normal handshaking, just say // "restart!". if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Config is relaunching resumed " + r); - relaunchActivityLocked(r, r.configChangeFlags, true, - preserveWindow && isResizeOnlyChange(changes)); - r.configChangeFlags = 0; + relaunchActivityLocked(r, r.configChangeFlags, true, preserveWindow); } else { if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Config is relaunching non-resumed " + r); - relaunchActivityLocked(r, r.configChangeFlags, false, - preserveWindow && isResizeOnlyChange(changes)); - r.configChangeFlags = 0; + relaunchActivityLocked(r, r.configChangeFlags, false, preserveWindow); } - // All done... tell the caller we weren't able to keep this - // activity around. + // All done... tell the caller we weren't able to keep this activity around. return false; } @@ -4298,6 +4291,7 @@ final class ActivityStack { private void relaunchActivityLocked( ActivityRecord r, int changes, boolean andResume, boolean preserveWindow) { if (mService.mSuppressResizeConfigChanges && preserveWindow) { + r.configChangeFlags = 0; return; } @@ -4341,6 +4335,8 @@ final class ActivityStack { mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r); r.state = ActivityState.PAUSED; } + + r.configChangeFlags = 0; } boolean willActivityBeVisibleLocked(IBinder token) { diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 4672023a220e5..c634e0e6a6ac3 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -2722,7 +2722,7 @@ public final class ActivityStackSupervisor implements DisplayListener { for (int activityNdx = mStoppingActivities.size() - 1; activityNdx >= 0; --activityNdx) { ActivityRecord s = mStoppingActivities.get(activityNdx); final boolean waitingVisible = mWaitingVisibleActivities.contains(s); - if (DEBUG_ALL) Slog.v(TAG, "Stopping " + s + ": nowVisible=" + nowVisible + if (DEBUG_STATES) Slog.v(TAG, "Stopping " + s + ": nowVisible=" + nowVisible + " waitingVisible=" + waitingVisible + " finishing=" + s.finishing); if (waitingVisible && nowVisible) { mWaitingVisibleActivities.remove(s); @@ -2732,12 +2732,12 @@ public final class ActivityStackSupervisor implements DisplayListener { // so get rid of it. Otherwise, we need to go through the // normal flow and hide it once we determine that it is // hidden by the activities in front of it. - if (DEBUG_ALL) Slog.v(TAG, "Before stopping, can hide: " + s); + if (DEBUG_STATES) Slog.v(TAG, "Before stopping, can hide: " + s); mWindowManager.setAppVisibility(s.appToken, false); } } if ((!waitingVisible || mService.isSleepingOrShuttingDown()) && remove) { - if (DEBUG_ALL) Slog.v(TAG, "Ready to stop: " + s); + if (DEBUG_STATES) Slog.v(TAG, "Ready to stop: " + s); if (stops == null) { stops = new ArrayList<>(); }