diff options
| author | 2017-07-11 10:39:58 -0400 | |
|---|---|---|
| committer | 2017-07-17 16:55:24 +0000 | |
| commit | eb3dca71b5df7fdf6299a3e65eb5d6fe8cb7bcbc (patch) | |
| tree | 762c77f432fa0841730334de9bae5000ddba2a12 | |
| parent | ea9009b4940f9fb99fa3d8f40e76bcdd6848715c (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
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 |