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 418f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds // groupd 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 488f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds public void onNotificationPosted(StatusBarNotification sbn) { 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 718f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds if (notificationsForPackage.size() >= AUTOGROUP_AT_COUNT) { 728f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds notificationsToGroup.addAll(notificationsForPackage); 738f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 748f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 758f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds if (notificationsToGroup.size() > 0) { 768f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds adjustAutogroupingSummary(sbn.getUserId(), sbn.getPackageName(), 778f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds notificationsToGroup.get(0), true); 788f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds adjustNotificationBundling(notificationsToGroup, true); 798f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 808f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } else { 818f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds // Grouped, but not by us. Send updates to un-autogroup, if we grouped it. 828f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds maybeUngroup(sbn, false, sbn.getUserId()); 838f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 848f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } catch (Exception e) { 858f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds Slog.e(TAG, "Failure processing new notification", e); 868f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 878f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 888f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds 898f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds public void onNotificationRemoved(StatusBarNotification sbn) { 908f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds try { 918f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds maybeUngroup(sbn, true, sbn.getUserId()); 928f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } catch (Exception e) { 938f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds Slog.e(TAG, "Error processing canceled notification", e); 948f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 958f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 968f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds 978f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds /** 98f4af65bbd6ccd9cf6951e26da12c2789ab14c42aJulia Reynolds * Un-autogroups notifications that are now grouped by the app. 998f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds */ 1008f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds private void maybeUngroup(StatusBarNotification sbn, boolean notificationGone, int userId) { 1018f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds List<String> notificationsToUnAutogroup = new ArrayList<>(); 1028f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds boolean removeSummary = false; 1038f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds synchronized (mUngroupedNotifications) { 104f4af65bbd6ccd9cf6951e26da12c2789ab14c42aJulia Reynolds Map<String, LinkedHashSet<String>> ungroupedNotificationsByUser 1058f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds = mUngroupedNotifications.get(sbn.getUserId()); 106f4af65bbd6ccd9cf6951e26da12c2789ab14c42aJulia Reynolds if (ungroupedNotificationsByUser == null || ungroupedNotificationsByUser.size() == 0) { 1078f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds return; 1088f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 1098f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds LinkedHashSet<String> notificationsForPackage 110f4af65bbd6ccd9cf6951e26da12c2789ab14c42aJulia Reynolds = ungroupedNotificationsByUser.get(sbn.getPackageName()); 1118f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds if (notificationsForPackage == null || notificationsForPackage.size() == 0) { 1128f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds return; 1138f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 1148f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds if (notificationsForPackage.remove(sbn.getKey())) { 1158f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds if (!notificationGone) { 1168f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds // Add the current notification to the ungrouping list if it still exists. 1178f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds notificationsToUnAutogroup.add(sbn.getKey()); 1188f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 119f4af65bbd6ccd9cf6951e26da12c2789ab14c42aJulia Reynolds } 120f4af65bbd6ccd9cf6951e26da12c2789ab14c42aJulia Reynolds // If the status change of this notification has brought the number of loose 121f4af65bbd6ccd9cf6951e26da12c2789ab14c42aJulia Reynolds // notifications to zero, remove the summary and un-autogroup. 122f4af65bbd6ccd9cf6951e26da12c2789ab14c42aJulia Reynolds if (notificationsForPackage.size() == 0) { 123f4af65bbd6ccd9cf6951e26da12c2789ab14c42aJulia Reynolds removeSummary = true; 1248f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 1258f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 126f4af65bbd6ccd9cf6951e26da12c2789ab14c42aJulia Reynolds if (removeSummary) { 127f4af65bbd6ccd9cf6951e26da12c2789ab14c42aJulia Reynolds adjustAutogroupingSummary(userId, sbn.getPackageName(), null, false); 128f4af65bbd6ccd9cf6951e26da12c2789ab14c42aJulia Reynolds } 1298f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds if (notificationsToUnAutogroup.size() > 0) { 1308f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds adjustNotificationBundling(notificationsToUnAutogroup, false); 1318f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 1328f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 1338f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds 1348f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds private void adjustAutogroupingSummary(int userId, String packageName, String triggeringKey, 1358f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds boolean summaryNeeded) { 1368f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds if (summaryNeeded) { 1378f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds mCallback.addAutoGroupSummary(userId, packageName, triggeringKey); 1388f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } else { 1398f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds mCallback.removeAutoGroupSummary(userId, packageName); 1408f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 1418f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 1428f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds 1438f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds private void adjustNotificationBundling(List<String> keys, boolean group) { 1448f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds for (String key : keys) { 1458f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds if (DEBUG) Log.i(TAG, "Sending grouping adjustment for: " + key + " group? " + group); 1468f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds if (group) { 1478f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds mCallback.addAutoGroup(key); 1488f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } else { 1498f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds mCallback.removeAutoGroup(key); 1508f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 1518f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 1528f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 1538f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds 1548f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds protected interface Callback { 1558f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds void addAutoGroup(String key); 1568f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds void removeAutoGroup(String key); 1578f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds void addAutoGroupSummary(int userId, String pkg, String triggeringKey); 1588f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds void removeAutoGroupSummary(int user, String pkg); 1598f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds } 1608f488d3f2f754bc6e8f4e87f40031a3f6a0b00dbJulia Reynolds} 161