Handle process die event of a bubble activity gracefully.
When the process behind a bubble activity died (e.g. killed by lmkd,
or app crash), collapse the bubble and restart the activity when it's
expanded next time.
Bug: 126945401
Test: atest TaskStackChangedListenerTest
Change-Id: Ia2ee21f9ced51fe33756b1e12159f445d6ed083d
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
index bd2b19c..3ec5642 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
@@ -72,6 +72,13 @@
*/
public void onSingleTaskDisplayDrawn(int displayId) { }
+ /**
+ * Called when the last task is removed from a display which can only contain one task.
+ *
+ * @param displayId the id of the display from which the window is removed.
+ */
+ public void onSingleTaskDisplayEmpty(int displayId) {}
+
public void onTaskProfileLocked(int taskId, int userId) { }
public void onTaskCreated(int taskId, ComponentName componentName) { }
public void onTaskRemoved(int taskId) { }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
index c89f2ab..d514a75 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
@@ -208,6 +208,12 @@
0 /* unused */).sendToTarget();
}
+ @Override
+ public void onSingleTaskDisplayEmpty(int displayId) throws RemoteException {
+ mHandler.obtainMessage(H.ON_SINGLE_TASK_DISPLAY_EMPTY, displayId,
+ 0 /* unused */).sendToTarget();
+ }
+
private final class H extends Handler {
private static final int ON_TASK_STACK_CHANGED = 1;
private static final int ON_TASK_SNAPSHOT_CHANGED = 2;
@@ -228,6 +234,7 @@
private static final int ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED = 17;
private static final int ON_BACK_PRESSED_ON_TASK_ROOT = 18;
private static final int ON_SINGLE_TASK_DISPLAY_DRAWN = 19;
+ private static final int ON_SINGLE_TASK_DISPLAY_EMPTY = 20;
public H(Looper looper) {
@@ -370,6 +377,13 @@
}
break;
}
+ case ON_SINGLE_TASK_DISPLAY_EMPTY: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onSingleTaskDisplayEmpty(
+ msg.arg1);
+ }
+ break;
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 573a066..5d61179 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -762,6 +762,20 @@
expandedBubble.setContentVisibility(true);
}
}
+
+ @Override
+ public void onSingleTaskDisplayEmpty(int displayId) {
+ final Bubble expandedBubble = getExpandedBubble(mContext);
+ if (expandedBubble == null) {
+ return;
+ }
+ if (expandedBubble.getDisplayId() == displayId) {
+ mBubbleData.setExpanded(false);
+ }
+ if (expandedBubble.expandedView != null) {
+ expandedBubble.expandedView.notifyDisplayEmpty();
+ }
+ }
}
private static boolean areBubblesEnabled(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 0a19ac3..8c09913 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -63,6 +63,17 @@
public class BubbleExpandedView extends LinearLayout implements View.OnClickListener {
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleExpandedView" : TAG_BUBBLES;
+ private enum ActivityViewStatus {
+ // ActivityView is being initialized, cannot start an activity yet.
+ INITIALIZING,
+ // ActivityView is initialized, and ready to start an activity.
+ INITIALIZED,
+ // Activity runs in the ActivityView.
+ ACTIVITY_STARTED,
+ // ActivityView is released, so activity launching will no longer be permitted.
+ RELEASED,
+ }
+
// The triangle pointing to the expanded view
private View mPointerView;
private int mPointerMargin;
@@ -72,7 +83,7 @@
// Views for expanded state
private ActivityView mActivityView;
- private boolean mActivityViewReady = false;
+ private ActivityViewStatus mActivityViewStatus = ActivityViewStatus.INITIALIZING;
private int mTaskId = -1;
private PendingIntent mBubbleIntent;
@@ -98,19 +109,21 @@
private ActivityView.StateCallback mStateCallback = new ActivityView.StateCallback() {
@Override
public void onActivityViewReady(ActivityView view) {
- if (!mActivityViewReady) {
- mActivityViewReady = true;
- // Custom options so there is no activity transition animation
- ActivityOptions options = ActivityOptions.makeCustomAnimation(getContext(),
- 0 /* enterResId */, 0 /* exitResId */);
- // Post to keep the lifecycle normal
- post(() -> mActivityView.startActivity(mBubbleIntent, options));
+ switch (mActivityViewStatus) {
+ case INITIALIZING:
+ case INITIALIZED:
+ // Custom options so there is no activity transition animation
+ ActivityOptions options = ActivityOptions.makeCustomAnimation(getContext(),
+ 0 /* enterResId */, 0 /* exitResId */);
+ // Post to keep the lifecycle normal
+ post(() -> mActivityView.startActivity(mBubbleIntent, options));
+ mActivityViewStatus = ActivityViewStatus.ACTIVITY_STARTED;
}
}
@Override
public void onActivityViewDestroyed(ActivityView view) {
- mActivityViewReady = false;
+ mActivityViewStatus = ActivityViewStatus.RELEASED;
}
@Override
@@ -433,8 +446,10 @@
if (mActivityView == null) {
return;
}
- if (mActivityViewReady) {
- mActivityView.release();
+ switch (mActivityViewStatus) {
+ case INITIALIZED:
+ case ACTIVITY_STARTED:
+ mActivityView.release();
}
if (mTaskId != -1) {
try {
@@ -447,7 +462,16 @@
removeView(mActivityView);
mActivityView = null;
- mActivityViewReady = false;
+ }
+
+ /**
+ * Called when the last task is removed from a {@link android.hardware.display.VirtualDisplay}
+ * which {@link ActivityView} uses.
+ */
+ void notifyDisplayEmpty() {
+ if (mActivityViewStatus == ActivityViewStatus.ACTIVITY_STARTED) {
+ mActivityViewStatus = ActivityViewStatus.INITIALIZED;
+ }
}
private boolean usingActivityView() {