diff options
| -rw-r--r-- | api/current.txt | 1 | ||||
| -rw-r--r-- | core/java/android/app/PictureInPictureParams.java | 49 | ||||
| -rw-r--r-- | core/proto/android/server/windowmanagerservice.proto | 1 | ||||
| -rw-r--r-- | non-updatable-api/current.txt | 1 | ||||
| -rw-r--r-- | services/core/java/com/android/server/wm/ActivityRecord.java | 7 | ||||
| -rw-r--r-- | services/core/java/com/android/server/wm/ActivityTaskManagerService.java | 107 | ||||
| -rw-r--r-- | services/core/java/com/android/server/wm/Task.java | 47 |
7 files changed, 149 insertions, 64 deletions
diff --git a/api/current.txt b/api/current.txt index a1629f1a937e..a92579488c68 100644 --- a/api/current.txt +++ b/api/current.txt @@ -6188,6 +6188,7 @@ package android.app { method public android.app.PictureInPictureParams build(); method public android.app.PictureInPictureParams.Builder setActions(java.util.List<android.app.RemoteAction>); method public android.app.PictureInPictureParams.Builder setAspectRatio(android.util.Rational); + method @NonNull public android.app.PictureInPictureParams.Builder setAutoEnterAllowed(boolean); method public android.app.PictureInPictureParams.Builder setSourceRectHint(android.graphics.Rect); } diff --git a/core/java/android/app/PictureInPictureParams.java b/core/java/android/app/PictureInPictureParams.java index 67d94dec88c5..18c4d70dd9b0 100644 --- a/core/java/android/app/PictureInPictureParams.java +++ b/core/java/android/app/PictureInPictureParams.java @@ -16,6 +16,7 @@ package android.app; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; import android.graphics.Rect; @@ -46,6 +47,8 @@ public final class PictureInPictureParams implements Parcelable { @Nullable private Rect mSourceRectHint; + private boolean mAutoEnterAllowed; + /** * Sets the aspect ratio. This aspect ratio is defined as the desired width / height, and * does not change upon device rotation. @@ -103,6 +106,25 @@ public final class PictureInPictureParams implements Parcelable { } /** + * Sets whether the system is allowed to automatically put the activity in + * picture-in-picture mode without needing/waiting for the activity to call + * {@link Activity#enterPictureInPictureMode(PictureInPictureParams)}. + * + * If true, {@link Activity#onPictureInPictureRequested()} will never be called. + * + * This property is false by default. + * @param autoEnterAllowed {@code true} if the system is allowed to automatically put the + * activity in picture-in-picture mode. + * + * @return this builder instance. + */ + @NonNull + public Builder setAutoEnterAllowed(boolean autoEnterAllowed) { + mAutoEnterAllowed = autoEnterAllowed; + return this; + } + + /** * @return an immutable {@link PictureInPictureParams} to be used when entering or updating * the activity in picture-in-picture. * @@ -111,7 +133,7 @@ public final class PictureInPictureParams implements Parcelable { */ public PictureInPictureParams build() { PictureInPictureParams params = new PictureInPictureParams(mAspectRatio, mUserActions, - mSourceRectHint); + mSourceRectHint, mAutoEnterAllowed); return params; } } @@ -136,6 +158,11 @@ public final class PictureInPictureParams implements Parcelable { @Nullable private Rect mSourceRectHint; + /** + * Whether the system is allowed to automatically put the activity in picture-in-picture mode. + */ + private boolean mAutoEnterAllowed; + /** {@hide} */ PictureInPictureParams() { } @@ -152,14 +179,18 @@ public final class PictureInPictureParams implements Parcelable { if (in.readInt() != 0) { mSourceRectHint = Rect.CREATOR.createFromParcel(in); } + if (in.readInt() != 0) { + mAutoEnterAllowed = in.readBoolean(); + } } /** {@hide} */ PictureInPictureParams(Rational aspectRatio, List<RemoteAction> actions, - Rect sourceRectHint) { + Rect sourceRectHint, boolean autoEnterAllowed) { mAspectRatio = aspectRatio; mUserActions = actions; mSourceRectHint = sourceRectHint; + mAutoEnterAllowed = autoEnterAllowed; } /** @@ -176,6 +207,7 @@ public final class PictureInPictureParams implements Parcelable { if (otherArgs.hasSourceBoundsHint()) { mSourceRectHint = new Rect(otherArgs.getSourceRectHint()); } + mAutoEnterAllowed = otherArgs.mAutoEnterAllowed; } /** @@ -248,11 +280,20 @@ public final class PictureInPictureParams implements Parcelable { } /** + * @return whether auto pip allowed. + * @hide + */ + public boolean isAutoEnterAllowed() { + return mAutoEnterAllowed; + } + + /** * @return True if no parameters are set * @hide */ public boolean empty() { - return !hasSourceBoundsHint() && !hasSetActions() && !hasSetAspectRatio(); + return !hasSourceBoundsHint() && !hasSetActions() && !hasSetAspectRatio() + && !mAutoEnterAllowed; } @Override @@ -281,6 +322,8 @@ public final class PictureInPictureParams implements Parcelable { } else { out.writeInt(0); } + out.writeInt(1); + out.writeBoolean(mAutoEnterAllowed); } public static final @android.annotation.NonNull Creator<PictureInPictureParams> CREATOR = diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index ff0ba8dd5ad4..3a59a16b54ca 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -316,6 +316,7 @@ message ActivityRecordProto { optional bool front_of_task = 28; optional int32 proc_id = 29; optional bool translucent = 30; + optional bool pip_auto_enter_allowed = 31; } /* represents WindowToken */ diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt index e1b3151acd5b..ce8a76e5443d 100644 --- a/non-updatable-api/current.txt +++ b/non-updatable-api/current.txt @@ -6188,6 +6188,7 @@ package android.app { method public android.app.PictureInPictureParams build(); method public android.app.PictureInPictureParams.Builder setActions(java.util.List<android.app.RemoteAction>); method public android.app.PictureInPictureParams.Builder setAspectRatio(android.util.Rational); + method @NonNull public android.app.PictureInPictureParams.Builder setAutoEnterAllowed(boolean); method public android.app.PictureInPictureParams.Builder setSourceRectHint(android.graphics.Rect); } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 56e404c8f409..6df46ed2532b 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -133,6 +133,7 @@ import static com.android.server.wm.ActivityRecordProto.LAST_SURFACE_SHOWING; import static com.android.server.wm.ActivityRecordProto.NAME; import static com.android.server.wm.ActivityRecordProto.NUM_DRAWN_WINDOWS; import static com.android.server.wm.ActivityRecordProto.NUM_INTERESTING_WINDOWS; +import static com.android.server.wm.ActivityRecordProto.PIP_AUTO_ENTER_ALLOWED; import static com.android.server.wm.ActivityRecordProto.PROC_ID; import static com.android.server.wm.ActivityRecordProto.REPORTED_DRAWN; import static com.android.server.wm.ActivityRecordProto.REPORTED_VISIBLE; @@ -4741,6 +4742,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // returns. Just need to confirm this reasoning makes sense. final boolean deferHidingClient = canEnterPictureInPicture && !isState(STARTED, STOPPING, STOPPED, PAUSED); + if (deferHidingClient && pictureInPictureArgs.isAutoEnterAllowed()) { + // Go ahead and just put the activity in pip if it supports auto-pip. + mAtmService.enterPictureInPictureMode(this, pictureInPictureArgs); + return; + } setDeferHidingClient(deferHidingClient); setVisibility(false); @@ -7678,6 +7684,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (hasProcess()) { proto.write(PROC_ID, app.getPid()); } + proto.write(PIP_AUTO_ENTER_ALLOWED, pictureInPictureArgs.isAutoEnterAllowed()); } @Override diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 505233cd8a4a..080a438df357 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -253,7 +253,6 @@ import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.AttributeCache; import com.android.server.LocalServices; import com.android.server.SystemService; -import com.android.server.SystemService.TargetUser; import com.android.server.SystemServiceManager; import com.android.server.UiThread; import com.android.server.Watchdog; @@ -4059,6 +4058,60 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { && r.getRootTask().isInTask(r) != null; } + /** + * Puts the given activity in picture in picture mode if possible. + * + * @return true if the activity is now in picture-in-picture mode, or false if it could not + * enter picture-in-picture mode. + */ + boolean enterPictureInPictureMode(ActivityRecord r, final PictureInPictureParams params) { + // If the activity is already in picture in picture mode, then just return early + if (isInPictureInPictureMode(r)) { + return true; + } + + // Activity supports picture-in-picture, now check that we can enter PiP at this + // point, if it is + if (!r.checkEnterPictureInPictureState("enterPictureInPictureMode", + false /* beforeStopping */)) { + return false; + } + + final Runnable enterPipRunnable = () -> { + synchronized (mGlobalLock) { + if (r.getParent() == null) { + Slog.e(TAG, "Skip enterPictureInPictureMode, destroyed " + r); + return; + } + // Only update the saved args from the args that are set + r.setPictureInPictureParams(params); + final float aspectRatio = r.pictureInPictureArgs.getAspectRatio(); + final List<RemoteAction> actions = r.pictureInPictureArgs.getActions(); + mRootWindowContainer.moveActivityToPinnedStack( + r, "enterPictureInPictureMode"); + final Task stack = r.getRootTask(); + stack.setPictureInPictureAspectRatio(aspectRatio); + stack.setPictureInPictureActions(actions); + } + }; + + if (isKeyguardLocked()) { + // If the keyguard is showing or occluded, then try and dismiss it before + // entering picture-in-picture (this will prompt the user to authenticate if the + // device is currently locked). + dismissKeyguard(r.appToken, new KeyguardDismissCallback() { + @Override + public void onDismissSucceeded() { + mH.post(enterPipRunnable); + } + }, null /* message */); + } else { + // Enter picture in picture immediately otherwise + enterPipRunnable.run(); + } + return true; + } + @Override public boolean enterPictureInPictureMode(IBinder token, final PictureInPictureParams params) { final long origId = Binder.clearCallingIdentity(); @@ -4066,52 +4119,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { final ActivityRecord r = ensureValidPictureInPictureActivityParamsLocked( "enterPictureInPictureMode", token, params); - - // If the activity is already in picture in picture mode, then just return early - if (isInPictureInPictureMode(r)) { - return true; - } - - // Activity supports picture-in-picture, now check that we can enter PiP at this - // point, if it is - if (!r.checkEnterPictureInPictureState("enterPictureInPictureMode", - false /* beforeStopping */)) { - return false; - } - - final Runnable enterPipRunnable = () -> { - synchronized (mGlobalLock) { - if (r.getParent() == null) { - Slog.e(TAG, "Skip enterPictureInPictureMode, destroyed " + r); - return; - } - // Only update the saved args from the args that are set - r.setPictureInPictureParams(params); - final float aspectRatio = r.pictureInPictureArgs.getAspectRatio(); - final List<RemoteAction> actions = r.pictureInPictureArgs.getActions(); - mRootWindowContainer.moveActivityToPinnedStack( - r, "enterPictureInPictureMode"); - final Task stack = r.getRootTask(); - stack.setPictureInPictureAspectRatio(aspectRatio); - stack.setPictureInPictureActions(actions); - } - }; - - if (isKeyguardLocked()) { - // If the keyguard is showing or occluded, then try and dismiss it before - // entering picture-in-picture (this will prompt the user to authenticate if the - // device is currently locked). - dismissKeyguard(token, new KeyguardDismissCallback() { - @Override - public void onDismissSucceeded() { - mH.post(enterPipRunnable); - } - }, null /* message */); - } else { - // Enter picture in picture immediately otherwise - enterPipRunnable.run(); - } - return true; + return enterPictureInPictureMode(r, params); } } finally { Binder.restoreCallingIdentity(origId); @@ -4857,6 +4865,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { "Requested PIP on an activity that doesn't support it"); } + if (activity.pictureInPictureArgs.isAutoEnterAllowed()) { + enterPictureInPictureMode(activity, activity.pictureInPictureArgs); + return; + } + try { final ClientTransaction transaction = ClientTransaction.obtain( activity.app.getThread(), diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 70fd860f21f2..c35d73282442 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -5428,6 +5428,7 @@ class Task extends WindowContainer<WindowContainer> { mAtmService.updateCpuStats(); boolean pauseImmediately = false; + boolean shouldAutoPip = false; if (resuming != null && (resuming.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0) { // 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 @@ -5435,26 +5436,39 @@ class Task extends WindowContainer<WindowContainer> { // activities a chance to enter Pip before resuming the next activity. final boolean lastResumedCanPip = prev != null && prev.checkEnterPictureInPictureState( "shouldResumeWhilePausing", userLeaving); - if (!lastResumedCanPip) { + if (lastResumedCanPip && prev.pictureInPictureArgs.isAutoEnterAllowed()) { + shouldAutoPip = true; + } else if (!lastResumedCanPip) { pauseImmediately = true; + } else { + // The previous activity may still enter PIP even though it did not allow auto-PIP. } } + boolean didAutoPip = false; if (prev.attachedToProcess()) { - if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev); - try { - EventLogTags.writeWmPauseActivity(prev.mUserId, System.identityHashCode(prev), - prev.shortComponentName, "userLeaving=" + userLeaving); - - mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(), - prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving, - prev.configChangeFlags, pauseImmediately)); - } catch (Exception e) { - // Ignore exception, if process died other code will cleanup. - Slog.w(TAG, "Exception thrown during pause", e); + if (shouldAutoPip) { + if (DEBUG_PAUSE) { + Slog.d(TAG_PAUSE, "Auto-PIP allowed, entering PIP mode directly: " + prev); + } + didAutoPip = mAtmService.enterPictureInPictureMode(prev, prev.pictureInPictureArgs); mPausingActivity = null; - mLastPausedActivity = null; - mLastNoHistoryActivity = null; + } else { + if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev); + try { + EventLogTags.writeWmPauseActivity(prev.mUserId, System.identityHashCode(prev), + prev.shortComponentName, "userLeaving=" + userLeaving); + + mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(), + prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving, + prev.configChangeFlags, pauseImmediately)); + } catch (Exception e) { + // Ignore exception, if process died other code will cleanup. + Slog.w(TAG, "Exception thrown during pause", e); + mPausingActivity = null; + mLastPausedActivity = null; + mLastNoHistoryActivity = null; + } } } else { mPausingActivity = null; @@ -5468,6 +5482,11 @@ class Task extends WindowContainer<WindowContainer> { mStackSupervisor.acquireLaunchWakelock(); } + if (didAutoPip) { + // Already entered PIP mode, no need to keep pausing. + return true; + } + if (mPausingActivity != null) { // Have the window manager pause its key dispatching until the new // activity has started. If we're pausing the activity just because |