diff options
4 files changed, 71 insertions, 33 deletions
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 732cd101e0b3..171e90b86113 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -6730,7 +6730,8 @@ public class ActivityManagerService extends IActivityManager.Stub ActivityStack stack = ActivityRecord.getStackLocked(token); if (stack != null) { ActivityRecord r = - mStackSupervisor.activityIdleInternalLocked(token, false, config); + mStackSupervisor.activityIdleInternalLocked(token, false /* fromTimeout */, + false /* processPausingActivities */, config); if (stopProfiling) { if ((mProfileProc == r.app) && (mProfileFd != null)) { try { @@ -7610,7 +7611,8 @@ public class ActivityManagerService extends IActivityManager.Stub // Activity supports picture-in-picture, now check that we can enter PiP at this // point, if it is - if (!r.checkEnterPictureInPictureState("enterPictureInPictureMode")) { + if (!r.checkEnterPictureInPictureState("enterPictureInPictureMode", + false /* noThrow */)) { return false; } diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index baf777220691..cd28da7f938d 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -944,9 +944,9 @@ final class ActivityRecord implements AppWindowContainerListener { /** * @return whether this activity is currently allowed to enter PIP, throwing an exception if - * the activity is not currently visible. + * the activity is not currently visible and {@param noThrow} is not set. */ - boolean checkEnterPictureInPictureState(String caller) { + boolean checkEnterPictureInPictureState(String caller, boolean noThrow) { boolean isKeyguardLocked = service.isKeyguardLocked(); boolean hasPinnedStack = mStackSupervisor.getStack(PINNED_STACK_ID) != null; switch (state) { @@ -969,9 +969,13 @@ final class ActivityRecord implements AppWindowContainerListener { && checkEnterPictureInPictureOnHideAppOpsState(); } default: - throw new IllegalStateException(caller - + ": Current activity is not visible (state=" + state.name() + ") " - + "r=" + this); + if (noThrow) { + return false; + } else { + throw new IllegalStateException(caller + + ": Current activity is not visible (state=" + state.name() + ") " + + "r=" + this); + } } } @@ -1669,7 +1673,8 @@ final class ActivityRecord implements AppWindowContainerListener { if (!idle) { // Instead of doing the full stop routine here, let's just hide any activities // we now can, and let them stop when the normal idle happens. - mStackSupervisor.processStoppingActivitiesLocked(false); + mStackSupervisor.processStoppingActivitiesLocked(null /* idleActivity */, + false /* remove */, true /* processPausingActivities */); } else { // If this activity was already idle, then we now need to make sure we perform // the full stop of any activities that are waiting to do so. This is because @@ -2139,7 +2144,7 @@ final class ActivityRecord implements AppWindowContainerListener { // if the app is relaunched when it's stopped, and we're not resuming, // put it back into stopped state. if (stopped) { - getStack().addToStopping(this, true /* immediate */); + getStack().addToStopping(this, true /* scheduleIdle */, false /* idleDelayed */); } } diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 9ce7ae30ebe5..df78a3d35355 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -1139,6 +1139,18 @@ final class ActivityStack extends ConfigurationContainer implements StackWindowL } /** + * Schedule a pause timeout in case the app doesn't respond. We don't give it much time because + * this directly impacts the responsiveness seen by the user. + */ + private void schedulePauseTimeout(ActivityRecord r) { + final Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG); + msg.obj = r; + r.pauseTime = SystemClock.uptimeMillis(); + mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT); + if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Waiting for pause to complete..."); + } + + /** * Start pausing the currently resumed activity. It is an error to call this if there * is already an activity being paused or there is no resumed activity. * @@ -1244,14 +1256,7 @@ final class ActivityStack extends ConfigurationContainer implements StackWindowL return false; } else { - // Schedule a pause timeout in case the app doesn't respond. - // We don't give it much time because this directly impacts the - // responsiveness seen by the user. - Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG); - msg.obj = prev; - prev.pauseTime = SystemClock.uptimeMillis(); - mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT); - if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Waiting for pause to complete..."); + schedulePauseTimeout(prev); return true; } @@ -1332,7 +1337,7 @@ final class ActivityStack extends ConfigurationContainer implements StackWindowL || mService.isSleepingOrShuttingDownLocked()) { // If we were visible then resumeTopActivities will release resources before // stopping. - addToStopping(prev, true /* immediate */); + addToStopping(prev, true /* scheduleIdle */, false /* idleDelayed */); } } else { if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "App died during pause, not stopping: " + prev); @@ -1398,7 +1403,7 @@ final class ActivityStack extends ConfigurationContainer implements StackWindowL mStackSupervisor.ensureActivitiesVisibleLocked(resuming, 0, !PRESERVE_WINDOWS); } - void addToStopping(ActivityRecord r, boolean immediate) { + void addToStopping(ActivityRecord r, boolean scheduleIdle, boolean idleDelayed) { if (!mStackSupervisor.mStoppingActivities.contains(r)) { mStackSupervisor.mStoppingActivities.add(r); } @@ -1409,11 +1414,14 @@ final class ActivityStack extends ConfigurationContainer implements StackWindowL // be cleared immediately. boolean forceIdle = mStackSupervisor.mStoppingActivities.size() > MAX_STOPPING_TO_FORCE || (r.frontOfTask && mTaskHistory.size() <= 1); - - if (immediate || forceIdle) { + if (scheduleIdle || forceIdle) { if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Scheduling idle now: forceIdle=" - + forceIdle + "immediate=" + immediate); - mStackSupervisor.scheduleIdleLocked(); + + forceIdle + "immediate=" + !idleDelayed); + if (!idleDelayed) { + mStackSupervisor.scheduleIdleLocked(); + } else { + mStackSupervisor.scheduleIdleTimeoutLocked(r); + } } else { mStackSupervisor.checkReadyForSleepLocked(); } @@ -1993,7 +2001,14 @@ final class ActivityStack extends ConfigurationContainer implements StackWindowL if (visibleBehind == r) { releaseBackgroundResources(r); } else { - addToStopping(r, true /* immediate */); + // If this activity is in a state where it can currently enter + // picture-in-picture, then don't immediately schedule the idle now in case + // the activity tries to enterPictureInPictureMode() later. Otherwise, + // we will try and stop the activity next time idle is processed. + final boolean canEnterPictureInPicture = r.checkEnterPictureInPictureState( + "makeInvisible", true /* noThrow */); + addToStopping(r, true /* scheduleIdle */, + canEnterPictureInPicture /* idleDelayed */); } break; @@ -3555,7 +3570,7 @@ final class ActivityStack extends ConfigurationContainer implements StackWindowL if (mode == FINISH_AFTER_VISIBLE && (r.visible || r.nowVisible) && next != null && !next.nowVisible) { if (!mStackSupervisor.mStoppingActivities.contains(r)) { - addToStopping(r, false /* immediate */); + addToStopping(r, false /* scheduleIdle */, false /* idleDelayed */); } if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to STOPPING: "+ r + " (finish requested)"); @@ -3808,7 +3823,7 @@ final class ActivityStack extends ConfigurationContainer implements StackWindowL mWindowManager.notifyAppRelaunchesCleared(r.appToken); } - private void removeTimeoutsForActivityLocked(ActivityRecord r) { + void removeTimeoutsForActivityLocked(ActivityRecord r) { mStackSupervisor.removeTimeoutsForActivityLocked(r); mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r); mHandler.removeMessages(STOP_TIMEOUT_MSG, r); @@ -5073,6 +5088,7 @@ final class ActivityStack extends ConfigurationContainer implements StackWindowL // If the activity was previously pausing, then ensure we transfer that as well if (setPause) { mPausingActivity = r; + schedulePauseTimeout(r); } // Move the stack in which we are placing the activity to the front. The call will also // make sure the activity focus is set. @@ -5114,6 +5130,7 @@ final class ActivityStack extends ConfigurationContainer implements StackWindowL } if (wasPaused) { prevStack.mPausingActivity = null; + prevStack.removeTimeoutsForActivityLocked(r); } } diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index b2b3e61ca534..83a786f626bf 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -1705,7 +1705,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // Checked. final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout, - Configuration config) { + boolean processPausingActivities, Configuration config) { if (DEBUG_ALL) Slog.v(TAG, "Activity idle: " + token); ArrayList<ActivityRecord> finishes = null; @@ -1761,7 +1761,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } // Atomically retrieve all of the other things to do. - final ArrayList<ActivityRecord> stops = processStoppingActivitiesLocked(true); + final ArrayList<ActivityRecord> stops = processStoppingActivitiesLocked(r, + true /* remove */, processPausingActivities); NS = stops != null ? stops.size() : 0; if ((NF = mFinishingActivities.size()) > 0) { finishes = new ArrayList<>(mFinishingActivities); @@ -2689,6 +2690,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // Reset the paused activity on the previous stack if (wasPaused) { prevStack.mPausingActivity = null; + prevStack.removeTimeoutsForActivityLocked(r); } // If the task had focus before (or we're requested to move focus), @@ -3367,7 +3369,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return mService.mUserController.isCurrentProfileLocked(userId); } - final ArrayList<ActivityRecord> processStoppingActivitiesLocked(boolean remove) { + final ArrayList<ActivityRecord> processStoppingActivitiesLocked(ActivityRecord idleActivity, + boolean remove, boolean processPausingActivities) { ArrayList<ActivityRecord> stops = null; final boolean nowVisible = allResumedActivitiesVisible(); @@ -3392,6 +3395,14 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } if ((!waitingVisible || mService.isSleepingOrShuttingDownLocked()) && remove) { + if (!processPausingActivities && s.state == PAUSING) { + // Defer processing pausing activities in this iteration and reschedule + // a delayed idle to reprocess it again + removeTimeoutsForActivityLocked(idleActivity); + scheduleIdleTimeoutLocked(idleActivity); + continue; + } + if (DEBUG_STATES) Slog.v(TAG, "Ready to stop: " + s); if (stops == null) { stops = new ArrayList<>(); @@ -4113,9 +4124,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D super(looper); } - void activityIdleInternal(ActivityRecord r) { + void activityIdleInternal(ActivityRecord r, boolean processPausingActivities) { synchronized (mService) { - activityIdleInternalLocked(r != null ? r.appToken : null, true, null); + activityIdleInternalLocked(r != null ? r.appToken : null, true /* fromTimeout */, + processPausingActivities, null); } } @@ -4150,11 +4162,13 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } // We don't at this point know if the activity is fullscreen, // so we need to be conservative and assume it isn't. - activityIdleInternal((ActivityRecord)msg.obj); + activityIdleInternal((ActivityRecord) msg.obj, + true /* processPausingActivities */); } break; case IDLE_NOW_MSG: { if (DEBUG_IDLE) Slog.d(TAG_IDLE, "handleMessage: IDLE_NOW_MSG: r=" + msg.obj); - activityIdleInternal((ActivityRecord)msg.obj); + activityIdleInternal((ActivityRecord) msg.obj, + false /* processPausingActivities */); } break; case RESUME_TOP_ACTIVITY_MSG: { synchronized (mService) { |