summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt1
-rw-r--r--core/java/android/app/PictureInPictureParams.java49
-rw-r--r--core/proto/android/server/windowmanagerservice.proto1
-rw-r--r--non-updatable-api/current.txt1
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java7
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java107
-rw-r--r--services/core/java/com/android/server/wm/Task.java47
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