Merge "fix(window magnification): call notifySourceBoundsChanged when the animation ends" into main
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index bcbac93..47a4052 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -36,6 +36,8 @@
import androidx.annotation.VisibleForTesting;
+import com.android.internal.annotations.GuardedBy;
+
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -75,14 +77,17 @@
@Nullable
private ImeBackAnimationController mImeBackAnimationController;
+ @GuardedBy("mLock")
/** Convenience hashmap to quickly decide if a callback has been added. */
private final HashMap<OnBackInvokedCallback, Integer> mAllCallbacks = new HashMap<>();
/** Holds all callbacks by priorities. */
@VisibleForTesting
+ @GuardedBy("mLock")
public final TreeMap<Integer, ArrayList<OnBackInvokedCallback>>
mOnBackInvokedCallbacks = new TreeMap<>();
private Checker mChecker;
+ private final Object mLock = new Object();
public WindowOnBackInvokedDispatcher(@NonNull Context context) {
mChecker = new Checker(context);
@@ -94,20 +99,24 @@
*/
public void attachToWindow(@NonNull IWindowSession windowSession, @NonNull IWindow window,
@Nullable ImeBackAnimationController imeBackAnimationController) {
- mWindowSession = windowSession;
- mWindow = window;
- mImeBackAnimationController = imeBackAnimationController;
- if (!mAllCallbacks.isEmpty()) {
- setTopOnBackInvokedCallback(getTopCallback());
+ synchronized (mLock) {
+ mWindowSession = windowSession;
+ mWindow = window;
+ mImeBackAnimationController = imeBackAnimationController;
+ if (!mAllCallbacks.isEmpty()) {
+ setTopOnBackInvokedCallback(getTopCallback());
+ }
}
}
/** Detaches the dispatcher instance from its window. */
public void detachFromWindow() {
- clear();
- mWindow = null;
- mWindowSession = null;
- mImeBackAnimationController = null;
+ synchronized (mLock) {
+ clear();
+ mWindow = null;
+ mWindowSession = null;
+ mImeBackAnimationController = null;
+ }
}
// TODO: Take an Executor for the callback to run on.
@@ -125,65 +134,71 @@
*/
public void registerOnBackInvokedCallbackUnchecked(
@NonNull OnBackInvokedCallback callback, @Priority int priority) {
- if (mImeDispatcher != null) {
- mImeDispatcher.registerOnBackInvokedCallback(priority, callback);
- return;
- }
- if (!mOnBackInvokedCallbacks.containsKey(priority)) {
- mOnBackInvokedCallbacks.put(priority, new ArrayList<>());
- }
- if (callback instanceof ImeOnBackInvokedDispatcher.DefaultImeOnBackAnimationCallback) {
- callback = mImeBackAnimationController;
- }
- ArrayList<OnBackInvokedCallback> callbacks = mOnBackInvokedCallbacks.get(priority);
-
- // If callback has already been added, remove it and re-add it.
- if (mAllCallbacks.containsKey(callback)) {
- if (DEBUG) {
- Log.i(TAG, "Callback already added. Removing and re-adding it.");
+ synchronized (mLock) {
+ if (mImeDispatcher != null) {
+ mImeDispatcher.registerOnBackInvokedCallback(priority, callback);
+ return;
}
- Integer prevPriority = mAllCallbacks.get(callback);
- mOnBackInvokedCallbacks.get(prevPriority).remove(callback);
- }
+ if (!mOnBackInvokedCallbacks.containsKey(priority)) {
+ mOnBackInvokedCallbacks.put(priority, new ArrayList<>());
+ }
+ if (callback instanceof ImeOnBackInvokedDispatcher.DefaultImeOnBackAnimationCallback) {
+ callback = mImeBackAnimationController;
+ }
+ ArrayList<OnBackInvokedCallback> callbacks = mOnBackInvokedCallbacks.get(priority);
- OnBackInvokedCallback previousTopCallback = getTopCallback();
- callbacks.add(callback);
- mAllCallbacks.put(callback, priority);
- if (previousTopCallback == null
- || (previousTopCallback != callback
- && mAllCallbacks.get(previousTopCallback) <= priority)) {
- setTopOnBackInvokedCallback(callback);
+ // If callback has already been added, remove it and re-add it.
+ if (mAllCallbacks.containsKey(callback)) {
+ if (DEBUG) {
+ Log.i(TAG, "Callback already added. Removing and re-adding it.");
+ }
+ Integer prevPriority = mAllCallbacks.get(callback);
+ mOnBackInvokedCallbacks.get(prevPriority).remove(callback);
+ }
+
+ OnBackInvokedCallback previousTopCallback = getTopCallback();
+ callbacks.add(callback);
+ mAllCallbacks.put(callback, priority);
+ if (previousTopCallback == null
+ || (previousTopCallback != callback
+ && mAllCallbacks.get(previousTopCallback) <= priority)) {
+ setTopOnBackInvokedCallback(callback);
+ }
}
}
@Override
public void unregisterOnBackInvokedCallback(@NonNull OnBackInvokedCallback callback) {
- if (mImeDispatcher != null) {
- mImeDispatcher.unregisterOnBackInvokedCallback(callback);
- return;
- }
- if (callback instanceof ImeOnBackInvokedDispatcher.DefaultImeOnBackAnimationCallback) {
- callback = mImeBackAnimationController;
- }
- if (!mAllCallbacks.containsKey(callback)) {
- if (DEBUG) {
- Log.i(TAG, "Callback not found. returning...");
+ synchronized (mLock) {
+ if (mImeDispatcher != null) {
+ mImeDispatcher.unregisterOnBackInvokedCallback(callback);
+ return;
}
- return;
- }
- OnBackInvokedCallback previousTopCallback = getTopCallback();
- Integer priority = mAllCallbacks.get(callback);
- ArrayList<OnBackInvokedCallback> callbacks = mOnBackInvokedCallbacks.get(priority);
- callbacks.remove(callback);
- if (callbacks.isEmpty()) {
- mOnBackInvokedCallbacks.remove(priority);
- }
- mAllCallbacks.remove(callback);
- // Re-populate the top callback to WM if the removed callback was previously the top one.
- if (previousTopCallback == callback) {
- // We should call onBackCancelled() when an active callback is removed from dispatcher.
- sendCancelledIfInProgress(callback);
- setTopOnBackInvokedCallback(getTopCallback());
+ if (callback instanceof ImeOnBackInvokedDispatcher.DefaultImeOnBackAnimationCallback) {
+ callback = mImeBackAnimationController;
+ }
+ if (!mAllCallbacks.containsKey(callback)) {
+ if (DEBUG) {
+ Log.i(TAG, "Callback not found. returning...");
+ }
+ return;
+ }
+ OnBackInvokedCallback previousTopCallback = getTopCallback();
+ Integer priority = mAllCallbacks.get(callback);
+ ArrayList<OnBackInvokedCallback> callbacks = mOnBackInvokedCallbacks.get(priority);
+ callbacks.remove(callback);
+ if (callbacks.isEmpty()) {
+ mOnBackInvokedCallbacks.remove(priority);
+ }
+ mAllCallbacks.remove(callback);
+ // Re-populate the top callback to WM if the removed callback was previously the top
+ // one.
+ if (previousTopCallback == callback) {
+ // We should call onBackCancelled() when an active callback is removed from
+ // dispatcher.
+ sendCancelledIfInProgress(callback);
+ setTopOnBackInvokedCallback(getTopCallback());
+ }
}
}
@@ -191,15 +206,21 @@
* Indicates if the dispatcher is actively dispatching to a callback.
*/
public boolean isDispatching() {
- return mIsDispatching;
+ synchronized (mLock) {
+ return mIsDispatching;
+ }
}
private void onStartDispatching() {
- mIsDispatching = true;
+ synchronized (mLock) {
+ mIsDispatching = true;
+ }
}
private void onStopDispatching() {
- mIsDispatching = false;
+ synchronized (mLock) {
+ mIsDispatching = false;
+ }
}
private void sendCancelledIfInProgress(@NonNull OnBackInvokedCallback callback) {
@@ -223,27 +244,29 @@
/** Clears all registered callbacks on the instance. */
public void clear() {
- if (mImeDispatcher != null) {
- mImeDispatcher.clear();
- mImeDispatcher = null;
- }
- if (!mAllCallbacks.isEmpty()) {
- OnBackInvokedCallback topCallback = getTopCallback();
- if (topCallback != null) {
- sendCancelledIfInProgress(topCallback);
- } else {
- // Should not be possible
- Log.e(TAG, "There is no topCallback, even if mAllCallbacks is not empty");
+ synchronized (mLock) {
+ if (mImeDispatcher != null) {
+ mImeDispatcher.clear();
+ mImeDispatcher = null;
}
- // Clear binder references in WM.
- setTopOnBackInvokedCallback(null);
- }
+ if (!mAllCallbacks.isEmpty()) {
+ OnBackInvokedCallback topCallback = getTopCallback();
+ if (topCallback != null) {
+ sendCancelledIfInProgress(topCallback);
+ } else {
+ // Should not be possible
+ Log.e(TAG, "There is no topCallback, even if mAllCallbacks is not empty");
+ }
+ // Clear binder references in WM.
+ setTopOnBackInvokedCallback(null);
+ }
- // We should also stop running animations since all callbacks have been removed.
- // note: mSpring.skipToEnd(), in ProgressAnimator.reset(), requires the main handler.
- Handler.getMain().post(mProgressAnimator::reset);
- mAllCallbacks.clear();
- mOnBackInvokedCallbacks.clear();
+ // We should also stop running animations since all callbacks have been removed.
+ // note: mSpring.skipToEnd(), in ProgressAnimator.reset(), requires the main handler.
+ Handler.getMain().post(mProgressAnimator::reset);
+ mAllCallbacks.clear();
+ mOnBackInvokedCallbacks.clear();
+ }
}
private void setTopOnBackInvokedCallback(@Nullable OnBackInvokedCallback callback) {
@@ -275,13 +298,15 @@
}
public OnBackInvokedCallback getTopCallback() {
- if (mAllCallbacks.isEmpty()) {
- return null;
- }
- for (Integer priority : mOnBackInvokedCallbacks.descendingKeySet()) {
- ArrayList<OnBackInvokedCallback> callbacks = mOnBackInvokedCallbacks.get(priority);
- if (!callbacks.isEmpty()) {
- return callbacks.get(callbacks.size() - 1);
+ synchronized (mLock) {
+ if (mAllCallbacks.isEmpty()) {
+ return null;
+ }
+ for (Integer priority : mOnBackInvokedCallbacks.descendingKeySet()) {
+ ArrayList<OnBackInvokedCallback> callbacks = mOnBackInvokedCallbacks.get(priority);
+ if (!callbacks.isEmpty()) {
+ return callbacks.get(callbacks.size() - 1);
+ }
}
}
return null;
@@ -315,16 +340,18 @@
public void dump(String prefix, PrintWriter writer) {
String innerPrefix = prefix + " ";
writer.println(prefix + "WindowOnBackDispatcher:");
- if (mAllCallbacks.isEmpty()) {
- writer.println(prefix + "<None>");
- return;
- }
+ synchronized (mLock) {
+ if (mAllCallbacks.isEmpty()) {
+ writer.println(prefix + "<None>");
+ return;
+ }
- writer.println(innerPrefix + "Top Callback: " + getTopCallback());
- writer.println(innerPrefix + "Callbacks: ");
- mAllCallbacks.forEach((callback, priority) -> {
- writer.println(innerPrefix + " Callback: " + callback + " Priority=" + priority);
- });
+ writer.println(innerPrefix + "Top Callback: " + getTopCallback());
+ writer.println(innerPrefix + "Callbacks: ");
+ mAllCallbacks.forEach((callback, priority) -> {
+ writer.println(innerPrefix + " Callback: " + callback + " Priority=" + priority);
+ });
+ }
}
static class OnBackInvokedCallbackWrapper extends IOnBackInvokedCallback.Stub {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java
index a32b435..4988a94 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java
@@ -28,6 +28,7 @@
import android.window.IBackAnimationRunner;
import android.window.IOnBackInvokedCallback;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.Cuj.CujType;
import com.android.wm.shell.common.InteractionJankMonitorUtils;
@@ -108,7 +109,8 @@
}
}
- private boolean shouldMonitorCUJ(RemoteAnimationTarget[] apps) {
+ @VisibleForTesting
+ boolean shouldMonitorCUJ(RemoteAnimationTarget[] apps) {
return apps.length > 0 && mCujType != NO_CUJ;
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index 4061763..65169e3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -596,6 +596,7 @@
// Set up the monitoring objects.
doNothing().when(runner).onAnimationStart(anyInt(), any(), any(), any(), any());
+ doReturn(false).when(animationRunner).shouldMonitorCUJ(any());
doReturn(runner).when(animationRunner).getRunner();
doReturn(callback).when(animationRunner).getCallback();
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index a5687e6..a0f615b 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -3779,17 +3779,25 @@
}
EventLogTags.writeWmEnterPip(r.mUserId, System.identityHashCode(r),
r.shortComponentName, Boolean.toString(isAutoEnter));
- r.setPictureInPictureParams(params);
- r.mAutoEnteringPip = isAutoEnter;
- mRootWindowContainer.moveActivityToPinnedRootTask(r,
- null /* launchIntoPipHostActivity */, "enterPictureInPictureMode",
- transition);
- // Continue the pausing process after entering pip.
- if (r.isState(PAUSING) && r.mPauseSchedulePendingForPip) {
- r.getTask().schedulePauseActivity(r, false /* userLeaving */,
- false /* pauseImmediately */, true /* autoEnteringPip */, "auto-pip");
+
+ // Ensure the ClientTransactionItems are bundled for this operation.
+ deferWindowLayout();
+ try {
+ r.setPictureInPictureParams(params);
+ r.mAutoEnteringPip = isAutoEnter;
+ mRootWindowContainer.moveActivityToPinnedRootTask(r,
+ null /* launchIntoPipHostActivity */, "enterPictureInPictureMode",
+ transition);
+ // Continue the pausing process after entering pip.
+ if (r.isState(PAUSING) && r.mPauseSchedulePendingForPip) {
+ r.getTask().schedulePauseActivity(r, false /* userLeaving */,
+ false /* pauseImmediately */, true /* autoEnteringPip */,
+ "auto-pip");
+ }
+ r.mAutoEnteringPip = false;
+ } finally {
+ continueWindowLayout();
}
- r.mAutoEnteringPip = false;
}
};
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 3eea6ac..e9a877e 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2378,6 +2378,14 @@
return false;
}
+ return resumeFocusedTasksTopActivitiesUnchecked(targetRootTask, target, targetOptions,
+ deferPause);
+ }
+
+ @VisibleForTesting
+ boolean resumeFocusedTasksTopActivitiesUnchecked(
+ Task targetRootTask, ActivityRecord target, ActivityOptions targetOptions,
+ boolean deferPause) {
boolean result = false;
if (targetRootTask != null && (targetRootTask.isTopRootTaskInDisplayArea()
|| getTopDisplayFocusedRootTask() == targetRootTask)) {
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index e1ad1be..393a01f 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -69,6 +69,7 @@
import static com.android.server.wm.ActivityRecord.State.PAUSING;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskManagerService.enforceTaskPermission;
+import static com.android.server.wm.ActivityTaskManagerService.isPip2ExperimentEnabled;
import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
@@ -829,18 +830,20 @@
}
private int applyTaskChanges(Task tr, WindowContainerTransaction.Change c) {
+ final boolean wasPrevFocusableAndVisible = tr.isFocusableAndVisible();
+
int effects = applyChanges(tr, c);
final SurfaceControl.Transaction t = c.getBoundsChangeTransaction();
if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) {
if (tr.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, c.getHidden())) {
- effects = TRANSACT_EFFECTS_LIFECYCLE;
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
}
}
if ((c.getChangeMask() & CHANGE_FORCE_TRANSLUCENT) != 0) {
tr.setForceTranslucent(c.getForceTranslucent());
- effects = TRANSACT_EFFECTS_LIFECYCLE;
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
}
if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_DRAG_RESIZING) != 0) {
@@ -873,8 +876,17 @@
boolean canEnterPip = activity.checkEnterPictureInPictureState(
"applyTaskChanges", true /* beforeStopping */);
if (canEnterPip) {
- canEnterPip = mService.mActivityClientController
- .requestPictureInPictureMode(activity);
+ mService.mTaskSupervisor.beginDeferResume();
+ try {
+ canEnterPip = mService.mActivityClientController
+ .requestPictureInPictureMode(activity);
+ } finally {
+ mService.mTaskSupervisor.endDeferResume();
+ if (canEnterPip && !isPip2ExperimentEnabled()) {
+ // Wait until the transaction is applied to only resume once.
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
+ }
+ }
}
if (!canEnterPip) {
// Restore the flag to its previous state when the activity cannot enter PIP.
@@ -883,6 +895,11 @@
}
}
+ // Activity in this Task may resume/pause when enter/exit pip.
+ if (wasPrevFocusableAndVisible != tr.isFocusableAndVisible()) {
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
+ }
+
return effects;
}
@@ -948,7 +965,7 @@
}
if ((c.getChangeMask() & CHANGE_FORCE_TRANSLUCENT) != 0) {
taskFragment.setForceTranslucent(c.getForceTranslucent());
- effects = TRANSACT_EFFECTS_LIFECYCLE;
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
}
effects |= applyChanges(taskFragment, c);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 43b424f..69b5c37 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -1681,7 +1681,8 @@
WindowContainerToken wct = rootTask.mRemoteToken.toWindowContainerToken();
t.setWindowingMode(wct, WINDOWING_MODE_PINNED);
mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
- verify(mWm.mAtmService.mRootWindowContainer).resumeFocusedTasksTopActivities();
+ verify(mWm.mAtmService.mRootWindowContainer).resumeFocusedTasksTopActivitiesUnchecked(any(),
+ any(), any(), anyBoolean());
clearInvocations(mWm.mAtmService.mRootWindowContainer);
// The token for the PIP root task may have changed when the task entered PIP mode, so do
@@ -1690,7 +1691,8 @@
record.getRootTask().mRemoteToken.toWindowContainerToken();
t.setWindowingMode(newToken, WINDOWING_MODE_FULLSCREEN);
mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
- verify(mWm.mAtmService.mRootWindowContainer).resumeFocusedTasksTopActivities();
+ verify(mWm.mAtmService.mRootWindowContainer).resumeFocusedTasksTopActivitiesUnchecked(any(),
+ any(), any(), anyBoolean());
}
@Test
diff --git a/tests/OneMedia/Android.bp b/tests/OneMedia/Android.bp
index 5c73177..a43cd39 100644
--- a/tests/OneMedia/Android.bp
+++ b/tests/OneMedia/Android.bp
@@ -16,6 +16,7 @@
platform_apis: true,
certificate: "platform",
libs: ["org.apache.http.legacy"],
+ optional_uses_libs: ["org.apache.http.legacy"],
optimize: {
enabled: false,
},