Synchronize bubble activity rendering status and its visibility change.

- Add an API ITaskStackListener.onSingleTaskDisplayDrawn() to notifity contents
  are drawn for the first time on a display which can only contain one task.
- BubbleController updates contents visibility (actually alpha value) of
  the Surface in a ActivityView.

Bug: 130442248
Test: atest WmTests:TaskStackChangedListenerTest
Change-Id: Ie5aed373996419b059935889b564ca91c2e3cf23
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 3d59e66..b3b6efe 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -40,6 +40,7 @@
 import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
 import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
 import static android.view.WindowManager.TRANSIT_NONE;
+import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
 import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
 import static android.view.WindowManager.TRANSIT_TASK_OPEN;
 import static android.view.WindowManager.TRANSIT_TASK_OPEN_BEHIND;
@@ -3194,6 +3195,8 @@
                 if (newTask) {
                     if (r.mLaunchTaskBehind) {
                         transit = TRANSIT_TASK_OPEN_BEHIND;
+                    } else if (getDisplay().isSingleTaskInstance()) {
+                        transit = TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
                     } else {
                         // If a new task is being launched, then mark the existing top activity as
                         // supporting picture-in-picture while pausing only if the starting activity
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index ed56501..dbc530d 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -34,7 +34,6 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.service.voice.IVoiceInteractionSession;
-import android.util.Pair;
 import android.util.SparseIntArray;
 import android.util.proto.ProtoOutputStream;
 
@@ -189,6 +188,13 @@
     public abstract void notifyDockedStackMinimizedChanged(boolean minimized);
 
     /**
+     * Notify listeners that contents are drawn for the first time on a single task display.
+     *
+     * @param displayId An ID of the display on which contents are drawn.
+     */
+    public abstract void notifySingleTaskDisplayDrawn(int displayId);
+
+    /**
      * Start activity {@code intents} as if {@code packageName} on user {@code userId} did it.
      *
      * - DO NOT call it with the calling UID cleared.
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 772e5e6..e9a266c 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -6087,7 +6087,8 @@
         }
 
         @Override
-        public void notifyAppTransitionStarting(SparseIntArray reasons, long timestamp) {
+        public void notifyAppTransitionStarting(SparseIntArray reasons,
+                long timestamp) {
             synchronized (mGlobalLock) {
                 mStackSupervisor.getActivityMetricsLogger().notifyTransitionStarting(
                         reasons, timestamp);
@@ -6095,6 +6096,11 @@
         }
 
         @Override
+        public void notifySingleTaskDisplayDrawn(int displayId) {
+            mTaskChangeNotificationController.notifySingleTaskDisplayDrawn(displayId);
+        }
+
+        @Override
         public void notifyAppTransitionFinished() {
             synchronized (mGlobalLock) {
                 mStackSupervisor.notifyAppTransitionDone();
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index ddd5c0a..19ccc62 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -29,6 +29,7 @@
 import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
 import static android.view.WindowManager.TRANSIT_NONE;
+import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
 import static android.view.WindowManager.TRANSIT_TASK_CHANGE_WINDOWING_MODE;
 import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
 import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE;
@@ -2052,6 +2053,9 @@
             case TRANSIT_CRASHING_ACTIVITY_CLOSE: {
                 return "TRANSIT_CRASHING_ACTIVITY_CLOSE";
             }
+            case TRANSIT_SHOW_SINGLE_TASK_DISPLAY: {
+                return "TRANSIT_SHOW_SINGLE_TASK_DISPLAY";
+            }
             default: {
                 return "<UNKNOWN: " + transition + ">";
             }
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index d4c4e6a..6c5ef52 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -28,6 +28,7 @@
 import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
 import static android.view.WindowManager.TRANSIT_NONE;
+import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
 import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
 import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE;
 import static android.view.WindowManager.TRANSIT_TASK_OPEN;
@@ -211,6 +212,12 @@
         mService.mAtmInternal.notifyAppTransitionStarting(mTempTransitionReasons.clone(),
                 SystemClock.uptimeMillis());
 
+        if (transit == TRANSIT_SHOW_SINGLE_TASK_DISPLAY) {
+            mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
+                mService.mAtmInternal.notifySingleTaskDisplayDrawn(mDisplayContent.getDisplayId());
+            });
+        }
+
         Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
 
         mDisplayContent.pendingLayoutChanges |=
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index 3ec461d..d58c613 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -35,6 +35,7 @@
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
+import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
 
 import static com.android.server.am.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER;
 import static com.android.server.am.ActivityStackSupervisorProto.DISPLAYS;
@@ -1217,6 +1218,15 @@
                 if (displayShouldSleep) {
                     stack.goToSleepIfPossible(false /* shuttingDown */);
                 } else {
+                    // When the display which can only contain one task turns on, start a special
+                    // transition. {@link AppTransitionController#handleAppTransitionReady} later
+                    // picks up the transition, and schedules
+                    // {@link ITaskStackListener#onSingleTaskDisplayDrawn} callback which is
+                    // triggered after contents are drawn on the display.
+                    if (display.isSingleTaskInstance()) {
+                        display.mDisplayContent.prepareAppTransition(
+                                TRANSIT_SHOW_SINGLE_TASK_DISPLAY, false);
+                    }
                     stack.awakeFromSleepingLocked();
                     if (stack.isFocusedStackOnDisplay()
                             && !mStackSupervisor.getKeyguardController()
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index 66200e3..27175c7 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -54,6 +54,7 @@
     private static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED_MSG = 19;
     private static final int NOTIFY_SIZE_COMPAT_MODE_ACTIVITY_CHANGED_MSG = 20;
     private static final int NOTIFY_BACK_PRESSED_ON_TASK_ROOT = 21;
+    private static final int NOTIFY_SINGLE_TASK_DISPLAY_DRAWN = 22;
 
     // Delay in notifying task stack change listeners (in millis)
     private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
@@ -154,6 +155,10 @@
         l.onSizeCompatModeActivityChanged(m.arg1, (IBinder) m.obj);
     };
 
+    private final TaskStackConsumer mNotifySingleTaskDisplayDrawn = (l, m) -> {
+        l.onSingleTaskDisplayDrawn(m.arg1);
+    };
+
     @FunctionalInterface
     public interface TaskStackConsumer {
         void accept(ITaskStackListener t, Message m) throws RemoteException;
@@ -233,6 +238,9 @@
                 case NOTIFY_BACK_PRESSED_ON_TASK_ROOT:
                     forAllRemoteListeners(mNotifyBackPressedOnTaskRoot, msg);
                     break;
+                case NOTIFY_SINGLE_TASK_DISPLAY_DRAWN:
+                    forAllRemoteListeners(mNotifySingleTaskDisplayDrawn, msg);
+                    break;
             }
         }
     }
@@ -477,4 +485,14 @@
         forAllLocalListeners(mNotifyBackPressedOnTaskRoot, msg);
         msg.sendToTarget();
     }
+
+    /**
+     * Notify listeners that contents are drawn for the first time on a single task display.
+     */
+    void notifySingleTaskDisplayDrawn(int displayId) {
+        final Message msg = mHandler.obtainMessage(NOTIFY_SINGLE_TASK_DISPLAY_DRAWN,
+                displayId, 0 /* unused */);
+        forAllLocalListeners(mNotifySingleTaskDisplayDrawn, msg);
+        msg.sendToTarget();
+    }
 }