Send task description changed though task org taskInfoChanged

- This removes the need for a separate task stack listener and aligns
  with other task info change properties
- Also implement equals/hashCode for the token so we can use it in
  containers

Bug: 148977538
Test: atest TaskOrganizerTests

Signed-off-by: Winson Chung <winsonc@google.com>
Change-Id: Ie035e6389fdbdc374c1a4b4a684758efa0cb7a9e
diff --git a/api/test-current.txt b/api/test-current.txt
index 641767c..0dff41b 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -5232,14 +5232,14 @@
     method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public static java.util.List<android.app.ActivityManager.RunningTaskInfo> getChildTasks(@NonNull android.window.WindowContainerToken, @NonNull int[]);
     method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public static android.window.WindowContainerToken getImeTarget(int);
     method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public static java.util.List<android.app.ActivityManager.RunningTaskInfo> getRootTasks(int, @NonNull int[]);
-    method public void onBackPressedOnTaskRoot(@NonNull android.app.ActivityManager.RunningTaskInfo);
-    method public void onTaskAppeared(@NonNull android.app.ActivityManager.RunningTaskInfo);
-    method public void onTaskInfoChanged(@NonNull android.app.ActivityManager.RunningTaskInfo);
-    method public void onTaskVanished(@NonNull android.app.ActivityManager.RunningTaskInfo);
-    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void registerOrganizer(int);
+    method @BinderThread public void onBackPressedOnTaskRoot(@NonNull android.app.ActivityManager.RunningTaskInfo);
+    method @BinderThread public void onTaskAppeared(@NonNull android.app.ActivityManager.RunningTaskInfo);
+    method @BinderThread public void onTaskInfoChanged(@NonNull android.app.ActivityManager.RunningTaskInfo);
+    method @BinderThread public void onTaskVanished(@NonNull android.app.ActivityManager.RunningTaskInfo);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public final void registerOrganizer(int);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void setInterceptBackPressedOnTaskRoot(boolean);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public static void setLaunchRoot(int, @NonNull android.window.WindowContainerToken);
-    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void unregisterOrganizer();
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public final void unregisterOrganizer();
   }
 
   public final class WindowContainerToken implements android.os.Parcelable {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index b6d519a..fd73632 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -69,6 +69,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.WorkSource;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.DisplayMetrics;
 import android.util.Singleton;
@@ -1597,6 +1598,39 @@
                     + " resizeMode: " + ActivityInfo.resizeModeToString(mResizeMode)
                     + " minWidth: " + mMinWidth + " minHeight: " + mMinHeight;
         }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof TaskDescription)) {
+                return false;
+            }
+
+            TaskDescription other = (TaskDescription) obj;
+            return TextUtils.equals(mLabel, other.mLabel)
+                    && TextUtils.equals(mIconFilename, other.mIconFilename)
+                    && mIcon == other.mIcon
+                    && mColorPrimary == other.mColorPrimary
+                    && mColorBackground == other.mColorBackground
+                    && mStatusBarColor == other.mStatusBarColor
+                    && mNavigationBarColor == other.mNavigationBarColor
+                    && mEnsureStatusBarContrastWhenTransparent
+                            == other.mEnsureStatusBarContrastWhenTransparent
+                    && mEnsureNavigationBarContrastWhenTransparent
+                            == other.mEnsureNavigationBarContrastWhenTransparent
+                    && mResizeMode == other.mResizeMode
+                    && mMinWidth == other.mMinWidth
+                    && mMinHeight == other.mMinHeight;
+        }
+
+        /** @hide */
+        public static boolean equals(TaskDescription td1, TaskDescription td2) {
+            if (td1 == null && td2 == null) {
+                return true;
+            } else if (td1 != null && td2 != null) {
+                return td1.equals(td2);
+            }
+            return false;
+        }
     }
 
     /**
diff --git a/core/java/android/window/TaskEmbedder.java b/core/java/android/window/TaskEmbedder.java
index 45ab310..2ead37a 100644
--- a/core/java/android/window/TaskEmbedder.java
+++ b/core/java/android/window/TaskEmbedder.java
@@ -26,7 +26,6 @@
 import android.app.ActivityView;
 import android.app.IActivityTaskManager;
 import android.app.PendingIntent;
-import android.app.TaskStackListener;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -129,7 +128,6 @@
 
     protected SurfaceControl.Transaction mTransaction;
     protected SurfaceControl mSurfaceControl;
-    protected TaskStackListener mTaskStackListener;
     protected Listener mListener;
     protected boolean mOpened; // Protected by mGuard.
 
@@ -170,13 +168,6 @@
         if (!onInitialize()) {
             return false;
         }
-
-        mTaskStackListener = createTaskStackListener();
-        try {
-            mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to register task stack listener", e);
-        }
         if (mListener != null && isInitialized()) {
             mListener.onInitialized();
         }
@@ -187,11 +178,6 @@
     }
 
     /**
-     * @return the task stack listener for this embedder
-     */
-    public abstract TaskStackListener createTaskStackListener();
-
-    /**
      * Whether this container has been initialized.
      *
      * @return true if initialized
@@ -420,16 +406,6 @@
         mSurfaceControl.release();
 
         boolean reportReleased = onRelease();
-
-        if (mTaskStackListener != null) {
-            try {
-                mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Failed to unregister task stack listener", e);
-            }
-            mTaskStackListener = null;
-        }
-
         if (mListener != null && reportReleased) {
             mListener.onReleased();
         }
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index 5098b44..f661d9a 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -16,6 +16,7 @@
 
 package android.window;
 
+import android.annotation.BinderThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -39,7 +40,7 @@
      * and receive taskVanished callbacks in the process.
      */
     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
