diff options
| author | 2017-08-10 15:57:18 -0700 | |
|---|---|---|
| committer | 2017-08-23 15:06:32 -0700 | |
| commit | 298f95b1ad04b2cc9f46f221dcc2d3932f532fdd (patch) | |
| tree | 7f98f095da863630b346dbb37a0a55c6cc6edd40 | |
| parent | 230e02584171feb0fc17706a1b70734172a89623 (diff) | |
Move PiP state check exception to the client side
- When calling enterPictureInPictureMode(), the state of the activity in
the client may be out of sync with the state of the activity in the
system, causing an exception to be thrown erroneously. Instead, fail
silently in the system if this occurs, and throw the exception in the
client when it attempts to enter PiP from an invalid state.
Bug: 63753007
Test: android.server.cts.ActivityManagerPinnedStackTests
Change-Id: Ia99cc086805edc31f997d4325f7a5ccd7c85a77e
5 files changed, 36 insertions, 27 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 3fa89279e9cc..757795e06cfb 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -762,6 +762,10 @@ public class Activity extends ContextThemeWrapper private boolean mDestroyed; private boolean mDoReportFullyDrawn = true; private boolean mRestoredFromBundle; + + /** {@code true} if the activity lifecycle is in a state which supports picture-in-picture. + * This only affects the client-side exception, the actual state check still happens in AMS. */ + private boolean mCanEnterPictureInPicture = false; /** true if the activity is going through a transient pause */ /*package*/ boolean mTemporaryPause = false; /** true if the activity is being destroyed in order to recreate it with a new configuration */ @@ -2091,6 +2095,10 @@ public class Activity extends ContextThemeWrapper if (params == null) { throw new IllegalArgumentException("Expected non-null picture-in-picture params"); } + if (!mCanEnterPictureInPicture) { + throw new IllegalStateException("Activity must be resumed to enter" + + " picture-in-picture"); + } return ActivityManagerNative.getDefault().enterPictureInPictureMode(mToken, params); } catch (RemoteException e) { return false; @@ -6957,25 +6965,29 @@ public class Activity extends ContextThemeWrapper return mParent != null ? mParent.getActivityToken() : mToken; } - final void performCreateCommon() { - mVisibleFromClient = !mWindow.getWindowStyle().getBoolean( - com.android.internal.R.styleable.Window_windowNoDisplay, false); - mFragments.dispatchActivityCreated(); - mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions()); - } - final void performCreate(Bundle icicle) { - restoreHasCurrentPermissionRequest(icicle); - onCreate(icicle); - mActivityTransitionState.readState(icicle); - performCreateCommon(); + performCreate(icicle, null); } final void performCreate(Bundle icicle, PersistableBundle persistentState) { + mCanEnterPictureInPicture = true; restoreHasCurrentPermissionRequest(icicle); - onCreate(icicle, persistentState); + if (persistentState != null) { + onCreate(icicle, persistentState); + } else { + onCreate(icicle); + } mActivityTransitionState.readState(icicle); - performCreateCommon(); + + mVisibleFromClient = !mWindow.getWindowStyle().getBoolean( + com.android.internal.R.styleable.Window_windowNoDisplay, false); + mFragments.dispatchActivityCreated(); + mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions()); + } + + final void performNewIntent(Intent intent) { + mCanEnterPictureInPicture = true; + onNewIntent(intent); } final void performStart() { @@ -7126,6 +7138,9 @@ public class Activity extends ContextThemeWrapper mDoReportFullyDrawn = false; mFragments.doLoaderStop(mChangingConfigurations /*retain*/); + // Disallow entering picture-in-picture after the activity has been stopped + mCanEnterPictureInPicture = false; + if (!mStopped) { if (mWindow != null) { mWindow.closeAllPanels(); diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index 467fc952ffee..e260967f92d0 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -48,6 +48,7 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.ViewConfiguration; import android.view.Window; + import com.android.internal.content.ReferrerIntent; import java.io.File; @@ -1305,7 +1306,7 @@ public class Instrumentation { * @param intent The new intent being received. */ public void callActivityOnNewIntent(Activity activity, Intent intent) { - activity.onNewIntent(intent); + activity.performNewIntent(intent); } /** diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 32d34450e2c1..5c5a6b7ac205 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -8045,7 +8045,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 /* beforeStopping */)) { + 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 e5985c58e5af..874bd1e7625b 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -1186,10 +1186,9 @@ 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. + * @return whether this activity is currently allowed to enter PIP. */ - boolean checkEnterPictureInPictureState(String caller, boolean noThrow, boolean beforeStopping) { + boolean checkEnterPictureInPictureState(String caller, boolean beforeStopping) { if (!supportsPictureInPicture()) { return false; } @@ -1237,13 +1236,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo return isNotLockedOrOnKeyguard && !hasPinnedStack; } default: - if (noThrow) { - return false; - } else { - throw new IllegalStateException(caller - + ": Current activity is not visible (state=" + state.name() + ") " - + "r=" + this); - } + return false; } } diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index c2656a188dc8..eb3177a6e1c9 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -2076,7 +2076,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Making invisible: " + r + " " + r.state); try { final boolean canEnterPictureInPicture = r.checkEnterPictureInPictureState( - "makeInvisible", true /* noThrow */, true /* beforeStopping */); + "makeInvisible", true /* beforeStopping */); // Defer telling the client it is hidden if it can enter Pip and isn't current stopped // or stopping. This gives it a chance to enter Pip in onPause(). final boolean deferHidingClient = canEnterPictureInPicture @@ -2390,7 +2390,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // represent the last resumed activity. However, the last focus stack does if it isn't null. final ActivityRecord lastResumed = lastFocusedStack.mResumedActivity; lastResumedCanPip = lastResumed != null && lastResumed.checkEnterPictureInPictureState( - "resumeTopActivity", true /* noThrow */, userLeaving /* beforeStopping */); + "resumeTopActivity", userLeaving /* beforeStopping */); } // If the flag RESUME_WHILE_PAUSING is set, then continue to schedule the previous activity // to be paused, while at the same time resuming the new resume activity only if the |