18f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds/* 28f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds * Copyright (C) 2016 The Android Open Source Project 38f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds * 48f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds * Licensed under the Apache License, Version 2.0 (the "License"); 58f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds * you may not use this file except in compliance with the License. 68f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds * You may obtain a copy of the License at 78f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds * 88f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds * http://www.apache.org/licenses/LICENSE-2.0 98f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds * 108f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds * Unless required by applicable law or agreed to in writing, software 118f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds * distributed under the License is distributed on an "AS IS" BASIS, 128f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 138f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds * See the License for the specific language governing permissions and 148f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds * limitations under the License. 158f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds */ 168f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynoldspackage com.android.server.notification; 178f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds 188f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynoldsimport android.service.notification.StatusBarNotification; 198f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynoldsimport android.util.Log; 208f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynoldsimport android.util.Slog; 218f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds 228f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynoldsimport java.util.ArrayList; 238f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynoldsimport java.util.HashMap; 248f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynoldsimport java.util.LinkedHashSet; 258f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynoldsimport java.util.List; 268f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynoldsimport java.util.Map; 278f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds 288f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds/** 298f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds * NotificationManagerService helper for auto-grouping notifications. 308f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds */ 318f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynoldspublic class GroupHelper { 328f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds private static final String TAG = "GroupHelper"; 338f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 348f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds 358f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds protected static final int AUTOGROUP_AT_COUNT = 4; 368f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds protected static final String AUTOGROUP_KEY = "ranker_group"; 378f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds 388f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds private final Callback mCallback; 398f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds 408f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds // Map of user : <Map of package : notification keys>. Only contains notifications that are not 41a13b3e257418b722282b1c4b5aaef316503bc127Julia Reynolds // grouped by the app (aka no group or sort key). 428f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds Map<Integer, Map<String, LinkedHashSet<String>>> mUngroupedNotifications = new HashMap<>(); 438f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds 448f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds public GroupHelper(Callback callback) {; 458f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds mCallback = callback; 468f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 478f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds 48a13b3e257418b722282b1c4b5aaef316503bc127Julia Reynolds public void onNotificationPosted(StatusBarNotification sbn, boolean autogroupSummaryExists) { 498f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds if (DEBUG) Log.i(TAG, "POSTED " + sbn.getKey()); 508f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds try { 518f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds List<String> notificationsToGroup = new ArrayList<>(); 528f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds if (!sbn.isAppGroup()) { 538f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds // Not grouped by the app, add to the list of notifications for the app; 548f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds // send grouping update if app exceeds the autogrouping limit. 558f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds synchronized (mUngroupedNotifications) { 568f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds Map<String, LinkedHashSet<String>> ungroupedNotificationsByUser 578f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds = mUngroupedNotifications.get(sbn.getUserId()); 588f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds if (ungroupedNotificationsByUser == null) { 598f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds ungroupedNotificationsByUser = new HashMap<>(); 608f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 618f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds mUngroupedNotifications.put(sbn.getUserId(), ungroupedNotificationsByUser); 628f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds LinkedHashSet<String> notificationsForPackage 638f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds = ungroupedNotificationsByUser.get(sbn.getPackageName()); 648f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds if (notificationsForPackage == null) { 658f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds notificationsForPackage = new LinkedHashSet<>(); 668f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 678f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds 688f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds notificationsForPackage.add(sbn.getKey()); 698f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds ungroupedNotificationsByUser.put(sbn.getPackageName(), notificationsForPackage); 708f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds 71a13b3e257418b722282b1c4b5aaef316503bc127Julia Reynolds if (notificationsForPackage.size() >= AUTOGROUP_AT_COUNT 72a13b3e257418b722282b1c4b5aaef316503bc127Julia Reynolds || autogroupSummaryExists) { 738f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds notificationsToGroup.addAll(notificationsForPackage); 748f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 758f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 768f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds if (notificationsToGroup.size() > 0) { 778f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds adjustAutogroupingSummary(sbn.getUserId(), sbn.getPackageName(), 788f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds notificationsToGroup.get(0), true); 798f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds adjustNotificationBundling(notificationsToGroup, true); 808f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 818f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } else { 828f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds // Grouped, but not by us. Send updates to un-autogroup, if we grouped it. 838f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds maybeUngroup(sbn, false, sbn.getUserId()); 848f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 858f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } catch (Exception e) { 868f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds Slog.e(TAG, "Failure processing new notification", e); 878f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 888f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 898f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds 908f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds public void onNotificationRemoved(StatusBarNotification sbn) { 918f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds try { 928f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds maybeUngroup(sbn, true, sbn.getUserId()); 938f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } catch (Exception e) { 948f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds Slog.e(TAG, "Error processing canceled notification", e); 958f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 968f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 978f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds 988f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds /** 99f4af65bbd6ccd9cf6951e26da12c2789ab14c42aJulia Reynolds * Un-autogroups notifications that are now grouped by the app. 1008f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds */ 1018f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds private void maybeUngroup(StatusBarNotification sbn, boolean notificationGone, int userId) { 1028f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds List<String> notificationsToUnAutogroup = new ArrayList<>(); 1038f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds boolean removeSummary = false; 1048f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds synchronized (mUngroupedNotifications) { 105f4af65bbd6ccd9cf6951e26da12c2789ab14c42aJulia Reynolds Map<String, LinkedHashSet<String>> ungroupedNotificationsByUser 1068f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds = mUngroupedNotifications.get(sbn.getUserId()); 107f4af65bbd6ccd9cf6951e26da12c2789ab14c42aJulia Reynolds if (ungroupedNotificationsByUser == null || ungroupedNotificationsByUser.size() == 0) { 1088f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds return; 1098f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 1108f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds LinkedHashSet<String> notificationsForPackage 111f4af65bbd6ccd9cf6951e26da12c2789ab14c42aJulia Reynolds = ungroupedNotificationsByUser.get(sbn.getPackageName()); 1128f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds if (notificationsForPackage == null || notificationsForPackage.size() == 0) { 1138f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds return; 1148f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 1158f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds if (notificationsForPackage.remove(sbn.getKey())) { 1168f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds if (!notificationGone) { 1178f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds // Add the current notification to the ungrouping list if it still exists. 1188f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds notificationsToUnAutogroup.add(sbn.getKey()); 1198f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 120f4af65bbd6ccd9cf6951e26da12c2789ab14c42aJulia Reynolds } 121f4af65bbd6ccd9cf6951e26da12c2789ab14c42aJulia Reynolds // If the status change of this notification has brought the number of loose 122f4af65bbd6ccd9cf6951e26da12c2789ab14c42aJulia Reynolds // notifications to zero, remove the summary and un-autogroup. 123f4af65bbd6ccd9cf6951e26da12c2789ab14c42aJulia Reynolds if (notificationsForPackage.size() == 0) { 124a13b3e257418b722282b1c4b5aaef316503bc127Julia Reynolds ungroupedNotificationsByUser.remove(sbn.getPackageName()); 125f4af65bbd6ccd9cf6951e26da12c2789ab14c42aJulia Reynolds removeSummary = true; 1268f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 1278f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 128f4af65bbd6ccd9cf6951e26da12c2789ab14c42aJulia Reynolds if (removeSummary) { 129f4af65bbd6ccd9cf6951e26da12c2789ab14c42aJulia Reynolds adjustAutogroupingSummary(userId, sbn.getPackageName(), null, false); 130f4af65bbd6ccd9cf6951e26da12c2789ab14c42aJulia Reynolds } 1318f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds if (notificationsToUnAutogroup.size() > 0) { 1328f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds adjustNotificationBundling(notificationsToUnAutogroup, false); 1338f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 1348f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 1358f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds 1368f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds private void adjustAutogroupingSummary(int userId, String packageName, String triggeringKey, 1378f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds boolean summaryNeeded) { 1388f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds if (summaryNeeded) { 1398f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds mCallback.addAutoGroupSummary(userId, packageName, triggeringKey); 1408f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } else { 1418f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds mCallback.removeAutoGroupSummary(userId, packageName); 1428f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 1438f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 1448f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds 1458f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds private void adjustNotificationBundling(List<String> keys, boolean group) { 1468f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds for (String key : keys) { 1478f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds if (DEBUG) Log.i(TAG, "Sending grouping adjustment for: " + key + " group? " + group); 1488f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds if (group) { 1498f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds mCallback.addAutoGroup(key); 1508f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } else { 1518f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds mCallback.removeAutoGroup(key); 1528f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 1538f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 1548f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 1558f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds 1568f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds protected interface Callback { 1578f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds void addAutoGroup(String key); 1588f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds void removeAutoGroup(String key); 1598f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds void addAutoGroupSummary(int userId, String pkg, String triggeringKey); 1608f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds void removeAutoGroupSummary(int user, String pkg); 1618f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 1628f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds} 163