Ensure notif-based bubbles don't occur once and for all!! & improve logging
* Move filtering of intent into NotificationManagerService
* Add Log.w messages when an activity isn't configured correctly
* Updates tests so that they will still pass
* Add tests for badly-configured bubbles
Test: atest NotificationManagerTest (other CL)
Test: manual - send a badly configured bubble and note the logs & that
it doesn't appear
Fixes: 132913407
Change-Id: I3b3e2d63b34fe51f1aea178a92f567b6b3e0b927
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index dcc0419..7e016bb 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -16,6 +16,7 @@
package com.android.systemui.bubbles;
+import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
@@ -41,7 +42,9 @@
import android.app.IActivityTaskManager;
import android.app.Notification;
import android.app.NotificationManager;
+import android.app.PendingIntent;
import android.content.Context;
+import android.content.pm.ActivityInfo;
import android.content.pm.ParceledListSlice;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -112,7 +115,7 @@
public static final int MAX_BUBBLES = 5; // TODO: actually enforce this
// Enables some subset of notifs to automatically become bubbles
- private static final boolean DEBUG_ENABLE_AUTO_BUBBLE = false;
+ public static final boolean DEBUG_ENABLE_AUTO_BUBBLE = false;
/** Flag to enable or disable the entire feature */
private static final String ENABLE_BUBBLES = "experiment_enable_bubbles";
@@ -450,7 +453,8 @@
if (!areBubblesEnabled(mContext)) {
return;
}
- if (mNotificationInterruptionStateProvider.shouldBubbleUp(entry)) {
+ if (mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
+ && canLaunchInActivityView(mContext, entry)) {
updateShowInShadeForSuppressNotification(entry);
}
}
@@ -460,7 +464,8 @@
if (!areBubblesEnabled(mContext)) {
return;
}
- if (mNotificationInterruptionStateProvider.shouldBubbleUp(entry)) {
+ if (mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
+ && canLaunchInActivityView(mContext, entry)) {
updateBubble(entry);
}
}
@@ -470,7 +475,8 @@
if (!areBubblesEnabled(mContext)) {
return;
}
- boolean shouldBubble = mNotificationInterruptionStateProvider.shouldBubbleUp(entry);
+ boolean shouldBubble = mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
+ && canLaunchInActivityView(mContext, entry);
if (!shouldBubble && mBubbleData.hasBubbleWithKey(entry.key)) {
// It was previously a bubble but no longer a bubble -- lets remove it
removeBubble(entry.key, DISMISS_NO_LONGER_BUBBLE);
@@ -657,12 +663,6 @@
|| autoBubbleAll;
}
- private boolean shouldAutoExpand(NotificationEntry entry) {
- Notification.BubbleMetadata metadata = entry.getBubbleMetadata();
- return metadata != null && metadata.getAutoExpandBubble()
- && isForegroundApp(mContext, entry.notification.getPackageName());
- }
-
private void updateShowInShadeForSuppressNotification(NotificationEntry entry) {
boolean suppressNotification = entry.getBubbleMetadata() != null
&& entry.getBubbleMetadata().isNotificationSuppressed()
@@ -771,6 +771,48 @@
(int) (defaultBounciness * 100)) / 100f;
}
+ /**
+ * Whether an intent is properly configured to display in an {@link android.app.ActivityView}.
+ *
+ * Keep checks in sync with NotificationManagerService#canLaunchInActivityView. Typically
+ * that should filter out any invalid bubbles, but should protect SysUI side just in case.
+ *
+ * @param context the context to use.
+ * @param entry the entry to bubble.
+ */
+ static boolean canLaunchInActivityView(Context context, NotificationEntry entry) {
+ PendingIntent intent = entry.getBubbleMetadata() != null
+ ? entry.getBubbleMetadata().getIntent()
+ : null;
+ if (intent == null) {
+ Log.w(TAG, "Unable to create bubble -- no intent");
+ return false;
+ }
+ ActivityInfo info =
+ intent.getIntent().resolveActivityInfo(context.getPackageManager(), 0);
+ if (info == null) {
+ Log.w(TAG, "Unable to send as bubble -- couldn't find activity info for intent: "
+ + intent);
+ return false;
+ }
+ if (!ActivityInfo.isResizeableMode(info.resizeMode)) {
+ Log.w(TAG, "Unable to send as bubble -- activity is not resizable for intent: "
+ + intent);
+ return false;
+ }
+ if (info.documentLaunchMode != DOCUMENT_LAUNCH_ALWAYS) {
+ Log.w(TAG, "Unable to send as bubble -- activity is not documentLaunchMode=always "
+ + "for intent: " + intent);
+ return false;
+ }
+ if ((info.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) == 0) {
+ Log.w(TAG, "Unable to send as bubble -- activity is not embeddable for intent: "
+ + intent);
+ return false;
+ }
+ return true;
+ }
+
/** PinnedStackListener that dispatches IME visibility updates to the stack. */
private class BubblesImeListener extends IPinnedStackListener.Stub {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index e7948b5..6add4a4 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -16,12 +16,10 @@
package com.android.systemui.bubbles;
-import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS;
-import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING;
-import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE;
-import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS;
import static android.view.Display.INVALID_DISPLAY;
+import static com.android.systemui.bubbles.BubbleController.DEBUG_ENABLE_AUTO_BUBBLE;
+
import android.annotation.Nullable;
import android.app.ActivityOptions;
import android.app.ActivityView;
@@ -30,7 +28,6 @@
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
@@ -337,11 +334,10 @@
mNotifRow = null;
}
mActivityView.setVisibility(VISIBLE);
- } else {
+ } else if (DEBUG_ENABLE_AUTO_BUBBLE) {
// Hide activity view if we had it previously
mActivityView.setVisibility(GONE);
mNotifRow = mEntry.getRow();
-
}
updateView();
}
@@ -532,59 +528,14 @@
@Nullable
private PendingIntent getBubbleIntent(NotificationEntry entry) {
Notification notif = entry.notification.getNotification();
- String packageName = entry.notification.getPackageName();
Notification.BubbleMetadata data = notif.getBubbleMetadata();
- if (data != null && canLaunchInActivityView(data.getIntent(), true /* enableLogging */,
- packageName)) {
+ if (BubbleController.canLaunchInActivityView(mContext, entry) && data != null) {
return data.getIntent();
- } else if (BubbleController.shouldUseContentIntent(mContext)
- && canLaunchInActivityView(notif.contentIntent, false /* enableLogging */,
- packageName)) {
- return notif.contentIntent;
}
return null;
}
/**
- * Whether an intent is properly configured to display in an {@link android.app.ActivityView}.
- *
- * @param intent the pending intent of the bubble.
- * @param enableLogging whether bubble developer error should be logged.
- * @param packageName the notification package name for this bubble.
- * @return
- */
- private boolean canLaunchInActivityView(PendingIntent intent, boolean enableLogging,
- String packageName) {
- if (intent == null) {
- return false;
- }
- ActivityInfo info =
- intent.getIntent().resolveActivityInfo(mContext.getPackageManager(), 0);
- if (info == null) {
- if (enableLogging) {
- StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
- BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING);
- }
- return false;
- }
- if (!ActivityInfo.isResizeableMode(info.resizeMode)) {
- if (enableLogging) {
- StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
- BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE);
- }
- return false;
- }
- if (info.documentLaunchMode != DOCUMENT_LAUNCH_ALWAYS) {
- if (enableLogging) {
- StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
- BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS);
- }
- return false;
- }
- return (info.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) != 0;
- }
-
- /**
* Listener that is notified when a bubble is blocked.
*/
public interface OnBubbleBlockedListener {