diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 17459eaa41ebb..cde3ec5bcb44f 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -7803,7 +7803,7 @@ 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", - false /* noThrow */)) { + false /* noThrow */, false /* beforeStopping */)) { 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 cfbd2b5e28be2..274dac6d3975f 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -1163,10 +1163,13 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } /** + * @param beforeStopping Whether this check is for an auto-enter-pip operation, that is to say + * the activity has requested to enter PiP when it would otherwise be stopped. + * * @return whether this activity is currently allowed to enter PIP, throwing an exception if * the activity is not currently visible and {@param noThrow} is not set. */ - boolean checkEnterPictureInPictureState(String caller, boolean noThrow) { + boolean checkEnterPictureInPictureState(String caller, boolean noThrow, boolean beforeStopping) { // Check app-ops and see if PiP is supported for this package if (!checkEnterPictureInPictureAppOpsState()) { return false; @@ -1177,17 +1180,24 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo return false; } - boolean isCurrentAppLocked = mStackSupervisor.getLockTaskModeState() != LOCK_TASK_MODE_NONE; boolean isKeyguardLocked = service.isKeyguardLocked(); + boolean isCurrentAppLocked = mStackSupervisor.getLockTaskModeState() != LOCK_TASK_MODE_NONE; boolean hasPinnedStack = mStackSupervisor.getStack(PINNED_STACK_ID) != null; // Don't return early if !isNotLocked, since we want to throw an exception if the activity // is in an incorrect state boolean isNotLockedOrOnKeyguard = !isKeyguardLocked && !isCurrentAppLocked; + + // We don't allow auto-PiP when something else is already pipped. + if (beforeStopping && hasPinnedStack) { + return false; + } + switch (state) { case RESUMED: // When visible, allow entering PiP if the app is not locked. If it is over the // keyguard, then we will prompt to unlock in the caller before entering PiP. - return !isCurrentAppLocked; + return !isCurrentAppLocked && + (supportsPictureInPictureWhilePausing || !beforeStopping); case PAUSING: case PAUSED: // When pausing, then only allow enter PiP as in the resume state, and in addition, diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 0e033d22000d4..bb71499d0728c 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -2001,10 +2001,10 @@ class ActivityStack extends ConfigurationContai // keeping the screen frozen. if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Making invisible: " + r + " " + r.state); try { - r.setVisible(false); switch (r.state) { case STOPPING: case STOPPED: + r.setVisible(false); if (r.app != null && r.app.thread != null) { if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Scheduling invisibility: " + r); @@ -2023,6 +2023,7 @@ class ActivityStack extends ConfigurationContai // This case created for transitioning activities from // translucent to opaque {@link Activity#convertToOpaque}. if (visibleBehind == r) { + r.setVisible(false); releaseBackgroundResources(r); } else { // If this activity is in a state where it can currently enter @@ -2030,7 +2031,18 @@ class ActivityStack extends ConfigurationContai // 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 */); + "makeInvisible", true /* noThrow */, true /* beforeStopping */); + + if (canEnterPictureInPicture) { + // We set r.visible=false so that Stop will later + // call setVisible for us. In this case + // we don't want to call setVisible(false) to avoid + // notifying the client of this intermittent invisible + // state. + r.visible = false; + } else { + r.setVisible(false); + } addToStopping(r, true /* scheduleIdle */, canEnterPictureInPicture /* idleDelayed */); }