diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 78d3581a70129..9d0364eba39f5 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -78,10 +78,9 @@ interface INotificationManager boolean shouldHideSilentStatusIcons(String callingPkg); void setHideSilentStatusIcons(boolean hide); - void setBubblesAllowed(String pkg, int uid, boolean allowed); + void setBubblesAllowed(String pkg, int uid, int bubblePreference); boolean areBubblesAllowed(String pkg); - boolean areBubblesAllowedForPackage(String pkg, int uid); - boolean hasUserApprovedBubblesForPackage(String pkg, int uid); + int getBubblePreferenceForPackage(String pkg, int uid); void createNotificationChannelGroups(String pkg, in ParceledListSlice channelGroupList); void createNotificationChannels(String pkg, in ParceledListSlice channelsList); diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java index d1d67f0d81d87..2feb9277775c5 100644 --- a/core/java/android/app/NotificationChannel.java +++ b/core/java/android/app/NotificationChannel.java @@ -102,7 +102,7 @@ public final class NotificationChannel implements Parcelable { private static final String ATT_FG_SERVICE_SHOWN = "fgservice"; private static final String ATT_GROUP = "group"; private static final String ATT_BLOCKABLE_SYSTEM = "blockable_system"; - private static final String ATT_ALLOW_BUBBLE = "can_bubble"; + private static final String ATT_ALLOW_BUBBLE = "allow_bubble"; private static final String ATT_ORIG_IMP = "orig_imp"; private static final String ATT_PARENT_CHANNEL = "parent"; private static final String ATT_CONVERSATION_ID = "conv_id"; @@ -168,7 +168,7 @@ public final class NotificationChannel implements Parcelable { NotificationManager.IMPORTANCE_UNSPECIFIED; private static final boolean DEFAULT_DELETED = false; private static final boolean DEFAULT_SHOW_BADGE = true; - private static final boolean DEFAULT_ALLOW_BUBBLE = true; + private static final boolean DEFAULT_ALLOW_BUBBLE = false; @UnsupportedAppUsage private String mId; @@ -545,15 +545,8 @@ public final class NotificationChannel implements Parcelable { } /** - * Sets whether notifications posted to this channel can appear outside of the notification - * shade, floating over other apps' content as a bubble. - * - *
This value will be ignored for channels that aren't allowed to pop on screen (that is, - * channels whose {@link #getImportance() importance} is < - * {@link NotificationManager#IMPORTANCE_HIGH}.
- * - *Only modifiable before the channel is submitted to - * * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
+ * As of Android 11 this value is no longer respected. + * @see #canBubble() * @see Notification#getBubbleMetadata() */ public void setAllowBubbles(boolean allowBubbles) { @@ -702,8 +695,10 @@ public final class NotificationChannel implements Parcelable { } /** - * Returns whether notifications posted to this channel can display outside of the notification - * shade, in a floating window on top of other apps. + * Returns whether notifications posted to this channel are allowed to display outside of the + * notification shade, in a floating window on top of other apps. + * + * @see Notification#getBubbleMetadata() */ public boolean canBubble() { return mAllowBubbles; diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 0e97e3fe06cee..d6df400f86b62 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -452,6 +452,19 @@ public class NotificationManager { */ public static final int IMPORTANCE_MAX = 5; + /** + * @hide + */ + public static final int BUBBLE_PREFERENCE_NONE = 0; + /** + * @hide + */ + public static final int BUBBLE_PREFERENCE_ALL = 1; + /** + * @hide + */ + public static final int BUBBLE_PREFERENCE_SELECTED = 2; + @UnsupportedAppUsage private static INotificationManager sService; @@ -1213,7 +1226,7 @@ public class NotificationManager { /** - * Sets whether notifications posted by this app can appear outside of the + * Gets whether all notifications posted by this app can appear outside of the * notification shade, floating over other apps' content. * *This value will be ignored for notifications that are posted to channels that do not
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index 6fc1264d69e2a..290784ddbffec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -17,6 +17,8 @@
package com.android.systemui.statusbar.notification.row;
import static android.app.Notification.EXTRA_IS_GROUP_CONVERSATION;
+import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
+import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
@@ -598,6 +600,13 @@ public class NotificationConversationInfo extends LinearLayout implements
!mChannelToUpdate.isImportantConversation());
if (mChannelToUpdate.isImportantConversation()) {
mChannelToUpdate.setAllowBubbles(true);
+ int currentPref =
+ mINotificationManager.getBubblePreferenceForPackage(
+ mAppPkg, mAppUid);
+ if (currentPref == BUBBLE_PREFERENCE_NONE) {
+ mINotificationManager.setBubblesAllowed(mAppPkg, mAppUid,
+ BUBBLE_PREFERENCE_SELECTED);
+ }
}
mChannelToUpdate.setImportance(Math.max(
mChannelToUpdate.getOriginalImportance(), IMPORTANCE_DEFAULT));
diff --git a/services/core/java/com/android/server/notification/BubbleExtractor.java b/services/core/java/com/android/server/notification/BubbleExtractor.java
index 2fa80cd8e4e4f..536ea30d6a947 100644
--- a/services/core/java/com/android/server/notification/BubbleExtractor.java
+++ b/services/core/java/com/android/server/notification/BubbleExtractor.java
@@ -16,12 +16,17 @@
package com.android.server.notification;
import static android.app.Notification.FLAG_BUBBLE;
+import static android.app.NotificationChannel.USER_LOCKED_ALLOW_BUBBLE;
+import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
+import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
+import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED;
import static com.android.internal.util.FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING;
import static com.android.internal.util.FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE;
import android.app.ActivityManager;
import android.app.Notification;
+import android.app.NotificationChannel;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
@@ -32,13 +37,13 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
/**
- * Determines whether a bubble can be shown for this notification
+ * Determines whether a bubble can be shown for this notification.
*/
public class BubbleExtractor implements NotificationSignalExtractor {
private static final String TAG = "BubbleExtractor";
private static final boolean DBG = false;
- private BubbleChecker mBubbleChecker;
+ private ShortcutHelper mShortcutHelper;
private RankingConfig mConfig;
private ActivityManager mActivityManager;
private Context mContext;
@@ -60,24 +65,34 @@ public class BubbleExtractor implements NotificationSignalExtractor {
return null;
}
- if (mBubbleChecker == null) {
- if (DBG) Slog.d(TAG, "missing bubble checker");
+ if (mShortcutHelper == null) {
+ if (DBG) Slog.d(TAG, "missing shortcut helper");
return null;
}
- boolean appCanShowBubble =
- mConfig.areBubblesAllowed(record.getSbn().getPackageName(), record.getSbn().getUid());
- if (!mConfig.bubblesEnabled() || !appCanShowBubble) {
+ int bubblePreference =
+ mConfig.getBubblePreference(
+ record.getSbn().getPackageName(), record.getSbn().getUid());
+ NotificationChannel recordChannel = record.getChannel();
+
+ if (!mConfig.bubblesEnabled() || bubblePreference == BUBBLE_PREFERENCE_NONE) {
record.setAllowBubble(false);
- } else {
- if (record.getChannel() != null) {
- record.setAllowBubble(record.getChannel().canBubble() && appCanShowBubble);
- } else {
- record.setAllowBubble(appCanShowBubble);
- }
+ } else if (recordChannel == null) {
+ // the app is allowed but there's no channel to check
+ record.setAllowBubble(true);
+ } else if (bubblePreference == BUBBLE_PREFERENCE_ALL) {
+ // by default the channel is not allowed, only don't bubble if the user specified
+ boolean userLockedNoBubbles = !recordChannel.canBubble()
+ && (recordChannel.getUserLockedFields() & USER_LOCKED_ALLOW_BUBBLE) != 0;
+ record.setAllowBubble(!userLockedNoBubbles);
+ } else if (bubblePreference == BUBBLE_PREFERENCE_SELECTED) {
+ record.setAllowBubble(recordChannel.canBubble());
}
- final boolean applyFlag = mBubbleChecker.isNotificationAppropriateToBubble(record)
- && !record.isFlagBubbleRemoved();
+
+ final boolean fulfillsPolicy = record.isConversation()
+ && !mActivityManager.isLowRamDevice()
+ && record.canBubble();
+ final boolean applyFlag = fulfillsPolicy && canPresentAsBubble(record);
if (applyFlag) {
record.getNotification().flags |= FLAG_BUBBLE;
} else {
@@ -95,165 +110,95 @@ public class BubbleExtractor implements NotificationSignalExtractor {
public void setZenHelper(ZenModeHelper helper) {
}
- /**
- * Expected to be called after {@link #setConfig(RankingConfig)} has occurred.
- */
- void setShortcutHelper(ShortcutHelper helper) {
- if (mConfig == null) {
- if (DBG) Slog.d(TAG, "setting shortcut helper prior to setConfig");
- return;
- }
- mBubbleChecker = new BubbleChecker(mContext, helper, mConfig, mActivityManager);
+ public void setShortcutHelper(ShortcutHelper helper) {
+ mShortcutHelper = helper;
}
@VisibleForTesting
- void setBubbleChecker(BubbleChecker checker) {
- mBubbleChecker = checker;
+ public void setActivityManager(ActivityManager manager) {
+ mActivityManager = manager;
}
/**
- * Encapsulates special checks to see if a notification can be flagged as a bubble. This
- * makes testing a bit easier.
+ * @return whether there is valid information for the notification to bubble.
*/
- public static class BubbleChecker {
-
- private ActivityManager mActivityManager;
- private RankingConfig mRankingConfig;
- private Context mContext;
- private ShortcutHelper mShortcutHelper;
-
- BubbleChecker(Context context, ShortcutHelper helper, RankingConfig config,
- ActivityManager activityManager) {
- mContext = context;
- mActivityManager = activityManager;
- mShortcutHelper = helper;
- mRankingConfig = config;
+ @VisibleForTesting
+ boolean canPresentAsBubble(NotificationRecord r) {
+ Notification notification = r.getNotification();
+ Notification.BubbleMetadata metadata = notification.getBubbleMetadata();
+ String pkg = r.getSbn().getPackageName();
+ if (metadata == null) {
+ return false;
}
- /**
- * @return whether the provided notification record is allowed to be represented as a
- * bubble, accounting for user choice & policy.
- */
- public boolean isNotificationAppropriateToBubble(NotificationRecord r) {
- final String pkg = r.getSbn().getPackageName();
- final int userId = r.getSbn().getUser().getIdentifier();
- Notification notification = r.getNotification();
- if (!canBubble(r, pkg, userId)) {
- // no log: canBubble has its own
- return false;
- }
-
- if (mActivityManager.isLowRamDevice()) {
- logBubbleError(r.getKey(), "low ram device");
- return false;
- }
-
- boolean isMessageStyle = Notification.MessagingStyle.class.equals(
- notification.getNotificationStyle());
- if (!isMessageStyle) {
- logBubbleError(r.getKey(), "must be Notification.MessageStyle");
- return false;
- }
+ String shortcutId = metadata.getShortcutId();
+ String notificationShortcutId = r.getShortcutInfo() != null
+ ? r.getShortcutInfo().getId()
+ : null;
+ boolean shortcutValid = false;
+ if (notificationShortcutId != null && shortcutId != null) {
+ // NoMan already checks validity of shortcut, just check if they match.
+ shortcutValid = shortcutId.equals(notificationShortcutId);
+ } else if (shortcutId != null) {
+ shortcutValid =
+ mShortcutHelper.getValidShortcutInfo(shortcutId, pkg, r.getUser()) != null;
+ }
+ if (metadata.getIntent() == null && !shortcutValid) {
+ // Should have a shortcut if intent is null
+ logBubbleError(r.getKey(),
+ "couldn't find valid shortcut for bubble with shortcutId: " + shortcutId);
+ return false;
+ }
+ if (shortcutValid) {
+ // TODO: check the shortcut intent / ensure it can show in activity view
return true;
}
+ return canLaunchInActivityView(mContext, metadata.getIntent(), pkg);
+ }
- /**
- * @return whether the user has enabled the provided notification to bubble, and if the
- * developer has provided valid information for the notification to bubble.
- */
- @VisibleForTesting
- boolean canBubble(NotificationRecord r, String pkg, int userId) {
- Notification notification = r.getNotification();
- Notification.BubbleMetadata metadata = notification.getBubbleMetadata();
- if (metadata == null) {
- // no log: no need to inform dev if they didn't attach bubble metadata
- return false;
- }
- if (!mRankingConfig.bubblesEnabled()) {
- logBubbleError(r.getKey(), "bubbles disabled for user: " + userId);
- return false;
- }
- if (!mRankingConfig.areBubblesAllowed(pkg, userId)) {
- logBubbleError(r.getKey(),
- "bubbles for package: " + pkg + " disabled for user: " + userId);
- return false;
- }
- if (!r.getChannel().canBubble()) {
- logBubbleError(r.getKey(),
- "bubbles for channel " + r.getChannel().getId() + " disabled");
- return false;
- }
-
- String shortcutId = metadata.getShortcutId();
- String notificationShortcutId = r.getShortcutInfo() != null
- ? r.getShortcutInfo().getId()
- : null;
- boolean shortcutValid = false;
- if (notificationShortcutId != null && shortcutId != null) {
- // NoMan already checks validity of shortcut, just check if they match.
- shortcutValid = shortcutId.equals(notificationShortcutId);
- } else if (shortcutId != null) {
- shortcutValid =
- mShortcutHelper.getValidShortcutInfo(shortcutId, pkg, r.getUser()) != null;
- }
- if (metadata.getIntent() == null && !shortcutValid) {
- // Should have a shortcut if intent is null
- logBubbleError(r.getKey(),
- "couldn't find valid shortcut for bubble with shortcutId: " + shortcutId);
- return false;
- }
- if (shortcutValid) {
- return true;
- }
- // no log: canLaunch method has the failure log
- return canLaunchInActivityView(mContext, metadata.getIntent(), pkg);
+ /**
+ * Whether an intent is properly configured to display in an {@link
+ * android.app.ActivityView} for bubbling.
+ *
+ * @param context the context to use.
+ * @param pendingIntent the pending intent of the bubble.
+ * @param packageName the notification package name for this bubble.
+ */
+ // Keep checks in sync with BubbleController#canLaunchInActivityView.
+ @VisibleForTesting
+ protected boolean canLaunchInActivityView(Context context, PendingIntent pendingIntent,
+ String packageName) {
+ if (pendingIntent == null) {
+ Slog.w(TAG, "Unable to create bubble -- no intent");
+ return false;
}
- /**
- * Whether an intent is properly configured to display in an {@link
- * android.app.ActivityView}.
- *
- * @param context the context to use.
- * @param pendingIntent the pending intent of the bubble.
- * @param packageName the notification package name for this bubble.
- */
- // Keep checks in sync with BubbleController#canLaunchInActivityView.
- @VisibleForTesting
- protected boolean canLaunchInActivityView(Context context, PendingIntent pendingIntent,
- String packageName) {
- if (pendingIntent == null) {
- Slog.w(TAG, "Unable to create bubble -- no intent");
- return false;
- }
-
- Intent intent = pendingIntent.getIntent();
-
- ActivityInfo info = intent != null
- ? intent.resolveActivityInfo(context.getPackageManager(), 0)
- : null;
- if (info == null) {
- FrameworkStatsLog.write(FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED,
- packageName,
- BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING);
- Slog.w(TAG, "Unable to send as bubble -- couldn't find activity info for intent: "
- + intent);
- return false;
- }
- if (!ActivityInfo.isResizeableMode(info.resizeMode)) {
- FrameworkStatsLog.write(FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED,
- packageName,
- BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE);
- Slog.w(TAG, "Unable to send as bubble -- activity is not resizable for intent: "
- + intent);
- return false;
- }
- return true;
+ Intent intent = pendingIntent.getIntent();
+ ActivityInfo info = intent != null
+ ? intent.resolveActivityInfo(context.getPackageManager(), 0)
+ : null;
+ if (info == null) {
+ FrameworkStatsLog.write(FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED,
+ packageName,
+ BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING);
+ Slog.w(TAG, "Unable to send as bubble -- couldn't find activity info for intent: "
+ + intent);
+ return false;
}
+ if (!ActivityInfo.isResizeableMode(info.resizeMode)) {
+ FrameworkStatsLog.write(FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED,
+ packageName,
+ BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE);
+ Slog.w(TAG, "Unable to send as bubble -- activity is not resizable for intent: "
+ + intent);
+ return false;
+ }
+ return true;
+ }
- private void logBubbleError(String key, String failureMessage) {
- if (DBG) {
- Slog.w(TAG, "Bubble notification: " + key + " failed: " + failureMessage);
- }
+ private void logBubbleError(String key, String failureMessage) {
+ if (DBG) {
+ Slog.w(TAG, "Bubble notification: " + key + " failed: " + failureMessage);
}
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 2ff8c869e13b0..23a1552522149 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -32,6 +32,7 @@ import static android.app.NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED
import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED;
import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED;
import static android.app.NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED;
+import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
import static android.app.NotificationManager.EXTRA_AUTOMATIC_ZEN_RULE_ID;
import static android.app.NotificationManager.EXTRA_AUTOMATIC_ZEN_RULE_STATUS;
import static android.app.NotificationManager.IMPORTANCE_LOW;
@@ -3079,13 +3080,17 @@ public class NotificationManagerService extends SystemService {
return mPreferencesHelper.getImportance(pkg, uid) != IMPORTANCE_NONE;
}
+ /**
+ * @return true if and only if "all" bubbles are allowed from the provided package.
+ */
@Override
public boolean areBubblesAllowed(String pkg) {
- return areBubblesAllowedForPackage(pkg, Binder.getCallingUid());
+ return getBubblePreferenceForPackage(pkg, Binder.getCallingUid())
+ == BUBBLE_PREFERENCE_ALL;
}
@Override
- public boolean areBubblesAllowedForPackage(String pkg, int uid) {
+ public int getBubblePreferenceForPackage(String pkg, int uid) {
enforceSystemOrSystemUIOrSamePackage(pkg,
"Caller not system or systemui or same package");
@@ -3095,23 +3100,16 @@ public class NotificationManagerService extends SystemService {
"canNotifyAsPackage for uid " + uid);
}
- return mPreferencesHelper.areBubblesAllowed(pkg, uid);
+ return mPreferencesHelper.getBubblePreference(pkg, uid);
}
@Override
- public void setBubblesAllowed(String pkg, int uid, boolean allowed) {
+ public void setBubblesAllowed(String pkg, int uid, int bubblePreference) {
enforceSystemOrSystemUI("Caller not system or systemui");
- mPreferencesHelper.setBubblesAllowed(pkg, uid, allowed);
+ mPreferencesHelper.setBubblesAllowed(pkg, uid, bubblePreference);
handleSavePolicyFile();
}
- @Override
- public boolean hasUserApprovedBubblesForPackage(String pkg, int uid) {
- enforceSystemOrSystemUI("Caller not system or systemui");
- int lockedFields = mPreferencesHelper.getAppLockedFields(pkg, uid);
- return (lockedFields & PreferencesHelper.LockableAppFields.USER_LOCKED_BUBBLE) != 0;
- }
-
@Override
public boolean shouldHideSilentStatusIcons(String callingPkg) {
checkCallerIsSameApp(callingPkg);
@@ -7214,7 +7212,10 @@ public class NotificationManagerService extends SystemService {
boolean interruptiveChanged =
record.canBubble() && (interruptiveBefore != record.isInterruptive());
- changed = indexChanged || interceptChanged || visibilityChanged || interruptiveChanged;
+ changed = indexChanged
+ || interceptChanged
+ || visibilityChanged
+ || interruptiveChanged;
if (interceptBefore && !record.isIntercepted()
&& record.isNewEnoughForAlerting(System.currentTimeMillis())) {
buzzBeepBlinkLocked(record);
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 8154988a4917a..b3d373ffab3ab 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -17,6 +17,7 @@
package com.android.server.notification;
import static android.app.NotificationChannel.PLACEHOLDER_CONVERSATION_ID;
+import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
import static android.app.NotificationManager.IMPORTANCE_NONE;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
@@ -117,10 +118,14 @@ public class PreferencesHelper implements RankingConfig {
@VisibleForTesting
static final boolean DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS = false;
private static final boolean DEFAULT_SHOW_BADGE = true;
- static final boolean DEFAULT_ALLOW_BUBBLE = true;
+
private static final boolean DEFAULT_OEM_LOCKED_IMPORTANCE = false;
private static final boolean DEFAULT_APP_LOCKED_IMPORTANCE = false;
+ static final boolean DEFAULT_GLOBAL_ALLOW_BUBBLE = true;
+ @VisibleForTesting
+ static final int DEFAULT_BUBBLE_PREFERENCE = BUBBLE_PREFERENCE_NONE;
+
/**
* Default value for what fields are user locked. See {@link LockableAppFields} for all lockable
* fields.
@@ -148,7 +153,7 @@ public class PreferencesHelper implements RankingConfig {
private final NotificationChannelLogger mNotificationChannelLogger;
private SparseBooleanArray mBadgingEnabled;
- private boolean mBubblesEnabled = DEFAULT_ALLOW_BUBBLE;
+ private boolean mBubblesEnabledGlobally = DEFAULT_GLOBAL_ALLOW_BUBBLE;
private boolean mAreChannelsBypassingDnd;
private boolean mHideSilentStatusBarIcons = DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS;
@@ -226,8 +231,8 @@ public class PreferencesHelper implements RankingConfig {
parser, ATT_VISIBILITY, DEFAULT_VISIBILITY),
XmlUtils.readBooleanAttribute(
parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE),
- XmlUtils.readBooleanAttribute(
- parser, ATT_ALLOW_BUBBLE, DEFAULT_ALLOW_BUBBLE));
+ XmlUtils.readIntAttribute(
+ parser, ATT_ALLOW_BUBBLE, DEFAULT_BUBBLE_PREFERENCE));
r.importance = XmlUtils.readIntAttribute(
parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
r.priority = XmlUtils.readIntAttribute(
@@ -339,19 +344,19 @@ public class PreferencesHelper implements RankingConfig {
int uid) {
return getOrCreatePackagePreferencesLocked(pkg, UserHandle.getUserId(uid), uid,
DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE,
- DEFAULT_ALLOW_BUBBLE);
+ DEFAULT_BUBBLE_PREFERENCE);
}
private PackagePreferences getOrCreatePackagePreferencesLocked(String pkg,
@UserIdInt int userId, int uid) {
return getOrCreatePackagePreferencesLocked(pkg, userId, uid,
DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE,
- DEFAULT_ALLOW_BUBBLE);
+ DEFAULT_BUBBLE_PREFERENCE);
}
private PackagePreferences getOrCreatePackagePreferencesLocked(String pkg,
@UserIdInt int userId, int uid, int importance, int priority, int visibility,
- boolean showBadge, boolean allowBubble) {
+ boolean showBadge, int bubblePreference) {
final String key = packagePreferencesKey(pkg, uid);
PackagePreferences
r = (uid == UNKNOWN_UID)
@@ -365,7 +370,7 @@ public class PreferencesHelper implements RankingConfig {
r.priority = priority;
r.visibility = visibility;
r.showBadge = showBadge;
- r.allowBubble = allowBubble;
+ r.bubblePreference = bubblePreference;
try {
createDefaultChannelIfNeededLocked(r);
@@ -479,7 +484,7 @@ public class PreferencesHelper implements RankingConfig {
|| r.channels.size() > 0
|| r.groups.size() > 0
|| r.delegate != null
- || r.allowBubble != DEFAULT_ALLOW_BUBBLE;
+ || r.bubblePreference != DEFAULT_BUBBLE_PREFERENCE;
if (hasNonDefaultSettings) {
out.startTag(null, TAG_PACKAGE);
out.attribute(null, ATT_NAME, r.pkg);
@@ -492,8 +497,8 @@ public class PreferencesHelper implements RankingConfig {
if (r.visibility != DEFAULT_VISIBILITY) {
out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility));
}
- if (r.allowBubble != DEFAULT_ALLOW_BUBBLE) {
- out.attribute(null, ATT_ALLOW_BUBBLE, Boolean.toString(r.allowBubble));
+ if (r.bubblePreference != DEFAULT_BUBBLE_PREFERENCE) {
+ out.attribute(null, ATT_ALLOW_BUBBLE, Integer.toString(r.bubblePreference));
}
out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(r.showBadge));
out.attribute(null, ATT_APP_USER_LOCKED_FIELDS,
@@ -544,14 +549,14 @@ public class PreferencesHelper implements RankingConfig {
*
* @param pkg the package to allow or not allow bubbles for.
* @param uid the uid to allow or not allow bubbles for.
- * @param allowed whether bubbles are allowed.
+ * @param bubblePreference whether bubbles are allowed.
*/
- public void setBubblesAllowed(String pkg, int uid, boolean allowed) {
+ public void setBubblesAllowed(String pkg, int uid, int bubblePreference) {
boolean changed = false;
synchronized (mPackagePreferences) {
PackagePreferences p = getOrCreatePackagePreferencesLocked(pkg, uid);
- changed = p.allowBubble != allowed;
- p.allowBubble = allowed;
+ changed = p.bubblePreference != bubblePreference;
+ p.bubblePreference = bubblePreference;
p.lockedAppFields = p.lockedAppFields | LockableAppFields.USER_LOCKED_BUBBLE;
}
if (changed) {
@@ -567,9 +572,9 @@ public class PreferencesHelper implements RankingConfig {
* @return whether bubbles are allowed.
*/
@Override
- public boolean areBubblesAllowed(String pkg, int uid) {
+ public int getBubblePreference(String pkg, int uid) {
synchronized (mPackagePreferences) {
- return getOrCreatePackagePreferencesLocked(pkg, uid).allowBubble;
+ return getOrCreatePackagePreferencesLocked(pkg, uid).bubblePreference;
}
}
@@ -788,6 +793,7 @@ public class PreferencesHelper implements RankingConfig {
}
if (fromTargetApp) {
channel.setLockscreenVisibility(r.visibility);
+ channel.setAllowBubbles(existing != null && existing.canBubble());
}
clearLockedFieldsLocked(channel);
channel.setImportanceLockedByOEM(r.oemLockedImportance);
@@ -2125,7 +2131,7 @@ public class PreferencesHelper implements RankingConfig {
p.groups = new ArrayMap<>();
p.delegate = null;
p.lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
- p.allowBubble = DEFAULT_ALLOW_BUBBLE;
+ p.bubblePreference = DEFAULT_BUBBLE_PREFERENCE;
p.importance = DEFAULT_IMPORTANCE;
p.priority = DEFAULT_PRIORITY;
p.visibility = DEFAULT_VISIBILITY;
@@ -2165,15 +2171,15 @@ public class PreferencesHelper implements RankingConfig {
public void updateBubblesEnabled() {
final boolean newValue = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.NOTIFICATION_BUBBLES,
- DEFAULT_ALLOW_BUBBLE ? 1 : 0) == 1;
- if (newValue != mBubblesEnabled) {
- mBubblesEnabled = newValue;
+ DEFAULT_GLOBAL_ALLOW_BUBBLE ? 1 : 0) == 1;
+ if (newValue != mBubblesEnabledGlobally) {
+ mBubblesEnabledGlobally = newValue;
updateConfig();
}
}
public boolean bubblesEnabled() {
- return mBubblesEnabled;
+ return mBubblesEnabledGlobally;
}
public void updateBadgingEnabled() {
@@ -2229,7 +2235,7 @@ public class PreferencesHelper implements RankingConfig {
int priority = DEFAULT_PRIORITY;
int visibility = DEFAULT_VISIBILITY;
boolean showBadge = DEFAULT_SHOW_BADGE;
- boolean allowBubble = DEFAULT_ALLOW_BUBBLE;
+ int bubblePreference = DEFAULT_BUBBLE_PREFERENCE;
int lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
// these fields are loaded on boot from a different source of truth and so are not
// written to notification policy xml
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index 7e98be7fe065a..7fc79e6a9bf75 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -29,7 +29,7 @@ public interface RankingConfig {
void setShowBadge(String packageName, int uid, boolean showBadge);
boolean canShowBadge(String packageName, int uid);
boolean badgingEnabled(UserHandle userHandle);
- boolean areBubblesAllowed(String packageName, int uid);
+ int getBubblePreference(String packageName, int uid);
boolean bubblesEnabled();
boolean isGroupBlocked(String packageName, int uid, String groupId);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BubbleCheckerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BubbleCheckerTest.java
deleted file mode 100644
index 2578ca8925204..0000000000000
--- a/services/tests/uiservicestests/src/com/android/server/notification/BubbleCheckerTest.java
+++ /dev/null
@@ -1,261 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.notification;
-
-import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
-
-import static junit.framework.Assert.assertTrue;
-
-import static org.junit.Assert.assertFalse;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.app.ActivityManager;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.PendingIntent;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ShortcutInfo;
-import android.os.UserHandle;
-import android.service.notification.StatusBarNotification;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.server.UiServiceTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class BubbleCheckerTest extends UiServiceTestCase {
-
- private static final String SHORTCUT_ID = "shortcut";
- private static final String PKG = "pkg";
- private static final String KEY = "key";
- private static final int USER_ID = 1;
-
- @Mock
- ActivityManager mActivityManager;
- @Mock
- RankingConfig mRankingConfig;
- @Mock
- ShortcutHelper mShortcutHelper;
-
- @Mock
- NotificationRecord mNr;
- @Mock
- UserHandle mUserHandle;
- @Mock
- Notification mNotif;
- @Mock
- StatusBarNotification mSbn;
- @Mock
- NotificationChannel mChannel;
- @Mock
- Notification.BubbleMetadata mBubbleMetadata;
- @Mock
- PendingIntent mPendingIntent;
- @Mock
- Intent mIntent;
-
- BubbleExtractor.BubbleChecker mBubbleChecker;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- when(mNr.getKey()).thenReturn(KEY);
- when(mNr.getSbn()).thenReturn(mSbn);
- when(mNr.getUser()).thenReturn(mUserHandle);
- when(mUserHandle.getIdentifier()).thenReturn(USER_ID);
- when(mNr.getChannel()).thenReturn(mChannel);
- when(mSbn.getPackageName()).thenReturn(PKG);
- when(mSbn.getUser()).thenReturn(mUserHandle);
- when(mNr.getNotification()).thenReturn(mNotif);
- when(mNotif.getBubbleMetadata()).thenReturn(mBubbleMetadata);
-
- mBubbleChecker = new BubbleExtractor.BubbleChecker(mContext,
- mShortcutHelper,
- mRankingConfig,
- mActivityManager);
- }
-
- void setUpIntentBubble() {
- when(mPendingIntent.getIntent()).thenReturn(mIntent);
- when(mBubbleMetadata.getIntent()).thenReturn(mPendingIntent);
- when(mBubbleMetadata.getShortcutId()).thenReturn(null);
- }
-
- void setUpShortcutBubble(boolean isValid) {
- when(mBubbleMetadata.getShortcutId()).thenReturn(SHORTCUT_ID);
- ShortcutInfo info = mock(ShortcutInfo.class);
- when(info.getId()).thenReturn(SHORTCUT_ID);
- when(mShortcutHelper.getValidShortcutInfo(SHORTCUT_ID, PKG, mUserHandle))
- .thenReturn(isValid ? info : null);
- when(mBubbleMetadata.getIntent()).thenReturn(null);
- }
-
- void setUpBubblesEnabled(boolean feature, boolean app, boolean channel) {
- when(mRankingConfig.bubblesEnabled()).thenReturn(feature);
- when(mRankingConfig.areBubblesAllowed(PKG, USER_ID)).thenReturn(app);
- when(mChannel.canBubble()).thenReturn(channel);
- }
-
- void setUpActivityIntent(boolean isResizable) {
- when(mPendingIntent.getIntent()).thenReturn(mIntent);
- ActivityInfo info = new ActivityInfo();
- info.resizeMode = isResizable
- ? RESIZE_MODE_RESIZEABLE
- : RESIZE_MODE_UNRESIZEABLE;
- when(mIntent.resolveActivityInfo(any(), anyInt())).thenReturn(info);
- }
-
- //
- // canBubble
- //
-
- @Test
- public void testCanBubble_true_intentBubble() {
- setUpBubblesEnabled(true /* feature */, true /* app */, true /* channel */);
- setUpIntentBubble();
- setUpActivityIntent(true /* isResizable */);
- when(mActivityManager.isLowRamDevice()).thenReturn(false);
- assertTrue(mBubbleChecker.canBubble(mNr, PKG, USER_ID));
- }
-
- @Test
- public void testCanBubble_true_shortcutBubble() {
- setUpBubblesEnabled(true /* feature */, true /* app */, true /* channel */);
- setUpShortcutBubble(true /* isValid */);
- assertTrue(mBubbleChecker.canBubble(mNr, PKG, USER_ID));
- }
-
- @Test
- public void testCanBubble_false_noIntentInvalidShortcut() {
- setUpBubblesEnabled(true /* feature */, true /* app */, true /* channel */);
- setUpShortcutBubble(false /* isValid */);
- assertFalse(mBubbleChecker.canBubble(mNr, PKG, USER_ID));
- }
-
- @Test
- public void testCanBubble_false_noIntentNoShortcut() {
- setUpBubblesEnabled(true /* feature */, true /* app */, true /* channel */);
- when(mBubbleMetadata.getIntent()).thenReturn(null);
- when(mBubbleMetadata.getShortcutId()).thenReturn(null);
- assertFalse(mBubbleChecker.canBubble(mNr, PKG, USER_ID));
- }
-
- @Test
- public void testCanBubbble_false_noMetadata() {
- setUpBubblesEnabled(true/* feature */, true /* app */, true /* channel */);
- when(mNotif.getBubbleMetadata()).thenReturn(null);
- assertFalse(mBubbleChecker.canBubble(mNr, PKG, USER_ID));
- }
-
- @Test
- public void testCanBubble_false_bubblesNotEnabled() {
- setUpBubblesEnabled(false /* feature */, true /* app */, true /* channel */);
- assertFalse(mBubbleChecker.canBubble(mNr, PKG, USER_ID));
- }
-
- @Test
- public void testCanBubble_false_packageNotAllowed() {
- setUpBubblesEnabled(true /* feature */, false /* app */, true /* channel */);
- assertFalse(mBubbleChecker.canBubble(mNr, PKG, USER_ID));
- }
-
- @Test
- public void testCanBubble_false_channelNotAllowed() {
- setUpBubblesEnabled(true /* feature */, true /* app */, false /* channel */);
- assertFalse(mBubbleChecker.canBubble(mNr, PKG, USER_ID));
- }
-
- //
- // canLaunchInActivityView
- //
-
- @Test
- public void testCanLaunchInActivityView_true() {
- setUpActivityIntent(true /* resizable */);
- assertTrue(mBubbleChecker.canLaunchInActivityView(mContext, mPendingIntent, PKG));
- }
-
- @Test
- public void testCanLaunchInActivityView_false_noIntent() {
- when(mPendingIntent.getIntent()).thenReturn(null);
- assertFalse(mBubbleChecker.canLaunchInActivityView(mContext, mPendingIntent, PKG));
- }
-
- @Test
- public void testCanLaunchInActivityView_false_noInfo() {
- when(mPendingIntent.getIntent()).thenReturn(mIntent);
- when(mIntent.resolveActivityInfo(any(), anyInt())).thenReturn(null);
- assertFalse(mBubbleChecker.canLaunchInActivityView(mContext, mPendingIntent, PKG));
- }
-
- @Test
- public void testCanLaunchInActivityView_false_notResizable() {
- setUpActivityIntent(false /* resizable */);
- assertFalse(mBubbleChecker.canLaunchInActivityView(mContext, mPendingIntent, PKG));
- }
-
- //
- // isNotificationAppropriateToBubble
- //
-
- @Test
- public void testIsNotifAppropriateToBubble_true() {
- setUpBubblesEnabled(true /* feature */, true /* app */, true /* channel */);
- setUpIntentBubble();
- when(mActivityManager.isLowRamDevice()).thenReturn(false);
- setUpActivityIntent(true /* resizable */);
- doReturn(Notification.MessagingStyle.class).when(mNotif).getNotificationStyle();
-
- assertTrue(mBubbleChecker.isNotificationAppropriateToBubble(mNr));
- }
-
- @Test
- public void testIsNotifAppropriateToBubble_false_lowRam() {
- setUpBubblesEnabled(true /* feature */, true /* app */, true /* channel */);
- when(mActivityManager.isLowRamDevice()).thenReturn(true);
- setUpActivityIntent(true /* resizable */);
- doReturn(Notification.MessagingStyle.class).when(mNotif).getNotificationStyle();
-
- assertFalse(mBubbleChecker.isNotificationAppropriateToBubble(mNr));
- }
-
- @Test
- public void testIsNotifAppropriateToBubble_false_notMessageStyle() {
- setUpBubblesEnabled(true /* feature */, true /* app */, true /* channel */);
- when(mActivityManager.isLowRamDevice()).thenReturn(false);
- setUpActivityIntent(true /* resizable */);
- doReturn(Notification.BigPictureStyle.class).when(mNotif).getNotificationStyle();
-
- assertFalse(mBubbleChecker.isNotificationAppropriateToBubble(mNr));
- }
-
-}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java
index 0dbbbaa9cdd69..3c376c9972ac3 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java
@@ -15,12 +15,19 @@
*/
package com.android.server.notification;
-import static android.app.NotificationManager.IMPORTANCE_HIGH;
-import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import static android.app.NotificationChannel.USER_LOCKED_ALLOW_BUBBLE;
+import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
+import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
+import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -28,6 +35,12 @@ import android.app.ActivityManager;
import android.app.Notification;
import android.app.Notification.Builder;
import android.app.NotificationChannel;
+import android.app.PendingIntent;
+import android.app.Person;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ShortcutInfo;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.test.suitebuilder.annotation.SmallTest;
@@ -46,16 +59,32 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class BubbleExtractorTest extends UiServiceTestCase {
- @Mock RankingConfig mConfig;
- @Mock BubbleExtractor.BubbleChecker mBubbleChecker;
+ private static final String SHORTCUT_ID = "shortcut";
+ private static final String PKG = "com.android.server.notification";
+ private static final String TAG = null;
+ private static final int ID = 1001;
+ private static final int UID = 1000;
+ private static final int PID = 2000;
+ UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser());
+
BubbleExtractor mBubbleExtractor;
- private String mPkg = "com.android.server.notification";
- private int mId = 1001;
- private String mTag = null;
- private int mUid = 1000;
- private int mPid = 2000;
- private UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser());
+ @Mock
+ RankingConfig mConfig;
+ @Mock
+ NotificationChannel mChannel;
+ @Mock
+ Notification.BubbleMetadata mBubbleMetadata;
+ @Mock
+ PendingIntent mPendingIntent;
+ @Mock
+ Intent mIntent;
+ @Mock
+ ShortcutInfo mShortcutInfo;
+ @Mock
+ ShortcutHelper mShortcutHelper;
+ @Mock
+ ActivityManager mActivityManager;
@Before
public void setUp() {
@@ -63,58 +92,103 @@ public class BubbleExtractorTest extends UiServiceTestCase {
mBubbleExtractor = new BubbleExtractor();
mBubbleExtractor.initialize(mContext, mock(NotificationUsageStats.class));
mBubbleExtractor.setConfig(mConfig);
- mBubbleExtractor.setShortcutHelper(mock(ShortcutHelper.class));
+ mBubbleExtractor.setShortcutHelper(mShortcutHelper);
+ mBubbleExtractor.setActivityManager(mActivityManager);
+
+ when(mConfig.getNotificationChannel(PKG, UID, "a", false)).thenReturn(mChannel);
+ when(mShortcutInfo.getId()).thenReturn(SHORTCUT_ID);
}
- private NotificationRecord getNotificationRecord(boolean allow, int importanceHigh) {
- NotificationChannel channel = new NotificationChannel("a", "a", importanceHigh);
- channel.setAllowBubbles(allow);
- when(mConfig.getNotificationChannel(mPkg, mUid, "a", false)).thenReturn(channel);
-
+ /* NotificationRecord that fulfills conversation requirements (message style + shortcut) */
+ private NotificationRecord getNotificationRecord(boolean addBubble) {
final Builder builder = new Builder(getContext())
.setContentTitle("foo")
.setSmallIcon(android.R.drawable.sym_def_app_icon)
.setPriority(Notification.PRIORITY_HIGH)
.setDefaults(Notification.DEFAULT_SOUND);
-
+ Person person = new Person.Builder()
+ .setName("bubblebot")
+ .build();
+ builder.setShortcutId(SHORTCUT_ID);
+ builder.setStyle(new Notification.MessagingStyle(person)
+ .setConversationTitle("Bubble Chat")
+ .addMessage("Hello?",
+ SystemClock.currentThreadTimeMillis() - 300000, person)
+ .addMessage("Is it me you're looking for?",
+ SystemClock.currentThreadTimeMillis(), person));
Notification n = builder.build();
- StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, mId, mTag, mUid,
- mPid, n, mUser, null, System.currentTimeMillis());
- NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
+ if (addBubble) {
+ n.setBubbleMetadata(mBubbleMetadata);
+ }
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, ID, TAG, UID,
+ PID, n, mUser, null, System.currentTimeMillis());
+ NotificationRecord r = new NotificationRecord(getContext(), sbn, mChannel);
+ r.setShortcutInfo(mShortcutInfo);
return r;
}
+ void setUpIntentBubble(boolean isValid) {
+ when(mPendingIntent.getIntent()).thenReturn(mIntent);
+ when(mBubbleMetadata.getIntent()).thenReturn(mPendingIntent);
+ when(mBubbleMetadata.getShortcutId()).thenReturn(null);
+
+ when(mPendingIntent.getIntent()).thenReturn(mIntent);
+ ActivityInfo info = new ActivityInfo();
+ info.resizeMode = isValid
+ ? RESIZE_MODE_RESIZEABLE
+ : RESIZE_MODE_UNRESIZEABLE;
+ when(mIntent.resolveActivityInfo(any(), anyInt())).thenReturn(info);
+ }
+
+ void setUpShortcutBubble(boolean isValid) {
+ when(mBubbleMetadata.getShortcutId()).thenReturn(SHORTCUT_ID);
+ when(mBubbleMetadata.getIntent()).thenReturn(null);
+ ShortcutInfo answer = isValid ? mShortcutInfo : null;
+ when(mShortcutHelper.getValidShortcutInfo(SHORTCUT_ID, PKG, mUser)).thenReturn(answer);
+ }
+
+ void setUpBubblesEnabled(boolean feature, int app, boolean channel) {
+ when(mConfig.bubblesEnabled()).thenReturn(feature);
+ when(mConfig.getBubblePreference(anyString(), anyInt())).thenReturn(app);
+ when(mChannel.canBubble()).thenReturn(channel);
+ }
+
//
- // Tests
+ // Tests for the record being allowed to bubble.
//
@Test
public void testAppYesChannelNo() {
- when(mConfig.bubblesEnabled()).thenReturn(true);
- when(mConfig.areBubblesAllowed(mPkg, mUid)).thenReturn(true);
- NotificationRecord r = getNotificationRecord(false, IMPORTANCE_UNSPECIFIED);
-
+ setUpBubblesEnabled(true /* feature */,
+ BUBBLE_PREFERENCE_ALL /* app */,
+ false /* channel */);
+ NotificationRecord r = getNotificationRecord(true /* bubble */);
+ when(mChannel.getUserLockedFields()).thenReturn(USER_LOCKED_ALLOW_BUBBLE);
mBubbleExtractor.process(r);
assertFalse(r.canBubble());
+ assertFalse(r.getNotification().isBubbleNotification());
}
@Test
public void testAppNoChannelYes() throws Exception {
- when(mConfig.bubblesEnabled()).thenReturn(true);
- when(mConfig.areBubblesAllowed(mPkg, mUid)).thenReturn(false);
- NotificationRecord r = getNotificationRecord(true, IMPORTANCE_HIGH);
+ setUpBubblesEnabled(true /* feature */,
+ BUBBLE_PREFERENCE_NONE /* app */,
+ true /* channel */);
+ NotificationRecord r = getNotificationRecord(true /* bubble */);
mBubbleExtractor.process(r);
assertFalse(r.canBubble());
+ assertFalse(r.getNotification().isBubbleNotification());
}
@Test
public void testAppYesChannelYes() {
- when(mConfig.bubblesEnabled()).thenReturn(true);
- when(mConfig.areBubblesAllowed(mPkg, mUid)).thenReturn(true);
- NotificationRecord r = getNotificationRecord(true, IMPORTANCE_UNSPECIFIED);
+ setUpBubblesEnabled(true /* feature */,
+ BUBBLE_PREFERENCE_ALL /* app */,
+ true /* channel */);
+ NotificationRecord r = getNotificationRecord(true /* bubble */);
mBubbleExtractor.process(r);
@@ -123,34 +197,85 @@ public class BubbleExtractorTest extends UiServiceTestCase {
@Test
public void testAppNoChannelNo() {
- when(mConfig.bubblesEnabled()).thenReturn(true);
- when(mConfig.areBubblesAllowed(mPkg, mUid)).thenReturn(false);
- NotificationRecord r = getNotificationRecord(false, IMPORTANCE_UNSPECIFIED);
+ setUpBubblesEnabled(true /* feature */,
+ BUBBLE_PREFERENCE_NONE /* app */,
+ false /* channel */);
+ NotificationRecord r = getNotificationRecord(true /* bubble */);
mBubbleExtractor.process(r);
assertFalse(r.canBubble());
+ assertFalse(r.getNotification().isBubbleNotification());
}
@Test
public void testAppYesChannelYesUserNo() {
- when(mConfig.bubblesEnabled()).thenReturn(false);
- when(mConfig.areBubblesAllowed(mPkg, mUid)).thenReturn(true);
- NotificationRecord r = getNotificationRecord(true, IMPORTANCE_HIGH);
+ setUpBubblesEnabled(false /* feature */,
+ BUBBLE_PREFERENCE_ALL /* app */,
+ true /* channel */);
+ NotificationRecord r = getNotificationRecord(true /* bubble */);
mBubbleExtractor.process(r);
assertFalse(r.canBubble());
+ assertFalse(r.getNotification().isBubbleNotification());
}
@Test
- public void testFlagBubble_true() {
- when(mConfig.bubblesEnabled()).thenReturn(true);
- when(mConfig.areBubblesAllowed(mPkg, mUid)).thenReturn(true);
- NotificationRecord r = getNotificationRecord(true, IMPORTANCE_UNSPECIFIED);
+ public void testAppSelectedChannelNo() {
+ setUpBubblesEnabled(true /* feature */,
+ BUBBLE_PREFERENCE_SELECTED /* app */,
+ false /* channel */);
+ NotificationRecord r = getNotificationRecord(true /* bubble */);
- mBubbleExtractor.setBubbleChecker(mBubbleChecker);
- when(mBubbleChecker.isNotificationAppropriateToBubble(r)).thenReturn(true);
+ mBubbleExtractor.process(r);
+
+ assertFalse(r.canBubble());
+ assertFalse(r.getNotification().isBubbleNotification());
+ }
+
+ @Test
+ public void testAppSeletedChannelYes() {
+ setUpBubblesEnabled(true /* feature */,
+ BUBBLE_PREFERENCE_SELECTED /* app */,
+ true /* channel */);
+ NotificationRecord r = getNotificationRecord(true /* bubble */);
+ when(mChannel.getUserLockedFields()).thenReturn(USER_LOCKED_ALLOW_BUBBLE);
+
+ mBubbleExtractor.process(r);
+
+ assertTrue(r.canBubble());
+ }
+
+ //
+ // Tests for flagging it as a bubble.
+ //
+
+ @Test
+ public void testFlagBubble_false_previouslyRemoved() {
+ setUpBubblesEnabled(true /* feature */,
+ BUBBLE_PREFERENCE_ALL /* app */,
+ true /* channel */);
+ when(mActivityManager.isLowRamDevice()).thenReturn(false);
+
+ NotificationRecord r = getNotificationRecord(true /* bubble */);
+ r.setFlagBubbleRemoved(true);
+
+ mBubbleExtractor.process(r);
+
+ assertTrue(r.canBubble());
+ assertFalse(r.getNotification().isBubbleNotification());
+ }
+
+ @Test
+ public void testFlagBubble_true_shortcutBubble() {
+ setUpBubblesEnabled(true /* feature */,
+ BUBBLE_PREFERENCE_ALL /* app */,
+ true /* channel */);
+ when(mActivityManager.isLowRamDevice()).thenReturn(false);
+ setUpShortcutBubble(true /* isValid */);
+
+ NotificationRecord r = getNotificationRecord(true /* bubble */);
mBubbleExtractor.process(r);
assertTrue(r.canBubble());
@@ -158,14 +283,142 @@ public class BubbleExtractorTest extends UiServiceTestCase {
}
@Test
- public void testFlagBubble_noFlag_previouslyRemoved() {
- when(mConfig.bubblesEnabled()).thenReturn(true);
- when(mConfig.areBubblesAllowed(mPkg, mUid)).thenReturn(true);
- NotificationRecord r = getNotificationRecord(true, IMPORTANCE_UNSPECIFIED);
- r.setFlagBubbleRemoved(true);
+ public void testFlagBubble_true_intentBubble() {
+ setUpBubblesEnabled(true /* feature */,
+ BUBBLE_PREFERENCE_ALL /* app */,
+ true /* channel */);
+ when(mActivityManager.isLowRamDevice()).thenReturn(false);
+ setUpIntentBubble(true /* isValid */);
- mBubbleExtractor.setBubbleChecker(mBubbleChecker);
- when(mBubbleChecker.isNotificationAppropriateToBubble(r)).thenReturn(true);
+ NotificationRecord r = getNotificationRecord(true /* bubble */);
+ mBubbleExtractor.process(r);
+
+ assertTrue(r.canBubble());
+ assertTrue(r.getNotification().isBubbleNotification());
+ }
+
+ @Test
+ public void testFlagBubble_false_noIntentInvalidShortcut() {
+ setUpBubblesEnabled(true /* feature */,
+ BUBBLE_PREFERENCE_ALL /* app */,
+ true /* channel */);
+ when(mActivityManager.isLowRamDevice()).thenReturn(false);
+ setUpShortcutBubble(false /* isValid */);
+
+ NotificationRecord r = getNotificationRecord(true /* bubble */);
+ r.setShortcutInfo(null);
+ mBubbleExtractor.process(r);
+
+ assertTrue(r.canBubble());
+ assertFalse(r.getNotification().isBubbleNotification());
+ }
+
+ @Test
+ public void testFlagBubble_false_invalidIntentNoShortcut() {
+ setUpBubblesEnabled(true /* feature */,
+ BUBBLE_PREFERENCE_ALL /* app */,
+ true /* channel */);
+ when(mActivityManager.isLowRamDevice()).thenReturn(false);
+ setUpIntentBubble(false /* isValid */);
+
+ NotificationRecord r = getNotificationRecord(true /* bubble */);
+ r.setShortcutInfo(null);
+ mBubbleExtractor.process(r);
+
+ assertTrue(r.canBubble());
+ assertFalse(r.getNotification().isBubbleNotification());
+ }
+
+ @Test
+ public void testFlagBubble_false_noIntentNoShortcut() {
+ setUpBubblesEnabled(true /* feature */,
+ BUBBLE_PREFERENCE_ALL /* app */,
+ true /* channel */);
+ when(mActivityManager.isLowRamDevice()).thenReturn(false);
+
+ // Shortcut here is for the notification not the bubble
+ NotificationRecord r = getNotificationRecord(true /* bubble */);
+ mBubbleExtractor.process(r);
+
+ assertTrue(r.canBubble());
+ assertFalse(r.getNotification().isBubbleNotification());
+ }
+
+ @Test
+ public void testFlagBubble_false_noMetadata() {
+ setUpBubblesEnabled(true /* feature */,
+ BUBBLE_PREFERENCE_ALL /* app */,
+ true /* channel */);
+ when(mActivityManager.isLowRamDevice()).thenReturn(false);
+
+ NotificationRecord r = getNotificationRecord(false /* bubble */);
+ mBubbleExtractor.process(r);
+
+ assertTrue(r.canBubble());
+ assertFalse(r.getNotification().isBubbleNotification());
+ }
+
+ @Test
+ public void testFlagBubble_false_notConversation() {
+ setUpBubblesEnabled(true /* feature */,
+ BUBBLE_PREFERENCE_ALL /* app */,
+ true /* channel */);
+ when(mActivityManager.isLowRamDevice()).thenReturn(false);
+ setUpIntentBubble(true /* isValid */);
+
+ NotificationRecord r = getNotificationRecord(true /* bubble */);
+ // No longer a conversation:
+ r.setShortcutInfo(null);
+ r.getNotification().extras.putString(Notification.EXTRA_TEMPLATE, null);
+
+ mBubbleExtractor.process(r);
+
+ assertTrue(r.canBubble());
+ assertFalse(r.getNotification().isBubbleNotification());
+ }
+
+ @Test
+ public void testFlagBubble_false_lowRamDevice() {
+ setUpBubblesEnabled(true /* feature */,
+ BUBBLE_PREFERENCE_ALL /* app */,
+ true /* channel */);
+ when(mActivityManager.isLowRamDevice()).thenReturn(true);
+ setUpIntentBubble(true /* isValid */);
+
+ NotificationRecord r = getNotificationRecord(true /* bubble */);
+ mBubbleExtractor.process(r);
+
+ assertTrue(r.canBubble());
+ assertFalse(r.getNotification().isBubbleNotification());
+ }
+
+ @Test
+ public void testFlagBubble_false_noIntent() {
+ setUpBubblesEnabled(true /* feature */,
+ BUBBLE_PREFERENCE_ALL /* app */,
+ true /* channel */);
+ when(mActivityManager.isLowRamDevice()).thenReturn(true);
+ setUpIntentBubble(true /* isValid */);
+ when(mPendingIntent.getIntent()).thenReturn(null);
+
+ NotificationRecord r = getNotificationRecord(true /* bubble */);
+ mBubbleExtractor.process(r);
+
+ assertTrue(r.canBubble());
+ assertFalse(r.getNotification().isBubbleNotification());
+ }
+
+ @Test
+ public void testFlagBubble_false_noActivityInfo() {
+ setUpBubblesEnabled(true /* feature */,
+ BUBBLE_PREFERENCE_ALL /* app */,
+ true /* channel */);
+ when(mActivityManager.isLowRamDevice()).thenReturn(true);
+ setUpIntentBubble(true /* isValid */);
+ when(mPendingIntent.getIntent()).thenReturn(mIntent);
+ when(mIntent.resolveActivityInfo(any(), anyInt())).thenReturn(null);
+
+ NotificationRecord r = getNotificationRecord(true /* bubble */);
mBubbleExtractor.process(r);
assertTrue(r.canBubble());
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 15220e1ff54ae..e6e84eeab4ed2 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -21,6 +21,10 @@ import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIB
import static android.app.Notification.FLAG_AUTO_CANCEL;
import static android.app.Notification.FLAG_BUBBLE;
import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
+import static android.app.NotificationChannel.USER_LOCKED_ALLOW_BUBBLE;
+import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
+import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
+import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED;
import static android.app.NotificationManager.EXTRA_BLOCKED_STATE;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
@@ -39,6 +43,7 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.PackageManager.FEATURE_WATCH;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -101,6 +106,7 @@ import android.content.ComponentName;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.LauncherApps;
@@ -204,7 +210,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
private TestableNotificationManagerService mService;
private INotificationManager mBinderService;
private NotificationManagerInternal mInternalService;
- private TestableBubbleChecker mTestableBubbleChecker;
private ShortcutHelper mShortcutHelper;
@Mock
private IPackageManager mPackageManager;
@@ -347,21 +352,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
}
- private class TestableBubbleChecker extends BubbleExtractor.BubbleChecker {
-
- TestableBubbleChecker(Context context, ShortcutHelper helper, RankingConfig config,
- ActivityManager manager) {
- super(context, helper, config, manager);
- }
-
- @Override
- protected boolean canLaunchInActivityView(Context context, PendingIntent pendingIntent,
- String packageName) {
- // Tests for this not being true are in CTS NotificationManagerTest
- return true;
- }
- }
-
private class TestableToastCallback extends ITransientNotification.Stub {
@Override
public void show(IBinder windowToken) {
@@ -480,9 +470,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// Set the testable bubble extractor
RankingHelper rankingHelper = mService.getRankingHelper();
BubbleExtractor extractor = rankingHelper.findExtractor(BubbleExtractor.class);
- mTestableBubbleChecker = new TestableBubbleChecker(mContext, mShortcutHelper,
- mService.mPreferencesHelper, mActivityManager);
- extractor.setBubbleChecker(mTestableBubbleChecker);
+ extractor.setActivityManager(mActivityManager);
// Tests call directly into the Binder.
mBinderService = mService.getBinderService();
@@ -544,13 +532,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
private void setUpPrefsForBubbles(String pkg, int uid, boolean globalEnabled,
- boolean pkgEnabled, boolean channelEnabled) {
+ int pkgPref, boolean channelEnabled) {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.NOTIFICATION_BUBBLES, globalEnabled ? 1 : 0);
mService.mPreferencesHelper.updateBubblesEnabled();
assertEquals(globalEnabled, mService.mPreferencesHelper.bubblesEnabled());
try {
- mBinderService.setBubblesAllowed(pkg, uid, pkgEnabled);
+ mBinderService.setBubblesAllowed(pkg, uid, pkgPref);
} catch (RemoteException e) {
e.printStackTrace();
}
@@ -687,19 +675,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
false);
}
- private Notification.BubbleMetadata.Builder getBubbleMetadataBuilder() {
- PendingIntent pi = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
- return new Notification.BubbleMetadata.Builder(pi,
- Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon));
- }
-
private Notification.Builder getMessageStyleNotifBuilder(boolean addBubbleMetadata,
String groupKey, boolean isSummary) {
// Give it a person
Person person = new Person.Builder()
.setName("bubblebot")
.build();
- // It needs remote input to be bubble-able
RemoteInput remoteInput = new RemoteInput.Builder("reply_key").setLabel("reply").build();
PendingIntent inputIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
Icon icon = Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
@@ -724,11 +705,26 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
nb.setGroup(groupKey);
}
if (addBubbleMetadata) {
- nb.setBubbleMetadata(getBubbleMetadataBuilder().build());
+ nb.setBubbleMetadata(getBubbleMetadata());
}
return nb;
}
+ private Notification.BubbleMetadata getBubbleMetadata() {
+ PendingIntent pendingIntent = mock(PendingIntent.class);
+ Intent intent = mock(Intent.class);
+ when(pendingIntent.getIntent()).thenReturn(intent);
+
+ ActivityInfo info = new ActivityInfo();
+ info.resizeMode = RESIZE_MODE_RESIZEABLE;
+ when(intent.resolveActivityInfo(any(), anyInt())).thenReturn(info);
+
+ return new Notification.BubbleMetadata.Builder(
+ pendingIntent,
+ Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon))
+ .build();
+ }
+
private NotificationRecord addGroupWithBubblesAndValidateAdded(boolean summaryAutoCancel)
throws RemoteException {
@@ -4483,24 +4479,31 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testBubble() throws Exception {
- mBinderService.setBubblesAllowed(PKG, mUid, false);
- assertFalse(mBinderService.areBubblesAllowedForPackage(PKG, mUid));
+ mBinderService.setBubblesAllowed(PKG, mUid, BUBBLE_PREFERENCE_NONE);
+ assertFalse(mBinderService.areBubblesAllowed(PKG));
+ assertEquals(mBinderService.getBubblePreferenceForPackage(PKG, mUid),
+ BUBBLE_PREFERENCE_NONE);
}
@Test
- public void testUserApprovedBubblesForPackage() throws Exception {
- assertFalse(mBinderService.hasUserApprovedBubblesForPackage(PKG, mUid));
- mBinderService.setBubblesAllowed(PKG, mUid, true);
- assertTrue(mBinderService.hasUserApprovedBubblesForPackage(PKG, mUid));
- assertTrue(mBinderService.areBubblesAllowedForPackage(PKG, mUid));
+ public void testUserApprovedBubblesForPackageSelected() throws Exception {
+ mBinderService.setBubblesAllowed(PKG, mUid, BUBBLE_PREFERENCE_SELECTED);
+ assertEquals(mBinderService.getBubblePreferenceForPackage(PKG, mUid),
+ BUBBLE_PREFERENCE_SELECTED);
+ }
+
+ @Test
+ public void testUserApprovedBubblesForPackageAll() throws Exception {
+ mBinderService.setBubblesAllowed(PKG, mUid, BUBBLE_PREFERENCE_ALL);
+ assertTrue(mBinderService.areBubblesAllowed(PKG));
+ assertEquals(mBinderService.getBubblePreferenceForPackage(PKG, mUid),
+ BUBBLE_PREFERENCE_ALL);
}
@Test
public void testUserRejectsBubblesForPackage() throws Exception {
- assertFalse(mBinderService.hasUserApprovedBubblesForPackage(PKG, mUid));
- mBinderService.setBubblesAllowed(PKG, mUid, false);
- assertTrue(mBinderService.hasUserApprovedBubblesForPackage(PKG, mUid));
- assertFalse(mBinderService.areBubblesAllowedForPackage(PKG, mUid));
+ mBinderService.setBubblesAllowed(PKG, mUid, BUBBLE_PREFERENCE_NONE);
+ assertFalse(mBinderService.areBubblesAllowed(PKG));
}
@Test
@@ -5166,8 +5169,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testFlagBubble() throws RemoteException {
- // Bubbles are allowed!
- setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, true /* channel */);
+ setUpPrefsForBubbles(PKG, mUid,
+ true /* global */,
+ BUBBLE_PREFERENCE_ALL /* app */,
+ true /* channel */);
NotificationRecord nr =
generateMessageBubbleNotifRecord(mTestNotificationChannel, "testFlagBubble");
@@ -5185,8 +5190,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testFlagBubble_noFlag_appNotAllowed() throws RemoteException {
- // Bubbles are allowed!
- setUpPrefsForBubbles(PKG, mUid, true /* global */, false /* app */, true /* channel */);
+ setUpPrefsForBubbles(PKG, mUid,
+ true /* global */,
+ BUBBLE_PREFERENCE_NONE /* app */,
+ true /* channel */);
NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
"testFlagBubble_noFlag_appNotAllowed");
@@ -5204,15 +5211,17 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testFlagBubbleNotifs_noFlag_whenAppForeground() throws RemoteException {
- // Bubbles are allowed!
- setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, true /* channel */);
+ setUpPrefsForBubbles(PKG, mUid,
+ true /* global */,
+ BUBBLE_PREFERENCE_ALL /* app */,
+ true /* channel */);
// Notif with bubble metadata but not our other misc requirements
Notification.Builder nb = new Notification.Builder(mContext,
mTestNotificationChannel.getId())
.setContentTitle("foo")
.setSmallIcon(android.R.drawable.sym_def_app_icon)
- .setBubbleMetadata(getBubbleMetadataBuilder().build());
+ .setBubbleMetadata(getBubbleMetadata());
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, "tag", mUid, 0,
nb.build(), new UserHandle(mUid), null, 0);
NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
@@ -5232,8 +5241,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testFlagBubbleNotifs_flag_messaging() throws RemoteException {
- // Bubbles are allowed!
- setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, true /* channel */);
+ setUpPrefsForBubbles(PKG, mUid,
+ true /* global */,
+ BUBBLE_PREFERENCE_ALL /* app */,
+ true /* channel */);
NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
"testFlagBubbleNotifs_flag_messaging");
@@ -5249,8 +5260,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testFlagBubbleNotifs_noFlag_messaging_appNotAllowed() throws RemoteException {
- // Bubbles are NOT allowed!
- setUpPrefsForBubbles(PKG, mUid, true /* global */, false /* app */, true /* channel */);
+ setUpPrefsForBubbles(PKG, mUid,
+ true /* global */,
+ BUBBLE_PREFERENCE_NONE /* app */,
+ true /* channel */);
NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
"testFlagBubbleNotifs_noFlag_messaging_appNotAllowed");
@@ -5267,8 +5280,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testFlagBubbleNotifs_noFlag_notBubble() throws RemoteException {
- // Bubbles are allowed!
- setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, true /* channel */);
+ setUpPrefsForBubbles(PKG, mUid,
+ true /* global */,
+ BUBBLE_PREFERENCE_ALL /* app */,
+ true /* channel */);
// Messaging notif WITHOUT bubble metadata
Notification.Builder nb = getMessageStyleNotifBuilder(false /* addBubbleMetadata */,
@@ -5291,11 +5306,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testFlagBubbleNotifs_noFlag_messaging_channelNotAllowed() throws RemoteException {
- // Bubbles are allowed except on this channel
- setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, false /* channel */);
+ setUpPrefsForBubbles(PKG, mUid,
+ true /* global */,
+ BUBBLE_PREFERENCE_ALL /* app */,
+ false /* channel */);
NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
"testFlagBubbleNotifs_noFlag_messaging_channelNotAllowed");
+ nr.getChannel().lockFields(USER_LOCKED_ALLOW_BUBBLE);
// Post the notification
mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
@@ -5488,7 +5506,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testAreBubblesAllowedForPackage_crossUser() throws Exception {
try {
- mBinderService.areBubblesAllowedForPackage(mContext.getPackageName(),
+ mBinderService.getBubblePreferenceForPackage(mContext.getPackageName(),
mUid + UserHandle.PER_USER_RANGE);
fail("Cannot call cross user without permission");
} catch (SecurityException e) {
@@ -5497,7 +5515,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// cross user, with permission, no problem
enableInteractAcrossUsers();
- mBinderService.areBubblesAllowedForPackage(mContext.getPackageName(),
+ mBinderService.getBubblePreferenceForPackage(mContext.getPackageName(),
mUid + UserHandle.PER_USER_RANGE);
}
@@ -5508,8 +5526,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testNotificationBubbleChanged_false() throws Exception {
- // Bubbles are allowed!
- setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, true /* channel */);
+ setUpPrefsForBubbles(PKG, mUid,
+ true /* global */,
+ BUBBLE_PREFERENCE_ALL /* app */,
+ true /* channel */);
// Notif with bubble metadata
NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
@@ -5539,8 +5559,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testNotificationBubbleChanged_true() throws Exception {
- // Bubbles are allowed!
- setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, true /* channel */);
+ setUpPrefsForBubbles(PKG, mUid,
+ true /* global */,
+ BUBBLE_PREFERENCE_ALL /* app */,
+ true /* channel */);
// Notif that is not a bubble
NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
@@ -5576,8 +5598,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testNotificationBubbleChanged_true_notAllowed() throws Exception {
- // Bubbles are allowed!
- setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, true /* channel */);
+ setUpPrefsForBubbles(PKG, mUid,
+ true /* global */,
+ BUBBLE_PREFERENCE_ALL /* app */,
+ true /* channel */);
// Notif that is not a bubble
NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel);
@@ -5605,8 +5629,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testNotificationBubbleIsFlagRemoved_resetOnUpdate() throws Exception {
- // Bubbles are allowed!
- setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, true /* channel */);
+ setUpPrefsForBubbles(PKG, mUid,
+ true /* global */,
+ BUBBLE_PREFERENCE_ALL /* app */,
+ true /* channel */);
+
// Notif with bubble metadata
NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
"testNotificationBubbleIsFlagRemoved_resetOnUpdate");
@@ -5637,8 +5664,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testNotificationBubbleIsFlagRemoved_resetOnBubbleChangedTrue() throws Exception {
- // Bubbles are allowed!
- setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, true /* channel */);
+ setUpPrefsForBubbles(PKG, mUid,
+ true /* global */,
+ BUBBLE_PREFERENCE_ALL /* app */,
+ true /* channel */);
+
// Notif with bubble metadata
NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
"testNotificationBubbleIsFlagRemoved_trueOnBubbleChangedTrue");
@@ -5666,16 +5696,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testOnBubbleNotificationSuppressionChanged() throws Exception {
- // Bubbles are allowed!
- setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, true /* channel */);
+ setUpPrefsForBubbles(PKG, mUid,
+ true /* global */,
+ BUBBLE_PREFERENCE_ALL /* app */,
+ true /* channel */);
// Bubble notification
NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel, "tag");
- // Bubbles are allowed!
- setUpPrefsForBubbles(PKG, nr.getSbn().getUserId(), true /* global */,
- true /* app */, true /* channel */);
-
mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
@@ -5888,8 +5916,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testNotificationBubbles_disabled_lowRamDevice() throws Exception {
- // Bubbles are allowed!
- setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, true /* channel */);
+ setUpPrefsForBubbles(PKG, mUid,
+ true /* global */,
+ BUBBLE_PREFERENCE_ALL /* app */,
+ true /* channel */);
// And we are low ram
when(mActivityManager.isLowRamDevice()).thenReturn(true);
@@ -5972,8 +6002,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testNotificationBubbles_flagAutoExpandForeground_fails_notForeground()
throws Exception {
- // Bubbles are allowed!
- setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, true /* channel */);
+ setUpPrefsForBubbles(PKG, mUid,
+ true /* global */,
+ BUBBLE_PREFERENCE_ALL /* app */,
+ true /* channel */);
NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
"testNotificationBubbles_flagAutoExpandForeground_fails_notForeground");
@@ -6002,8 +6034,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testNotificationBubbles_flagAutoExpandForeground_succeeds_foreground()
throws RemoteException {
- // Bubbles are allowed!
- setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, true /* channel */);
+ setUpPrefsForBubbles(PKG, mUid,
+ true /* global */,
+ BUBBLE_PREFERENCE_ALL /* app */,
+ true /* channel */);
NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
"testNotificationBubbles_flagAutoExpandForeground_succeeds_foreground");
@@ -6032,8 +6066,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testNotificationBubbles_flagRemoved_whenShortcutRemoved()
throws RemoteException {
- // Bubbles are allowed!
- setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, true /* channel */);
+ setUpPrefsForBubbles(PKG, mUid,
+ true /* global */,
+ BUBBLE_PREFERENCE_ALL /* app */,
+ true /* channel */);
ArgumentCaptor