-    public void registerOrganizer(int windowingMode) {
+    public final void registerOrganizer(int windowingMode) {
         try {
             getController().registerTaskOrganizer(mInterface, windowingMode);
         } catch (RemoteException e) {
@@ -49,7 +50,7 @@
 
     /** Unregisters a previously registered task organizer. */
     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
-    public void unregisterOrganizer() {
+    public final void unregisterOrganizer() {
         try {
             getController().unregisterTaskOrganizer(mInterface);
         } catch (RemoteException e) {
@@ -57,13 +58,17 @@
         }
     }
 
+    @BinderThread
     public void onTaskAppeared(@NonNull ActivityManager.RunningTaskInfo taskInfo) {}
 
+    @BinderThread
     public void onTaskVanished(@NonNull ActivityManager.RunningTaskInfo taskInfo) {}
 
-    public void onTaskInfoChanged(@NonNull ActivityManager.RunningTaskInfo info) {}
+    @BinderThread
+    public void onTaskInfoChanged(@NonNull ActivityManager.RunningTaskInfo taskInfo) {}
 
-    public void onBackPressedOnTaskRoot(@NonNull ActivityManager.RunningTaskInfo info) {}
+    @BinderThread
+    public void onBackPressedOnTaskRoot(@NonNull ActivityManager.RunningTaskInfo taskInfo) {}
 
     /** Creates a persistent root task in WM for a particular windowing-mode. */
     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
diff --git a/core/java/android/window/TaskOrganizerTaskEmbedder.java b/core/java/android/window/TaskOrganizerTaskEmbedder.java
index 39a0101..b63741e 100644
--- a/core/java/android/window/TaskOrganizerTaskEmbedder.java
+++ b/core/java/android/window/TaskOrganizerTaskEmbedder.java
@@ -21,7 +21,6 @@
 
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
-import android.app.ActivityView;
 import android.app.TaskStackListener;
 import android.content.Context;
 import android.graphics.Rect;
@@ -55,11 +54,6 @@
         super(context, host);
     }
 
-    @Override
-    public TaskStackListener createTaskStackListener() {
-        return new TaskStackListenerImpl();
-    }
-
     /**
      * Whether this container has been initialized.
      *
@@ -219,29 +213,6 @@
         Log.d(TAG, "[" + System.identityHashCode(this) + "] " + msg);
     }
 
-    /**
-     * A task change listener that detects background color change of the topmost stack on our
-     * virtual display and updates the background of the surface view. This background will be shown
-     * when surface view is resized, but the app hasn't drawn its content in new size yet.
-     * It also calls StateCallback.onTaskMovedToFront to notify interested parties that the stack
-     * associated with the {@link ActivityView} has had a Task moved to the front. This is useful
-     * when needing to also bring the host Activity to the foreground at the same time.
-     */
-    private class TaskStackListenerImpl extends TaskStackListener {
-
-        @Override
-        public void onTaskDescriptionChanged(ActivityManager.RunningTaskInfo taskInfo) {
-            if (!isInitialized()) {
-                return;
-            }
-            if (taskInfo.taskId == mTaskInfo.taskId) {
-                mTaskInfo.taskDescription = taskInfo.taskDescription;
-                mHost.onTaskBackgroundColorChanged(TaskOrganizerTaskEmbedder.this,
-                        taskInfo.taskDescription.getBackgroundColor());
-            }
-        }
-    }
-
     private class TaskOrganizerImpl extends TaskOrganizer {
         @Override
         public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo) {
@@ -249,7 +220,6 @@
                 log("taskAppeared: " + taskInfo.taskId);
             }
 
-            // TODO: Ensure visibility/alpha of the leash in its initial state?
             mTaskInfo = taskInfo;
             mTaskToken = taskInfo.token;
             mTaskLeash = mTaskToken.getLeash();
@@ -270,6 +240,13 @@
         }
 
         @Override
+        public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+            mTaskInfo.taskDescription = taskInfo.taskDescription;
+            mHost.post(() -> mHost.onTaskBackgroundColorChanged(TaskOrganizerTaskEmbedder.this,
+                    taskInfo.taskDescription.getBackgroundColor()));
+        }
+
+        @Override
         public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
             if (DEBUG) {
                 log("taskVanished: " + taskInfo.taskId);
diff --git a/core/java/android/window/VirtualDisplayTaskEmbedder.java b/core/java/android/window/VirtualDisplayTaskEmbedder.java
index 0f26d5d..7016469 100644
--- a/core/java/android/window/VirtualDisplayTaskEmbedder.java
+++ b/core/java/android/window/VirtualDisplayTaskEmbedder.java
@@ -67,6 +67,7 @@
     private VirtualDisplay mVirtualDisplay;
     private Insets mForwardedInsets;
     private DisplayMetrics mTmpDisplayMetrics;
+    private TaskStackListener mTaskStackListener;
 
     /**
      * Constructs a new TaskEmbedder.
@@ -82,11 +83,6 @@
         mSingleTaskInstance = singleTaskInstance;
     }
 
-    @Override
-    public TaskStackListener createTaskStackListener() {
-        return new TaskStackListenerImpl();
-    }
-
     /**
      * Whether this container has been initialized.
      *
@@ -124,6 +120,9 @@
                         .setDisplayToSingleTaskInstance(displayId);
             }
             setForwardedInsets(mForwardedInsets);
+
+            mTaskStackListener = new TaskStackListenerImpl();
+            mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
         } catch (RemoteException e) {
             e.rethrowAsRuntimeException();
         }
@@ -142,6 +141,15 @@
         // Clear tap-exclude region (if any) for this window.
         clearTapExcludeRegion();
 
+        if (mTaskStackListener != null) {
+            try {
+                mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to unregister task stack listener", e);
+            }
+            mTaskStackListener = null;
+        }
+
         if (isInitialized()) {
             mVirtualDisplay.release();
             mVirtualDisplay = null;
diff --git a/core/java/android/window/WindowContainerToken.java b/core/java/android/window/WindowContainerToken.java
index dde98da..3316d0e 100644
--- a/core/java/android/window/WindowContainerToken.java
+++ b/core/java/android/window/WindowContainerToken.java
@@ -84,4 +84,17 @@
     public int describeContents() {
         return 0;
     }
+
+    @Override
+    public int hashCode() {
+        return mRealToken.asBinder().hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof WindowContainerToken)) {
+            return false;
+        }
+        return mRealToken.asBinder() == ((WindowContainerToken) obj).asBinder();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
index 91d638e..a4b1310 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
@@ -105,10 +105,6 @@
         mDivider.getHandler().post(() -> handleTaskInfoChanged(taskInfo));
     }
 
-    @Override
-    public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
-    }
-
     /**
      * This is effectively a finite state machine which moves between the various split-screen
      * presentations based on the contents of the split regions.
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 856fbc7..ac13492 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -5592,6 +5592,7 @@
             _taskDescription.setIconFilename(iconFilePath);
         }
         taskDescription = _taskDescription;
+        getTask().updateTaskDescription();
     }
 
     void setVoiceSessionLocked(IVoiceInteractionSession session) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index c253cd2..f4eb0d2 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -3061,8 +3061,6 @@
             ActivityRecord r = ActivityRecord.isInStackLocked(token);
             if (r != null) {
                 r.setTaskDescription(td);
-                final Task task = r.getTask();
-                task.updateTaskDescription();
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 1e70573..2742b38 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1767,6 +1767,10 @@
                 t.updateTaskDescription();
             }
         }
+
+        if (isOrganized()) {
+            mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(this, true /* force */);
+        }
     }
 
     private static boolean setTaskDescriptionFromActivityAboveRoot(
@@ -1962,7 +1966,7 @@
         final boolean taskOrgChanged = updateTaskOrganizerState(false /* forceUpdate */);
         // If the task organizer has changed, then it will already be receiving taskAppeared with
         // the latest task-info thus the task-info won't have changed.
-        if (!taskOrgChanged && mTaskOrganizer != null) {
+        if (!taskOrgChanged && isOrganized()) {
             mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(this, false /* force */);
         }
     }
@@ -4153,8 +4157,6 @@
         // Let the old organizer know it has lost control.
         sendTaskVanished();
         mTaskOrganizer = organizer;
-
-
         sendTaskAppeared();
         onTaskOrganizerChanged();
         return true;
@@ -4291,8 +4293,9 @@
 
     void setPictureInPictureParams(PictureInPictureParams p) {
         mPictureInPictureParams.copyOnlySet(p);
-        mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(
-                this, true /* force */);
+        if (isOrganized()) {
+            mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(this, true /* force */);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 9873031..adc50bf 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -27,6 +27,7 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityManager.TaskDescription;
 import android.app.WindowConfiguration;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
@@ -439,7 +440,8 @@
         boolean changed = lastInfo == null
                 || mTmpTaskInfo.topActivityType != lastInfo.topActivityType
                 || mTmpTaskInfo.isResizable() != lastInfo.isResizable()
-                || mTmpTaskInfo.pictureInPictureParams != lastInfo.pictureInPictureParams;
+                || mTmpTaskInfo.pictureInPictureParams != lastInfo.pictureInPictureParams
+                || !TaskDescription.equals(mTmpTaskInfo.taskDescription, lastInfo.taskDescription);
         if (!changed) {
             int cfgChanges = mTmpTaskInfo.configuration.diff(lastInfo.configuration);
             final int winCfgChanges = (cfgChanges & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
index f275e37..e41d4dc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
@@ -50,6 +50,7 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 
+import android.app.ActivityManager;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityManager.StackInfo;
 import android.app.IRequestFinishCallback;
@@ -856,6 +857,30 @@
     }
 
     @Test
+    public void testChangeTaskDescription() {
+        class ChangeSavingOrganizer extends StubOrganizer {
+            RunningTaskInfo mChangedInfo;
+            @Override
+            public void onTaskInfoChanged(RunningTaskInfo info) {
+                mChangedInfo = info;
+            }
+        }
+        ChangeSavingOrganizer o = new ChangeSavingOrganizer();
+        mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o,
+                WINDOWING_MODE_MULTI_WINDOW);
+
+        final ActivityStack stack = createStack();
+        final Task task = createTask(stack);
+        final ActivityRecord record = WindowTestUtils.createActivityRecordInTask(
+                stack.mDisplayContent, task);
+
+        stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription"));
+        waitUntilHandlersIdle();
+        assertEquals("TestDescription", o.mChangedInfo.taskDescription.getLabel());
+    }
+
+    @Test
     public void testPreventDuplicateAppear() throws RemoteException {
         final ActivityStack stack = createStack();
         final Task task = createTask(stack);