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();
+ }
}
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index 5136705..4c27a3c 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -49,6 +49,9 @@
<activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityRequestedOrientationChange" />
<activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityTaskChangeCallbacks" />
<activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityTaskDescriptionChange" />
+ <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityViewTestActivity" />
+ <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityInActivityView"
+ android:resizeableActivity="true" />
<activity android:name="com.android.server.wm.ScreenDecorWindowTests$TestActivity"
android:showWhenLocked="true" />
</application>
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
index 62247d8..19fd93fe 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static org.junit.Assert.assertEquals;
@@ -26,18 +28,22 @@
import android.app.ActivityManager;
import android.app.ActivityManager.TaskDescription;
import android.app.ActivityTaskManager;
+import android.app.ActivityView;
import android.app.IActivityManager;
import android.app.ITaskStackListener;
+import android.app.Instrumentation;
import android.app.Instrumentation.ActivityMonitor;
import android.app.TaskStackListener;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.os.Bundle;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.support.test.uiautomator.UiDevice;
import android.text.TextUtils;
+import android.view.ViewGroup;
import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
@@ -231,6 +237,40 @@
assertTrue(activity.mOnDetachedFromWindowCalled);
}
+ @Test
+ public void testTaskOnSingleTaskDisplayDrawn() throws Exception {
+ final Instrumentation instrumentation = getInstrumentation();
+
+ final CountDownLatch activityViewReadyLatch = new CountDownLatch(1);
+ final CountDownLatch singleTaskDisplayDrawnLatch = new CountDownLatch(1);
+ registerTaskStackChangedListener(new TaskStackListener() {
+ @Override
+ public void onSingleTaskDisplayDrawn(int displayId) throws RemoteException {
+ singleTaskDisplayDrawnLatch.countDown();
+ }
+ });
+ final ActivityViewTestActivity activity =
+ (ActivityViewTestActivity) startTestActivity(ActivityViewTestActivity.class);
+ final ActivityView activityView = activity.getActivityView();
+ activityView.setCallback(new ActivityView.StateCallback() {
+ @Override
+ public void onActivityViewReady(ActivityView view) {
+ activityViewReadyLatch.countDown();
+ }
+
+ @Override
+ public void onActivityViewDestroyed(ActivityView view) {
+ }
+ });
+ waitForCallback(activityViewReadyLatch);
+
+ final Context context = instrumentation.getContext();
+ Intent intent = new Intent(context, ActivityInActivityView.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+ activityView.startActivity(intent);
+ waitForCallback(singleTaskDisplayDrawnLatch);
+ }
+
/**
* Starts the provided activity and returns the started instance.
*/
@@ -369,4 +409,29 @@
mOnDetachedFromWindowCountDownLatch = countDownLatch;
}
}
+
+ public static class ActivityViewTestActivity extends TestActivity {
+ private ActivityView mActivityView;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mActivityView = new ActivityView(this, null /* attrs */, 0 /* defStyle */,
+ true /* singleTaskInstance */);
+ setContentView(mActivityView);
+
+ ViewGroup.LayoutParams layoutParams = mActivityView.getLayoutParams();
+ layoutParams.width = MATCH_PARENT;
+ layoutParams.height = MATCH_PARENT;
+ mActivityView.requestLayout();
+ }
+
+ ActivityView getActivityView() {
+ return mActivityView;
+ }
+ }
+
+ // Activity that has {@link android.R.attr#resizeableActivity} attribute set to {@code true}
+ public static class ActivityInActivityView extends TestActivity {}
}