summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Julia Reynolds <juliacr@google.com> 2017-07-11 10:39:58 -0400
committer Julia Reynolds <juliacr@google.com> 2017-07-17 16:55:24 +0000
commiteb3dca71b5df7fdf6299a3e65eb5d6fe8cb7bcbc (patch)
tree762c77f432fa0841730334de9bae5000ddba2a12
parentea9009b4940f9fb99fa3d8f40e76bcdd6848715c (diff)
Send less ranking reconsiderations and updates
- NotificationIntrusivenessExtractor does not need to reconsider ranking for non intrusive notifications - All adjustments (by group helper and the assistant) have been moved to extractors so we can selectively send ranking updates instead of always sending them. Fixes: 62827235 Test: runtest systemui-notification Change-Id: I2ea746c3883049abac0752788a3f4c2fa50c8064
-rw-r--r--core/java/android/service/notification/Adjustment.java15
-rw-r--r--core/res/res/values/config.xml10
-rw-r--r--services/core/java/com/android/server/notification/BadgeExtractor.java8
-rw-r--r--services/core/java/com/android/server/notification/NotificationAdjustmentExtractor.java48
-rw-r--r--services/core/java/com/android/server/notification/NotificationChannelExtractor.java55
-rw-r--r--services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java4
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java171
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecord.java37
-rw-r--r--services/core/java/com/android/server/notification/RankingHandler.java4
-rw-r--r--services/core/java/com/android/server/notification/RankingHelper.java10
-rw-r--r--services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java3
-rw-r--r--services/tests/notification/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java115
-rw-r--r--services/tests/notification/src/com/android/server/notification/NotificationChannelExtractorTest.java76
-rw-r--r--services/tests/notification/src/com/android/server/notification/NotificationIntrusivenessExtractorTest.java67
-rw-r--r--services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java139
-rw-r--r--services/tests/notification/src/com/android/server/notification/RankingHelperTest.java12
16 files changed, 667 insertions, 107 deletions
diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java
index 137cf577ab7c..ce678fc80587 100644
--- a/core/java/android/service/notification/Adjustment.java
+++ b/core/java/android/service/notification/Adjustment.java
@@ -17,7 +17,7 @@ package android.service.notification;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.app.NotificationChannel;
+import android.app.Notification;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -48,6 +48,12 @@ public final class Adjustment implements Parcelable {
* {@link NotificationAssistantService#onNotificationSnoozedUntilContext}.
*/
public static final String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
+ /**
+ * Data type: String. Used to change what {@link Notification#getGroup() group} a notification
+ * belongs to.
+ * @hide
+ */
+ public static final String KEY_GROUP_KEY = "key_group_key";
/**
* Create a notification adjustment.
@@ -146,4 +152,11 @@ public final class Adjustment implements Parcelable {
dest.writeBundle(mSignals);
dest.writeInt(mUser);
}
+
+ @Override
+ public String toString() {
+ return "Adjustment{"
+ + "mSignals=" + mSignals
+ + '}';
+ }
}
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 10ffa5d74df7..966e645c8b88 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2226,11 +2226,19 @@
<string-array name="config_disabledUntilUsedPreinstalledCarrierApps" translatable="false" />
<!-- The list of classes that should be added to the notification ranking pipline.
- See {@link com.android.server.notification.NotificationSignalExtractor} -->
+ See {@link com.android.server.notification.NotificationSignalExtractor}
+ If you add a new extractor to this list make sure to update
+ NotificationManagerService.handleRankingSort()-->
<string-array name="config_notificationSignalExtractors">
+ <!-- many of the following extractors depend on the notification channel, so this
+ extractor must come first -->
+ <item>com.android.server.notification.NotificationChannelExtractor</item>
+ <item>com.android.server.notification.NotificationAdjustmentExtractor</item>
+ <!-- depends on AdjustmentExtractor-->
<item>com.android.server.notification.ValidateNotificationPeople</item>
<item>com.android.server.notification.PriorityExtractor</item>
<item>com.android.server.notification.ImportanceExtractor</item>
+ <!-- depends on ImportanceExtractor-->
<item>com.android.server.notification.NotificationIntrusivenessExtractor</item>
<item>com.android.server.notification.VisibilityExtractor</item>
<item>com.android.server.notification.BadgeExtractor</item>
diff --git a/services/core/java/com/android/server/notification/BadgeExtractor.java b/services/core/java/com/android/server/notification/BadgeExtractor.java
index 1bd2085952f1..184f8b27ff5c 100644
--- a/services/core/java/com/android/server/notification/BadgeExtractor.java
+++ b/services/core/java/com/android/server/notification/BadgeExtractor.java
@@ -47,9 +47,11 @@ public class BadgeExtractor implements NotificationSignalExtractor {
if (!userWantsBadges || !appCanShowBadge) {
record.setShowBadge(false);
} else {
- record.setShowBadge(mConfig.getNotificationChannel(record.sbn.getPackageName(),
- record.sbn.getUid(), record.getChannel().getId(), false).canShowBadge()
- && appCanShowBadge);
+ if (record.getChannel() != null) {
+ record.setShowBadge(record.getChannel().canShowBadge() && appCanShowBadge);
+ } else {
+ record.setShowBadge(appCanShowBadge);
+ }
}
return null;
diff --git a/services/core/java/com/android/server/notification/NotificationAdjustmentExtractor.java b/services/core/java/com/android/server/notification/NotificationAdjustmentExtractor.java
new file mode 100644
index 000000000000..7c8284589ddd
--- /dev/null
+++ b/services/core/java/com/android/server/notification/NotificationAdjustmentExtractor.java
@@ -0,0 +1,48 @@
+/**
+* Copyright (C) 2017 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 android.content.Context;
+import android.util.Slog;
+
+/**
+ * Applies adjustments from the group helper and notification assistant
+ */
+public class NotificationAdjustmentExtractor implements NotificationSignalExtractor {
+ private static final String TAG = "BadgeExtractor";
+ private static final boolean DBG = false;
+
+
+ public void initialize(Context ctx, NotificationUsageStats usageStats) {
+ if (DBG) Slog.d(TAG, "Initializing " + getClass().getSimpleName() + ".");
+ }
+
+ public RankingReconsideration process(NotificationRecord record) {
+ if (record == null || record.getNotification() == null) {
+ if (DBG) Slog.d(TAG, "skipping empty notification");
+ return null;
+ }
+
+ record.applyAdjustments();
+
+ return null;
+ }
+
+ @Override
+ public void setConfig(RankingConfig config) {
+ // config is not used
+ }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
new file mode 100644
index 000000000000..46ab556ffc48
--- /dev/null
+++ b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
@@ -0,0 +1,55 @@
+/**
+* Copyright (C) 2017 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 android.content.Context;
+import android.util.Slog;
+
+/**
+ * Stores the latest notification channel information for this notification
+ */
+public class NotificationChannelExtractor implements NotificationSignalExtractor {
+ private static final String TAG = "BadgeExtractor";
+ private static final boolean DBG = false;
+
+ private RankingConfig mConfig;
+
+ public void initialize(Context ctx, NotificationUsageStats usageStats) {
+ if (DBG) Slog.d(TAG, "Initializing " + getClass().getSimpleName() + ".");
+ }
+
+ public RankingReconsideration process(NotificationRecord record) {
+ if (record == null || record.getNotification() == null) {
+ if (DBG) Slog.d(TAG, "skipping empty notification");
+ return null;
+ }
+
+ if (mConfig == null) {
+ if (DBG) Slog.d(TAG, "missing config");
+ return null;
+ }
+
+ record.updateNotificationChannel(mConfig.getNotificationChannel(record.sbn.getPackageName(),
+ record.sbn.getUid(), record.getChannel().getId(), false));
+
+ return null;
+ }
+
+ @Override
+ public void setConfig(RankingConfig config) {
+ mConfig = config;
+ }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
index 4981d5c5731f..12b29cff5c13 100644
--- a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
@@ -58,6 +58,10 @@ public class NotificationIntrusivenessExtractor implements NotificationSignalExt
}
}
+ if (!record.isRecentlyIntrusive()) {
+ return null;
+ }
+
return new RankingReconsideration(record.getKey(), HANG_TIME_MS) {
@Override
public void work() {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 98a247975959..e7a9e3346ac8 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -278,7 +278,7 @@ public class NotificationManagerService extends SystemService {
private ICompanionDeviceManager mCompanionManager;
final IBinder mForegroundToken = new Binder();
- private Handler mHandler;
+ private WorkerHandler mHandler;
private final HandlerThread mRankingThread = new HandlerThread("ranker",
Process.THREAD_PRIORITY_BACKGROUND);
@@ -295,8 +295,8 @@ public class NotificationManagerService extends SystemService {
private String mVibrateNotificationKey;
private final SparseArray<ArraySet<ManagedServiceInfo>> mListenersDisablingEffects =
- new SparseArray<ArraySet<ManagedServiceInfo>>();
- private List<ComponentName> mEffectsSuppressors = new ArrayList<ComponentName>();
+ new SparseArray<>();
+ private List<ComponentName> mEffectsSuppressors = new ArrayList<>();
private int mListenerHints; // right now, all hints are global
private int mInterruptionFilter = NotificationListenerService.INTERRUPTION_FILTER_UNKNOWN;
@@ -312,16 +312,14 @@ public class NotificationManagerService extends SystemService {
// used as a mutex for access to all active notifications & listeners
final Object mNotificationLock = new Object();
@GuardedBy("mNotificationLock")
- final ArrayList<NotificationRecord> mNotificationList =
- new ArrayList<NotificationRecord>();
+ final ArrayList<NotificationRecord> mNotificationList = new ArrayList<>();
@GuardedBy("mNotificationLock")
- final ArrayMap<String, NotificationRecord> mNotificationsByKey =
- new ArrayMap<String, NotificationRecord>();
+ final ArrayMap<String, NotificationRecord> mNotificationsByKey = new ArrayMap<>();
@GuardedBy("mNotificationLock")
final ArrayList<NotificationRecord> mEnqueuedNotifications = new ArrayList<>();
@GuardedBy("mNotificationLock")
final ArrayMap<Integer, ArrayMap<String, String>> mAutobundledSummaries = new ArrayMap<>();
- final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
+ final ArrayList<ToastRecord> mToastQueue = new ArrayList<>();
final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
// The last key in this list owns the hardware.
@@ -1093,7 +1091,7 @@ public class NotificationManagerService extends SystemService {
count--;
}
if (posted.sbn.isGroup() && posted.getNotification().isGroupSummary()) {
- count --;
+ count--;
}
}
@@ -1101,6 +1099,13 @@ public class NotificationManagerService extends SystemService {
}
}
+ void clearNotifications() {
+ mEnqueuedNotifications.clear();
+ mNotificationList.clear();
+ mNotificationsByKey.clear();
+ mSummaryByGroupKey.clear();
+ }
+
@VisibleForTesting
void addNotification(NotificationRecord r) {
mNotificationList.add(r);
@@ -1121,7 +1126,7 @@ public class NotificationManagerService extends SystemService {
}
@VisibleForTesting
- void setHandler(Handler handler) {
+ void setHandler(WorkerHandler handler) {
mHandler = handler;
}
@@ -1141,6 +1146,11 @@ public class NotificationManagerService extends SystemService {
}
@VisibleForTesting
+ void setRankingHandler(RankingHandler rankingHandler) {
+ mRankingHandler = rankingHandler;
+ }
+
+ @VisibleForTesting
void setIsTelevision(boolean isTelevision) {
mIsTelevision = isTelevision;
}
@@ -1152,7 +1162,8 @@ public class NotificationManagerService extends SystemService {
// TODO: Tests should call onStart instead once the methods above are removed.
@VisibleForTesting
- void init(Looper looper, IPackageManager packageManager, PackageManager packageManagerClient,
+ void init(Looper looper, IPackageManager packageManager,
+ PackageManager packageManagerClient,
LightsManager lightsManager, NotificationListeners notificationListeners,
NotificationAssistants notificationAssistants, ConditionProviders conditionProviders,
ICompanionDeviceManager companionManager, SnoozeHelper snoozeHelper,
@@ -1323,7 +1334,8 @@ public class NotificationManagerService extends SystemService {
final File systemDir = new File(Environment.getDataDirectory(), "system");
- init(Looper.myLooper(), AppGlobals.getPackageManager(), getContext().getPackageManager(),
+ init(Looper.myLooper(),
+ AppGlobals.getPackageManager(), getContext().getPackageManager(),
getLocalService(LightsManager.class),
new NotificationListeners(AppGlobals.getPackageManager()),
new NotificationAssistants(AppGlobals.getPackageManager()),
@@ -1343,7 +1355,6 @@ public class NotificationManagerService extends SystemService {
synchronized (mNotificationLock) {
addAutogroupKeyLocked(key);
}
- mRankingHandler.requestSort(false);
}
@Override
@@ -1351,7 +1362,6 @@ public class NotificationManagerService extends SystemService {
synchronized (mNotificationLock) {
removeAutogroupKeyLocked(key);
}
- mRankingHandler.requestSort(false);
}
@Override
@@ -1424,28 +1434,14 @@ public class NotificationManagerService extends SystemService {
}
mRankingHelper.updateNotificationChannel(pkg, uid, channel);
- final NotificationChannel modifiedChannel =
- mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false);
-
if (!fromListener) {
+ final NotificationChannel modifiedChannel =
+ mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false);
mListeners.notifyNotificationChannelChanged(
pkg, UserHandle.getUserHandleForUid(uid),
modifiedChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
}
- synchronized (mNotificationLock) {
- final int N = mNotificationList.size();
- for (int i = N - 1; i >= 0; --i) {
- NotificationRecord r = mNotificationList.get(i);
- if (r.sbn.getPackageName().equals(pkg)
- && r.sbn.getUid() == uid
- && channel.getId() != null
- && channel.getId().equals(r.getChannel().getId())) {
- r.updateNotificationChannel(modifiedChannel);
- }
- }
- }
- mRankingHandler.requestSort(true);
savePolicyFile();
}
@@ -2883,7 +2879,7 @@ public class NotificationManagerService extends SystemService {
NotificationRecord n = mNotificationsByKey.get(adjustment.getKey());
applyAdjustment(n, adjustment);
}
- mRankingHandler.requestSort(true);
+ mRankingHandler.requestSort();
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -2902,7 +2898,7 @@ public class NotificationManagerService extends SystemService {
applyAdjustment(n, adjustment);
}
}
- mRankingHandler.requestSort(true);
+ mRankingHandler.requestSort();
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -2976,39 +2972,44 @@ public class NotificationManagerService extends SystemService {
}
};
- private void applyAdjustment(NotificationRecord n, Adjustment adjustment) {
- if (n == null) {
+ private void applyAdjustment(NotificationRecord r, Adjustment adjustment) {
+ if (r == null) {
return;
}
if (adjustment.getSignals() != null) {
Bundle.setDefusable(adjustment.getSignals(), true);
- final ArrayList<String> people =
- adjustment.getSignals().getStringArrayList(Adjustment.KEY_PEOPLE);
- final ArrayList<SnoozeCriterion> snoozeCriterionList =
- adjustment.getSignals().getParcelableArrayList(Adjustment.KEY_SNOOZE_CRITERIA);
- n.setPeopleOverride(people);
- n.setSnoozeCriteria(snoozeCriterionList);
+ r.addAdjustment(adjustment);
}
}
@GuardedBy("mNotificationLock")
- private void addAutogroupKeyLocked(String key) {
- NotificationRecord n = mNotificationsByKey.get(key);
- if (n == null) {
+ void addAutogroupKeyLocked(String key) {
+ NotificationRecord r = mNotificationsByKey.get(key);
+ if (r == null) {
return;
}
- n.setOverrideGroupKey(GroupHelper.AUTOGROUP_KEY);
+ addAutoGroupAdjustment(r, GroupHelper.AUTOGROUP_KEY);
EventLogTags.writeNotificationAutogrouped(key);
+ mRankingHandler.requestSort();
}
@GuardedBy("mNotificationLock")
- private void removeAutogroupKeyLocked(String key) {
- NotificationRecord n = mNotificationsByKey.get(key);
- if (n == null) {
+ void removeAutogroupKeyLocked(String key) {
+ NotificationRecord r = mNotificationsByKey.get(key);
+ if (r == null) {
return;
}
- n.setOverrideGroupKey(null);
+ addAutoGroupAdjustment(r, null);
EventLogTags.writeNotificationUnautogrouped(key);
+ mRankingHandler.requestSort();
+ }
+
+ private void addAutoGroupAdjustment(NotificationRecord r, String overrideGroupKey) {
+ Bundle signals = new Bundle();
+ signals.putString(Adjustment.KEY_GROUP_KEY, overrideGroupKey);
+ Adjustment adjustment =
+ new Adjustment(r.sbn.getPackageName(), r.getKey(), signals, "", r.sbn.getUserId());
+ r.addAdjustment(adjustment);
}
// Clears the 'fake' auto-group summary.
@@ -4267,39 +4268,44 @@ public class NotificationManagerService extends SystemService {
}
}
if (changed) {
- scheduleSendRankingUpdate();
+ mHandler.scheduleSendRankingUpdate();
}
}
- private void handleRankingSort(Message msg) {
- if (!(msg.obj instanceof Boolean)) return;
+ void handleRankingSort() {
if (mRankingHelper == null) return;
- boolean forceUpdate = ((Boolean) msg.obj == null) ? false : (boolean) msg.obj;
synchronized (mNotificationLock) {
final int N = mNotificationList.size();
- // Any field that can change via one of the extractors or by the assistant
- // needs to be added here.
- ArrayList<String> orderBefore = new ArrayList<String>(N);
- ArrayList<String> groupOverrideBefore = new ArrayList<>(N);
+ // Any field that can change via one of the extractors needs to be added here.
+ ArrayList<String> orderBefore = new ArrayList<>(N);
int[] visibilities = new int[N];
boolean[] showBadges = new boolean[N];
+ ArrayList<NotificationChannel> channelBefore = new ArrayList<>(N);
+ ArrayList<String> groupKeyBefore = new ArrayList<>(N);
+ ArrayList<ArrayList<String>> overridePeopleBefore = new ArrayList<>(N);
+ ArrayList<ArrayList<SnoozeCriterion>> snoozeCriteriaBefore = new ArrayList<>(N);
for (int i = 0; i < N; i++) {
final NotificationRecord r = mNotificationList.get(i);
orderBefore.add(r.getKey());
- groupOverrideBefore.add(r.sbn.getGroupKey());
visibilities[i] = r.getPackageVisibilityOverride();
showBadges[i] = r.canShowBadge();
+ channelBefore.add(r.getChannel());
+ groupKeyBefore.add(r.getGroupKey());
+ overridePeopleBefore.add(r.getPeopleOverride());
+ snoozeCriteriaBefore.add(r.getSnoozeCriteria());
mRankingHelper.extractSignals(r);
}
mRankingHelper.sort(mNotificationList);
for (int i = 0; i < N; i++) {
final NotificationRecord r = mNotificationList.get(i);
- if (forceUpdate
- || !orderBefore.get(i).equals(r.getKey())
+ if (!orderBefore.get(i).equals(r.getKey())
|| visibilities[i] != r.getPackageVisibilityOverride()
- || !groupOverrideBefore.get(i).equals(r.sbn.getGroupKey())
- || showBadges[i] != r.canShowBadge()) {
- scheduleSendRankingUpdate();
+ || showBadges[i] != r.canShowBadge()
+ || !Objects.equals(channelBefore.get(i), r.getChannel())
+ || !Objects.equals(groupKeyBefore.get(i), r.getGroupKey())
+ || !Objects.equals(overridePeopleBefore.get(i), r.getPeopleOverride())
+ || !Objects.equals(snoozeCriteriaBefore.get(i), r.getSnoozeCriteria())) {
+ mHandler.scheduleSendRankingUpdate();
return;
}
}
@@ -4333,13 +4339,6 @@ public class NotificationManagerService extends SystemService {
return mRankingHelper.indexOf(mNotificationList, target);
}
- private void scheduleSendRankingUpdate() {
- if (!mHandler.hasMessages(MESSAGE_SEND_RANKING_UPDATE)) {
- Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
- mHandler.sendMessage(m);
- }
- }
-
private void handleSendRankingUpdate() {
synchronized (mNotificationLock) {
mListeners.notifyRankingUpdateLocked();
@@ -4371,7 +4370,7 @@ public class NotificationManagerService extends SystemService {
}
}
- private final class WorkerHandler extends Handler
+ protected class WorkerHandler extends Handler
{
public WorkerHandler(Looper looper) {
super(looper);
@@ -4400,6 +4399,13 @@ public class NotificationManagerService extends SystemService {
}
}
+ protected void scheduleSendRankingUpdate() {
+ if (!hasMessages(MESSAGE_SEND_RANKING_UPDATE)) {
+ Message m = Message.obtain(this, MESSAGE_SEND_RANKING_UPDATE);
+ sendMessage(m);
+ }
+ }
+
}
private final class RankingHandlerWorker extends Handler implements RankingHandler
@@ -4415,16 +4421,15 @@ public class NotificationManagerService extends SystemService {
handleRankingReconsideration(msg);
break;
case MESSAGE_RANKING_SORT:
- handleRankingSort(msg);
+ handleRankingSort();
break;
}
}
- public void requestSort(boolean forceUpdate) {
+ public void requestSort() {
removeMessages(MESSAGE_RANKING_SORT);
Message msg = Message.obtain();
msg.what = MESSAGE_RANKING_SORT;
- msg.obj = forceUpdate;
sendMessage(msg);
}
@@ -5741,6 +5746,8 @@ public class NotificationManagerService extends SystemService {
public static final String USAGE = "help\n"
+ "allow_listener COMPONENT\n"
+ "disallow_listener COMPONENT\n"
+ + "set_assistant COMPONENT\n"
+ + "remove_assistant COMPONENT\n"
+ "allow_dnd PACKAGE\n"
+ "disallow_dnd PACKAGE";
@@ -5778,6 +5785,24 @@ public class NotificationManagerService extends SystemService {
getBinderService().setNotificationListenerAccessGranted(cn, false);
}
break;
+ case "allow_assistant": {
+ ComponentName cn = ComponentName.unflattenFromString(getNextArgRequired());
+ if (cn == null) {
+ pw.println("Invalid assistant - must be a ComponentName");
+ return -1;
+ }
+ getBinderService().setNotificationAssistantAccessGranted(cn, true);
+ }
+ break;
+ case "disallow_assistant": {
+ ComponentName cn = ComponentName.unflattenFromString(getNextArgRequired());
+ if (cn == null) {
+ pw.println("Invalid assistant - must be a ComponentName");
+ return -1;
+ }
+ getBinderService().setNotificationAssistantAccessGranted(cn, false);
+ }
+ break;
default:
return handleDefaultCommands(cmd);
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 1dee71cf2a5b..77bf9e30f95d 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -35,8 +35,10 @@ import android.media.AudioSystem;
import android.metrics.LogMaker;
import android.net.Uri;
import android.os.Build;
+import android.os.Bundle;
import android.os.UserHandle;
import android.provider.Settings;
+import android.service.notification.Adjustment;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationRecordProto;
import android.service.notification.SnoozeCriterion;
@@ -57,6 +59,7 @@ import java.io.PrintWriter;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.Objects;
/**
@@ -132,6 +135,8 @@ public final class NotificationRecord {
private String mGroupLogTag;
private String mChannelIdLogTag;
+ private final List<Adjustment> mAdjustments;
+
@VisibleForTesting
public NotificationRecord(Context context, StatusBarNotification sbn,
NotificationChannel channel)
@@ -150,6 +155,7 @@ public final class NotificationRecord {
mAttributes = calculateAttributes();
mImportance = calculateImportance();
mLight = calculateLights();
+ mAdjustments = new ArrayList<>();
}
private boolean isPreChannelsNotification() {
@@ -504,6 +510,7 @@ public final class NotificationRecord {
if (getSnoozeCriteria() != null) {
pw.println(prefix + "snoozeCriteria=" + TextUtils.join(",", getSnoozeCriteria()));
}
+ pw.println(prefix + "mAdjustments=" + mAdjustments);
}
@@ -539,6 +546,36 @@ public final class NotificationRecord {
this.sbn.getNotification());
}
+ public void addAdjustment(Adjustment adjustment) {
+ synchronized (mAdjustments) {
+ mAdjustments.add(adjustment);
+ }
+ }
+
+ public void applyAdjustments() {
+ synchronized (mAdjustments) {
+ for (Adjustment adjustment: mAdjustments) {
+ Bundle signals = adjustment.getSignals();
+ if (signals.containsKey(Adjustment.KEY_PEOPLE)) {
+ final ArrayList<String> people =
+ adjustment.getSignals().getStringArrayList(Adjustment.KEY_PEOPLE);
+ setPeopleOverride(people);
+ }
+ if (signals.containsKey(Adjustment.KEY_SNOOZE_CRITERIA)) {
+ final ArrayList<SnoozeCriterion> snoozeCriterionList =
+ adjustment.getSignals().getParcelableArrayList(
+ Adjustment.KEY_SNOOZE_CRITERIA);
+ setSnoozeCriteria(snoozeCriterionList);
+ }
+ if (signals.containsKey(Adjustment.KEY_GROUP_KEY)) {
+ final String groupOverrideKey =
+ adjustment.getSignals().getString(Adjustment.KEY_GROUP_KEY);
+ setOverrideGroupKey(groupOverrideKey);
+ }
+ }
+ }
+ }
+
public void setContactAffinity(float contactAffinity) {
mContactAffinity = contactAffinity;
if (mImportance < IMPORTANCE_DEFAULT &&
diff --git a/services/core/java/com/android/server/notification/RankingHandler.java b/services/core/java/com/android/server/notification/RankingHandler.java
index 656d727321a8..96324d829573 100644
--- a/services/core/java/com/android/server/notification/RankingHandler.java
+++ b/services/core/java/com/android/server/notification/RankingHandler.java
@@ -16,6 +16,6 @@
package com.android.server.notification;
public interface RankingHandler {
- public void requestSort(boolean forceUpdate);
+ public void requestSort();
public void requestReconsideration(RankingReconsideration recon);
-}
+} \ No newline at end of file
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 108a41de7398..5c2c2b37c5bc 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -399,7 +399,7 @@ public class RankingHelper implements RankingConfig {
for (int i = 0; i < N; i++) {
mSignalExtractors[i].setConfig(this);
}
- mRankingHandler.requestSort(false);
+ mRankingHandler.requestSort();
}
public void sort(ArrayList<NotificationRecord> notificationList) {
@@ -511,7 +511,6 @@ public class RankingHelper implements RankingConfig {
MetricsLogger.action(getChannelGroupLog(group.getId(), pkg));
}
r.groups.put(group.getId(), group);
- updateConfig();
}
@Override
@@ -569,7 +568,6 @@ public class RankingHelper implements RankingConfig {
r.channels.put(channel.getId(), channel);
MetricsLogger.action(getChannelLog(channel, pkg).setType(
MetricsProto.MetricsEvent.TYPE_OPEN));
- updateConfig();
}
void clearLockedFields(NotificationChannel channel) {
@@ -641,7 +639,6 @@ public class RankingHelper implements RankingConfig {
LogMaker lm = getChannelLog(channel, pkg);
lm.setType(MetricsProto.MetricsEvent.TYPE_CLOSE);
MetricsLogger.action(lm);
- updateConfig();
}
}
@@ -655,7 +652,6 @@ public class RankingHelper implements RankingConfig {
return;
}
r.channels.remove(channelId);
- updateConfig();
}
@Override
@@ -672,7 +668,6 @@ public class RankingHelper implements RankingConfig {
r.channels.remove(key);
}
}
- updateConfig();
}
public NotificationChannelGroup getNotificationChannelGroup(String groupId, String pkg,
@@ -735,7 +730,6 @@ public class RankingHelper implements RankingConfig {
deletedChannels.add(nc);
}
}
- updateConfig();
return deletedChannels;
}
@@ -1150,7 +1144,7 @@ public class RankingHelper implements RankingConfig {
changed |= oldValue != newValue;
}
if (changed) {
- mRankingHandler.requestSort(false);
+ mRankingHandler.requestSort();
}
}
diff --git a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 807703b95a63..f7d2a0227e2a 100644
--- a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -76,7 +76,8 @@ public class BuzzBeepBlinkTest extends NotificationTestCase {
@Mock Vibrator mVibrator;
@Mock android.media.IRingtonePlayer mRingtonePlayer;
@Mock Light mLight;
- @Mock Handler mHandler;
+ @Mock
+ NotificationManagerService.WorkerHandler mHandler;
@Mock
NotificationUsageStats mUsageStats;
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java b/services/tests/notification/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java
new file mode 100644
index 000000000000..e52764450ee5
--- /dev/null
+++ b/services/tests/notification/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2017 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.app.NotificationManager.IMPORTANCE_LOW;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.service.notification.Adjustment;
+import android.service.notification.SnoozeCriterion;
+import android.service.notification.StatusBarNotification;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Objects;
+
+public class NotificationAdjustmentExtractorTest extends NotificationTestCase {
+
+ @Test
+ public void testExtractsAdjustment() {
+ NotificationAdjustmentExtractor extractor = new NotificationAdjustmentExtractor();
+
+ NotificationRecord r = generateRecord();
+
+ Bundle signals = new Bundle();
+ signals.putString(Adjustment.KEY_GROUP_KEY, GroupHelper.AUTOGROUP_KEY);
+ ArrayList<SnoozeCriterion> snoozeCriteria = new ArrayList<>();
+ snoozeCriteria.add(new SnoozeCriterion("n", "n", "n"));
+ signals.putParcelableArrayList(Adjustment.KEY_SNOOZE_CRITERIA, snoozeCriteria);
+ ArrayList<String> people = new ArrayList<>();
+ people.add("you");
+ signals.putStringArrayList(Adjustment.KEY_PEOPLE, people);
+ Adjustment adjustment = new Adjustment("pkg", r.getKey(), signals, "", 0);
+ r.addAdjustment(adjustment);
+
+ assertFalse(r.getGroupKey().contains(GroupHelper.AUTOGROUP_KEY));
+ assertFalse(Objects.equals(people, r.getPeopleOverride()));
+ assertFalse(Objects.equals(snoozeCriteria, r.getSnoozeCriteria()));
+
+ assertNull(extractor.process(r));
+
+ assertTrue(r.getGroupKey().contains(GroupHelper.AUTOGROUP_KEY));
+ assertEquals(people, r.getPeopleOverride());
+ assertEquals(snoozeCriteria, r.getSnoozeCriteria());
+ }
+
+ @Test
+ public void testExtractsAdjustments() {
+ NotificationAdjustmentExtractor extractor = new NotificationAdjustmentExtractor();
+
+ NotificationRecord r = generateRecord();
+
+ Bundle pSignals = new Bundle();
+ ArrayList<String> people = new ArrayList<>();
+ people.add("you");
+ pSignals.putStringArrayList(Adjustment.KEY_PEOPLE, people);
+ Adjustment pAdjustment = new Adjustment("pkg", r.getKey(), pSignals, "", 0);
+ r.addAdjustment(pAdjustment);
+
+ Bundle sSignals = new Bundle();
+ ArrayList<SnoozeCriterion> snoozeCriteria = new ArrayList<>();
+ snoozeCriteria.add(new SnoozeCriterion("n", "n", "n"));
+ sSignals.putParcelableArrayList(Adjustment.KEY_SNOOZE_CRITERIA, snoozeCriteria);
+ Adjustment sAdjustment = new Adjustment("pkg", r.getKey(), sSignals, "", 0);
+ r.addAdjustment(sAdjustment);
+
+ Bundle gSignals = new Bundle();
+ gSignals.putString(Adjustment.KEY_GROUP_KEY, GroupHelper.AUTOGROUP_KEY);
+ Adjustment gAdjustment = new Adjustment("pkg", r.getKey(), gSignals, "", 0);
+ r.addAdjustment(gAdjustment);
+
+ assertFalse(r.getGroupKey().contains(GroupHelper.AUTOGROUP_KEY));
+ assertFalse(Objects.equals(people, r.getPeopleOverride()));
+ assertFalse(Objects.equals(snoozeCriteria, r.getSnoozeCriteria()));
+
+ assertNull(extractor.process(r));
+
+ assertTrue(r.getGroupKey().contains(GroupHelper.AUTOGROUP_KEY));
+ assertEquals(people, r.getPeopleOverride());
+ assertEquals(snoozeCriteria, r.getSnoozeCriteria());
+ }
+
+ private NotificationRecord generateRecord() {
+ NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW);
+ final Notification.Builder builder = new Notification.Builder(getContext())
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon);
+ Notification n = builder.build();
+ StatusBarNotification sbn = new StatusBarNotification("", "", 0, "", 0,
+ 0, n, UserHandle.ALL, null, System.currentTimeMillis());
+ return new NotificationRecord(getContext(), sbn, channel);
+ }
+}
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationChannelExtractorTest.java b/services/tests/notification/src/com/android/server/notification/NotificationChannelExtractorTest.java
new file mode 100644
index 000000000000..d75213c3e773
--- /dev/null
+++ b/services/tests/notification/src/com/android/server/notification/NotificationChannelExtractorTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 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.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class NotificationChannelExtractorTest extends NotificationTestCase {
+
+ @Mock RankingConfig mConfig;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testExtractsUpdatedChannel() {
+ NotificationChannelExtractor extractor = new NotificationChannelExtractor();
+ extractor.setConfig(mConfig);
+
+ NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW);
+ final Notification.Builder builder = new Notification.Builder(getContext())
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon);
+ Notification n = builder.build();
+ StatusBarNotification sbn = new StatusBarNotification("", "", 0, "", 0,
+ 0, n, UserHandle.ALL, null, System.currentTimeMillis());
+ NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
+
+ NotificationChannel updatedChannel =
+ new NotificationChannel("a", "", IMPORTANCE_HIGH);
+ when(mConfig.getNotificationChannel(any(), anyInt(), eq("a"), eq(false)))
+ .thenReturn(updatedChannel);
+
+ assertNull(extractor.process(r));
+ assertEquals(updatedChannel, r.getChannel());
+ }
+
+}
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationIntrusivenessExtractorTest.java b/services/tests/notification/src/com/android/server/notification/NotificationIntrusivenessExtractorTest.java
new file mode 100644
index 000000000000..d2f608e63e53
--- /dev/null
+++ b/services/tests/notification/src/com/android/server/notification/NotificationIntrusivenessExtractorTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 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.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+
+import org.junit.Test;
+
+public class NotificationIntrusivenessExtractorTest extends NotificationTestCase {
+
+ @Test
+ public void testNonIntrusive() {
+ NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW);
+ final Notification.Builder builder = new Notification.Builder(getContext())
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon);
+
+ Notification n = builder.build();
+ StatusBarNotification sbn = new StatusBarNotification("", "", 0, "", 0,
+ 0, n, UserHandle.ALL, null, System.currentTimeMillis());
+ NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
+
+ assertNull(new NotificationIntrusivenessExtractor().process(r));
+ }
+
+ @Test
+ public void testIntrusive_fillScreen() {
+ NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT);
+ final Notification.Builder builder = new Notification.Builder(getContext())
+ .setContentTitle("foo")
+ .setFullScreenIntent(PendingIntent.getActivity(
+ getContext(), 0, new Intent(""), 0), true)
+ .setSmallIcon(android.R.drawable.sym_def_app_icon);
+
+ Notification n = builder.build();
+ StatusBarNotification sbn = new StatusBarNotification("", "", 0, "", 0,
+ 0, n, UserHandle.ALL, null, System.currentTimeMillis());
+ NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
+
+ assertNotNull(new NotificationIntrusivenessExtractor().process(r));
+ }
+}
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
index 1a5814b668be..ae2253efdb98 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -20,6 +20,9 @@ import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_NONE;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static com.android.server.notification.NotificationManagerService
+ .MESSAGE_SEND_RANKING_UPDATE;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
@@ -31,9 +34,12 @@ import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -54,6 +60,9 @@ import android.content.pm.ParceledListSlice;
import android.graphics.Color;
import android.media.AudioManager;
import android.os.Binder;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
import android.os.Process;
import android.os.UserHandle;
import android.provider.Settings.Secure;
@@ -63,24 +72,30 @@ import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
+import android.util.ArrayMap;
import android.util.AtomicFile;
+import android.util.Log;
+import android.util.Slog;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
+import com.android.server.lights.Light;
+import com.android.server.lights.LightsManager;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
-import com.android.server.lights.Light;
-import com.android.server.lights.LightsManager;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -109,6 +124,7 @@ public class NotificationManagerServiceTest extends NotificationTestCase {
private AudioManager mAudioManager;
@Mock
ActivityManager mActivityManager;
+ NotificationManagerService.WorkerHandler mHandler;
private NotificationChannel mTestNotificationChannel = new NotificationChannel(
TEST_CHANNEL_ID, TEST_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT);
@@ -152,6 +168,9 @@ public class NotificationManagerServiceTest extends NotificationTestCase {
mNotificationManagerService = new TestableNotificationManagerService(mContext);
+ // Use this testable looper.
+ mTestableLooper = TestableLooper.get(this);
+ mHandler = mNotificationManagerService.new WorkerHandler(mTestableLooper.getLooper());
// MockPackageManager - default returns ApplicationInfo with matching calling UID
final ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.uid = uid;
@@ -162,8 +181,6 @@ public class NotificationManagerServiceTest extends NotificationTestCase {
final LightsManager mockLightsManager = mock(LightsManager.class);
when(mockLightsManager.getLight(anyInt())).thenReturn(mock(Light.class));
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
- // Use this testable looper.
- mTestableLooper = TestableLooper.get(this);
mFile = new File(mContext.getCacheDir(), "test.xml");
mFile.createNewFile();
@@ -174,10 +191,11 @@ public class NotificationManagerServiceTest extends NotificationTestCase {
null, new ComponentName(PKG, "test_class"), uid, true, null, 0);
when(mNotificationListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
try {
- mNotificationManagerService.init(mTestableLooper.getLooper(), mPackageManager,
- mPackageManagerClient, mockLightsManager, mNotificationListeners,
- mNotificationAssistants, mConditionProviders, mCompanionMgr,
- mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager, mGroupHelper);
+ mNotificationManagerService.init(mTestableLooper.getLooper(),
+ mPackageManager, mPackageManagerClient, mockLightsManager,
+ mNotificationListeners, mNotificationAssistants, mConditionProviders,
+ mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager,
+ mGroupHelper);
} catch (SecurityException e) {
if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
throw e;
@@ -234,6 +252,43 @@ public class NotificationManagerServiceTest extends NotificationTestCase {
return new NotificationRecord(mContext, sbn, channel);
}
+ private Map<String, Answer> getSignalExtractorSideEffects() {
+ Map<String, Answer> answers = new ArrayMap<>();
+
+ answers.put("override group key", invocationOnMock -> {
+ ((NotificationRecord) invocationOnMock.getArguments()[0])
+ .setOverrideGroupKey("bananas");
+ return null;
+ });
+ answers.put("override people", invocationOnMock -> {
+ ((NotificationRecord) invocationOnMock.getArguments()[0])
+ .setPeopleOverride(new ArrayList<>());
+ return null;
+ });
+ answers.put("snooze criteria", invocationOnMock -> {
+ ((NotificationRecord) invocationOnMock.getArguments()[0])
+ .setSnoozeCriteria(new ArrayList<>());
+ return null;
+ });
+ answers.put("notification channel", invocationOnMock -> {
+ ((NotificationRecord) invocationOnMock.getArguments()[0])
+ .updateNotificationChannel(new NotificationChannel("a", "", IMPORTANCE_LOW));
+ return null;
+ });
+ answers.put("badging", invocationOnMock -> {
+ NotificationRecord r = (NotificationRecord) invocationOnMock.getArguments()[0];
+ r.setShowBadge(!r.canShowBadge());
+ return null;
+ });
+ answers.put("package visibility", invocationOnMock -> {
+ ((NotificationRecord) invocationOnMock.getArguments()[0]).setPackageVisibilityOverride(
+ Notification.VISIBILITY_SECRET);
+ return null;
+ });
+
+ return answers;
+ }
+
@Test
public void testCreateNotificationChannels_SingleChannel() throws Exception {
final NotificationChannel channel =
@@ -1317,11 +1372,59 @@ public class NotificationManagerServiceTest extends NotificationTestCase {
mNotificationManagerService.addNotification(otherPackage);
// Same notifications are enqueued as posted, everything counts b/c id and tag don't match
- assertEquals(40, mNotificationManagerService.getNotificationCountLocked(PKG, new UserHandle(uid).getIdentifier(), 0, null));
- assertEquals(40, mNotificationManagerService.getNotificationCountLocked(PKG, new UserHandle(uid).getIdentifier(), 0, "tag2"));
- assertEquals(2, mNotificationManagerService.getNotificationCountLocked("a", new UserHandle(uid).getIdentifier(), 0, "banana"));
+ int userId = new UserHandle(uid).getIdentifier();
+ assertEquals(40, mNotificationManagerService.getNotificationCountLocked(PKG, userId, 0, null));
+ assertEquals(40, mNotificationManagerService.getNotificationCountLocked(PKG, userId, 0, "tag2"));
+ assertEquals(2, mNotificationManagerService.getNotificationCountLocked("a", userId, 0, "banana"));
// exclude a known notification - it's excluded from only the posted list, not enqueued
- assertEquals(39, mNotificationManagerService.getNotificationCountLocked(PKG, new UserHandle(uid).getIdentifier(), 0, "tag"));
+ assertEquals(39, mNotificationManagerService.getNotificationCountLocked(PKG, userId, 0, "tag"));
+ }
+
+ @Test
+ public void testModifyAutogroup_requestsSort() throws Exception {
+ RankingHandler rh = mock(RankingHandler.class);
+ mNotificationManagerService.setRankingHandler(rh);
+
+ final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ mNotificationManagerService.addNotification(r);
+ mNotificationManagerService.addAutogroupKeyLocked(r.getKey());
+ mNotificationManagerService.removeAutogroupKeyLocked(r.getKey());
+
+ verify(rh, times(2)).requestSort();
+ }
+
+ @Test
+ public void testHandleRankingSort_sendsUpdateOnSignalExtractorChange() throws Exception {
+ mNotificationManagerService.setRankingHelper(mRankingHelper);
+ NotificationManagerService.WorkerHandler handler = mock(
+ NotificationManagerService.WorkerHandler.class);
+ mNotificationManagerService.setHandler(handler);
+
+ Map<String, Answer> answers = getSignalExtractorSideEffects();
+ for (String message : answers.keySet()) {
+ mNotificationManagerService.clearNotifications();
+ final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ mNotificationManagerService.addNotification(r);
+
+ doAnswer(answers.get(message)).when(mRankingHelper).extractSignals(r);
+
+ mNotificationManagerService.handleRankingSort();
+ }
+ verify(handler, times(answers.size())).scheduleSendRankingUpdate();
+ }
+
+ @Test
+ public void testHandleRankingSort_noUpdateWhenNoSignalChange() throws Exception {
+ mNotificationManagerService.setRankingHelper(mRankingHelper);
+ NotificationManagerService.WorkerHandler handler = mock(
+ NotificationManagerService.WorkerHandler.class);
+ mNotificationManagerService.setHandler(handler);
+
+ final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ mNotificationManagerService.addNotification(r);
+
+ mNotificationManagerService.handleRankingSort();
+ verify(handler, never()).scheduleSendRankingUpdate();
}
}
diff --git a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
index 5a72e6ba665a..801479b33ce5 100644
--- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
@@ -78,6 +78,9 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@SmallTest
@@ -630,6 +633,8 @@ public class RankingHelperTest extends NotificationTestCase {
// all fields should be changed
assertEquals(channel2, mHelper.getNotificationChannel(PKG, UID, channel.getId(), false));
+
+ verify(mHandler, times(1)).requestSort();
}
@Test
@@ -712,6 +717,8 @@ public class RankingHelperTest extends NotificationTestCase {
assertFalse(savedChannel.canBypassDnd());
assertFalse(Notification.VISIBILITY_SECRET == savedChannel.getLockscreenVisibility());
assertEquals(channel.canShowBadge(), savedChannel.canShowBadge());
+
+ verify(mHandler, never()).requestSort();
}
@Test
@@ -1058,6 +1065,8 @@ public class RankingHelperTest extends NotificationTestCase {
// notDeleted
assertEquals(1, mHelper.getNotificationChannelGroups(PKG, UID).size());
+
+ verify(mHandler, never()).requestSort();
}
@Test
@@ -1159,6 +1168,7 @@ public class RankingHelperTest extends NotificationTestCase {
NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
assertEquals(ncg, mHelper.getNotificationChannelGroups(PKG, UID).iterator().next());
+ verify(mHandler, never()).requestSort();
}
@Test
@@ -1275,6 +1285,8 @@ public class RankingHelperTest extends NotificationTestCase {
actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
assertEquals("goodbye", actual.getName());
assertEquals(IMPORTANCE_DEFAULT, actual.getImportance());
+
+ verify(mHandler, times(1)).requestSort();
}
@Test