• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.settings.notification;
17 
18 import static android.app.NotificationManager.IMPORTANCE_NONE;
19 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
20 import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED;
21 import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
22 import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER;
23 
24 import android.app.INotificationManager;
25 import android.app.NotificationChannel;
26 import android.app.NotificationChannelGroup;
27 import android.app.NotificationHistory;
28 import android.app.NotificationManager;
29 import android.app.usage.IUsageStatsManager;
30 import android.app.usage.UsageEvents;
31 import android.companion.ICompanionDeviceManager;
32 import android.content.ComponentName;
33 import android.content.Context;
34 import android.content.Intent;
35 import android.content.pm.ApplicationInfo;
36 import android.content.pm.LauncherApps;
37 import android.content.pm.PackageInfo;
38 import android.content.pm.PackageManager;
39 import android.content.pm.ParceledListSlice;
40 import android.content.pm.ShortcutInfo;
41 import android.content.pm.ShortcutManager;
42 import android.graphics.drawable.Drawable;
43 import android.os.Build;
44 import android.os.RemoteException;
45 import android.os.ServiceManager;
46 import android.os.UserHandle;
47 import android.service.notification.ConversationChannelWrapper;
48 import android.service.notification.NotificationListenerFilter;
49 import android.text.format.DateUtils;
50 import android.util.IconDrawableFactory;
51 import android.util.Log;
52 
53 import androidx.annotation.VisibleForTesting;
54 
55 import com.android.internal.util.CollectionUtils;
56 import com.android.settings.R;
57 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
58 import com.android.settingslib.bluetooth.LocalBluetoothManager;
59 import com.android.settingslib.notification.ConversationIconFactory;
60 import com.android.settingslib.utils.StringUtil;
61 
62 import java.util.ArrayList;
63 import java.util.Arrays;
64 import java.util.Collection;
65 import java.util.HashMap;
66 import java.util.List;
67 import java.util.Map;
68 import java.util.Objects;
69 
70 public class NotificationBackend {
71     private static final String TAG = "NotificationBackend";
72 
73     static IUsageStatsManager sUsageStatsManager = IUsageStatsManager.Stub.asInterface(
74             ServiceManager.getService(Context.USAGE_STATS_SERVICE));
75     private static final int DAYS_TO_CHECK = 7;
76     static INotificationManager sINM = INotificationManager.Stub.asInterface(
77             ServiceManager.getService(Context.NOTIFICATION_SERVICE));
78 
loadAppRow(Context context, PackageManager pm, ApplicationInfo app)79     public AppRow loadAppRow(Context context, PackageManager pm, ApplicationInfo app) {
80         final AppRow row = new AppRow();
81         row.pkg = app.packageName;
82         row.uid = app.uid;
83         try {
84             row.label = app.loadLabel(pm);
85         } catch (Throwable t) {
86             Log.e(TAG, "Error loading application label for " + row.pkg, t);
87             row.label = row.pkg;
88         }
89         row.icon = IconDrawableFactory.newInstance(context).getBadgedIcon(app);
90         row.banned = getNotificationsBanned(row.pkg, row.uid);
91         row.showBadge = canShowBadge(row.pkg, row.uid);
92         row.bubblePreference = getBubblePreference(row.pkg, row.uid);
93         row.userId = UserHandle.getUserId(row.uid);
94         row.blockedChannelCount = getBlockedChannelCount(row.pkg, row.uid);
95         row.channelCount = getChannelCount(row.pkg, row.uid);
96         recordAggregatedUsageEvents(context, row);
97         return row;
98     }
99 
loadAppRow(Context context, PackageManager pm, PackageInfo app)100     public AppRow loadAppRow(Context context, PackageManager pm, PackageInfo app) {
101         final AppRow row = loadAppRow(context, pm, app.applicationInfo);
102         recordCanBeBlocked(app, row);
103         return row;
104     }
105 
recordCanBeBlocked(PackageInfo app, AppRow row)106     void recordCanBeBlocked(PackageInfo app, AppRow row) {
107         try {
108             row.systemApp = row.lockedImportance =
109                     sINM.isImportanceLocked(app.packageName, app.applicationInfo.uid);
110         } catch (RemoteException e) {
111             Log.w(TAG, "Error calling NMS", e);
112         }
113 
114         // if the app targets T but has not requested the permission, we cannot change the
115         // permission state
116         if (app.applicationInfo.targetSdkVersion > Build.VERSION_CODES.S_V2) {
117             if (app.requestedPermissions == null || Arrays.stream(app.requestedPermissions)
118                     .noneMatch(p -> p.equals(android.Manifest.permission.POST_NOTIFICATIONS))) {
119                 row.lockedImportance = true;
120                 row.permissionStateLocked = true;
121             }
122         }
123     }
124 
getDeviceList(ICompanionDeviceManager cdm, LocalBluetoothManager lbm, String pkg, int userId)125     static public CharSequence getDeviceList(ICompanionDeviceManager cdm, LocalBluetoothManager lbm,
126             String pkg, int userId) {
127         if (cdm == null) {
128             return "";
129         }
130         boolean multiple = false;
131         StringBuilder sb = new StringBuilder();
132 
133         try {
134             List<String> associatedMacAddrs = CollectionUtils.mapNotNull(
135                     cdm.getAssociations(pkg, userId),
136                     a -> a.isSelfManaged() ? null : a.getDeviceMacAddress().toString());
137             if (associatedMacAddrs != null) {
138                 for (String assocMac : associatedMacAddrs) {
139                     final Collection<CachedBluetoothDevice> cachedDevices =
140                             lbm.getCachedDeviceManager().getCachedDevicesCopy();
141                     for (CachedBluetoothDevice cachedBluetoothDevice : cachedDevices) {
142                         if (Objects.equals(assocMac, cachedBluetoothDevice.getAddress())) {
143                             if (multiple) {
144                                 sb.append(", ");
145                             } else {
146                                 multiple = true;
147                             }
148                             sb.append(cachedBluetoothDevice.getName());
149                         }
150                     }
151                 }
152             }
153         } catch (RemoteException e) {
154             Log.w(TAG, "Error calling CDM", e);
155         }
156         return sb.toString();
157     }
158 
enableSwitch(Context context, ApplicationInfo app)159     public boolean enableSwitch(Context context, ApplicationInfo app) {
160         try {
161             PackageInfo info = context.getPackageManager().getPackageInfo(
162                     app.packageName, PackageManager.GET_PERMISSIONS);
163             final AppRow row = new AppRow();
164             recordCanBeBlocked(info, row);
165             boolean systemBlockable = !row.systemApp || (row.systemApp && row.banned);
166             return systemBlockable && !row.lockedImportance;
167         } catch (PackageManager.NameNotFoundException e) {
168             e.printStackTrace();
169         }
170         return false;
171     }
172 
getNotificationsBanned(String pkg, int uid)173     public boolean getNotificationsBanned(String pkg, int uid) {
174         try {
175             final boolean enabled = sINM.areNotificationsEnabledForPackage(pkg, uid);
176             return !enabled;
177         } catch (Exception e) {
178             Log.w(TAG, "Error calling NoMan", e);
179             return false;
180         }
181     }
182 
setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled)183     public boolean setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
184         try {
185             if (onlyHasDefaultChannel(pkg, uid)) {
186                 NotificationChannel defaultChannel =
187                         getChannel(pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID, null);
188                 defaultChannel.setImportance(enabled ? IMPORTANCE_UNSPECIFIED : IMPORTANCE_NONE);
189                 updateChannel(pkg, uid, defaultChannel);
190             }
191             sINM.setNotificationsEnabledForPackage(pkg, uid, enabled);
192             return true;
193         } catch (Exception e) {
194             Log.w(TAG, "Error calling NoMan", e);
195             return false;
196         }
197     }
198 
canShowBadge(String pkg, int uid)199     public boolean canShowBadge(String pkg, int uid) {
200         try {
201             return sINM.canShowBadge(pkg, uid);
202         } catch (Exception e) {
203             Log.w(TAG, "Error calling NoMan", e);
204             return false;
205         }
206     }
207 
setShowBadge(String pkg, int uid, boolean showBadge)208     public boolean setShowBadge(String pkg, int uid, boolean showBadge) {
209         try {
210             sINM.setShowBadge(pkg, uid, showBadge);
211             return true;
212         } catch (Exception e) {
213             Log.w(TAG, "Error calling NoMan", e);
214             return false;
215         }
216     }
217 
getBubblePreference(String pkg, int uid)218     public int getBubblePreference(String pkg, int uid) {
219         try {
220             return sINM.getBubblePreferenceForPackage(pkg, uid);
221         } catch (Exception e) {
222             Log.w(TAG, "Error calling NoMan", e);
223             return -1;
224         }
225     }
226 
setAllowBubbles(String pkg, int uid, int preference)227     public boolean setAllowBubbles(String pkg, int uid, int preference) {
228         try {
229             sINM.setBubblesAllowed(pkg, uid, preference);
230             return true;
231         } catch (Exception e) {
232             Log.w(TAG, "Error calling NoMan", e);
233             return false;
234         }
235     }
236 
getChannel(String pkg, int uid, String channelId)237     public NotificationChannel getChannel(String pkg, int uid, String channelId) {
238         return getChannel(pkg, uid, channelId, null);
239     }
240 
getChannel(String pkg, int uid, String channelId, String conversationId)241     public NotificationChannel getChannel(String pkg, int uid, String channelId,
242             String conversationId) {
243         if (channelId == null) {
244             return null;
245         }
246         try {
247             return sINM.getNotificationChannelForPackage(pkg, uid, channelId, conversationId, true);
248         } catch (Exception e) {
249             Log.w(TAG, "Error calling NoMan", e);
250             return null;
251         }
252     }
253 
getGroup(String pkg, int uid, String groupId)254     public NotificationChannelGroup getGroup(String pkg, int uid, String groupId) {
255         if (groupId == null) {
256             return null;
257         }
258         try {
259             return sINM.getNotificationChannelGroupForPackage(groupId, pkg, uid);
260         } catch (Exception e) {
261             Log.w(TAG, "Error calling NoMan", e);
262             return null;
263         }
264     }
265 
getGroups(String pkg, int uid)266     public ParceledListSlice<NotificationChannelGroup> getGroups(String pkg, int uid) {
267         try {
268             return sINM.getNotificationChannelGroupsForPackage(pkg, uid, false);
269         } catch (Exception e) {
270             Log.w(TAG, "Error calling NoMan", e);
271             return ParceledListSlice.emptyList();
272         }
273     }
274 
getConversations(String pkg, int uid)275     public ParceledListSlice<ConversationChannelWrapper> getConversations(String pkg, int uid) {
276         try {
277             return sINM.getConversationsForPackage(pkg, uid);
278         } catch (Exception e) {
279             Log.w(TAG, "Error calling NoMan", e);
280             return ParceledListSlice.emptyList();
281         }
282     }
283 
getConversations(boolean onlyImportant)284     public ParceledListSlice<ConversationChannelWrapper> getConversations(boolean onlyImportant) {
285         try {
286             return sINM.getConversations(onlyImportant);
287         } catch (Exception e) {
288             Log.w(TAG, "Error calling NoMan", e);
289             return ParceledListSlice.emptyList();
290         }
291     }
292 
hasSentValidMsg(String pkg, int uid)293     public boolean hasSentValidMsg(String pkg, int uid) {
294         try {
295             return sINM.hasSentValidMsg(pkg, uid);
296         } catch (Exception e) {
297             Log.w(TAG, "Error calling NoMan", e);
298             return false;
299         }
300     }
301 
isInInvalidMsgState(String pkg, int uid)302     public boolean isInInvalidMsgState(String pkg, int uid) {
303         try {
304             return sINM.isInInvalidMsgState(pkg, uid);
305         } catch (Exception e) {
306             Log.w(TAG, "Error calling NoMan", e);
307             return false;
308         }
309     }
310 
hasUserDemotedInvalidMsgApp(String pkg, int uid)311     public boolean hasUserDemotedInvalidMsgApp(String pkg, int uid) {
312         try {
313             return sINM.hasUserDemotedInvalidMsgApp(pkg, uid);
314         } catch (Exception e) {
315             Log.w(TAG, "Error calling NoMan", e);
316             return false;
317         }
318     }
319 
setInvalidMsgAppDemoted(String pkg, int uid, boolean isDemoted)320     public void setInvalidMsgAppDemoted(String pkg, int uid, boolean isDemoted) {
321         try {
322              sINM.setInvalidMsgAppDemoted(pkg, uid, isDemoted);
323         } catch (Exception e) {
324             Log.w(TAG, "Error calling NoMan", e);
325         }
326     }
327 
hasSentValidBubble(String pkg, int uid)328     public boolean hasSentValidBubble(String pkg, int uid) {
329         try {
330             return sINM.hasSentValidBubble(pkg, uid);
331         } catch (Exception e) {
332             Log.w(TAG, "Error calling NoMan", e);
333             return false;
334         }
335     }
336 
337     /**
338      * Returns all notification channels associated with the package and uid that will bypass DND
339      */
getNotificationChannelsBypassingDnd(String pkg, int uid)340     public ParceledListSlice<NotificationChannel> getNotificationChannelsBypassingDnd(String pkg,
341             int uid) {
342         try {
343             return sINM.getNotificationChannelsBypassingDnd(pkg, uid);
344         } catch (Exception e) {
345             Log.w(TAG, "Error calling NoMan", e);
346             return ParceledListSlice.emptyList();
347         }
348     }
349 
updateChannel(String pkg, int uid, NotificationChannel channel)350     public void updateChannel(String pkg, int uid, NotificationChannel channel) {
351         try {
352             sINM.updateNotificationChannelForPackage(pkg, uid, channel);
353         } catch (Exception e) {
354             Log.w(TAG, "Error calling NoMan", e);
355         }
356     }
357 
updateChannelGroup(String pkg, int uid, NotificationChannelGroup group)358     public void updateChannelGroup(String pkg, int uid, NotificationChannelGroup group) {
359         try {
360             sINM.updateNotificationChannelGroupForPackage(pkg, uid, group);
361         } catch (Exception e) {
362             Log.w(TAG, "Error calling NoMan", e);
363         }
364     }
365 
getDeletedChannelCount(String pkg, int uid)366     public int getDeletedChannelCount(String pkg, int uid) {
367         try {
368             return sINM.getDeletedChannelCount(pkg, uid);
369         } catch (Exception e) {
370             Log.w(TAG, "Error calling NoMan", e);
371             return 0;
372         }
373     }
374 
getBlockedChannelCount(String pkg, int uid)375     public int getBlockedChannelCount(String pkg, int uid) {
376         try {
377             return sINM.getBlockedChannelCount(pkg, uid);
378         } catch (Exception e) {
379             Log.w(TAG, "Error calling NoMan", e);
380             return 0;
381         }
382     }
383 
onlyHasDefaultChannel(String pkg, int uid)384     public boolean onlyHasDefaultChannel(String pkg, int uid) {
385         try {
386             return sINM.onlyHasDefaultChannel(pkg, uid);
387         } catch (Exception e) {
388             Log.w(TAG, "Error calling NoMan", e);
389             return false;
390         }
391     }
392 
getChannelCount(String pkg, int uid)393     public int getChannelCount(String pkg, int uid) {
394         try {
395             return sINM.getNumNotificationChannelsForPackage(pkg, uid, false);
396         } catch (Exception e) {
397             Log.w(TAG, "Error calling NoMan", e);
398             return 0;
399         }
400     }
401 
shouldHideSilentStatusBarIcons(Context context)402     public boolean shouldHideSilentStatusBarIcons(Context context) {
403         try {
404             return sINM.shouldHideSilentStatusIcons(context.getPackageName());
405         } catch (Exception e) {
406             Log.w(TAG, "Error calling NoMan", e);
407             return false;
408         }
409     }
410 
setHideSilentStatusIcons(boolean hide)411     public void setHideSilentStatusIcons(boolean hide) {
412         try {
413             sINM.setHideSilentStatusIcons(hide);
414         } catch (Exception e) {
415             Log.w(TAG, "Error calling NoMan", e);
416         }
417     }
418 
getAssistantAdjustments(String pkg)419     public List<String> getAssistantAdjustments(String pkg) {
420         try {
421             return sINM.getAllowedAssistantAdjustments(pkg);
422         } catch (Exception e) {
423             Log.w(TAG, "Error calling NoMan", e);
424         }
425         return new ArrayList<>();
426     }
427 
showSilentInStatusBar(String pkg)428     public boolean showSilentInStatusBar(String pkg) {
429         try {
430             return !sINM.shouldHideSilentStatusIcons(pkg);
431         } catch (Exception e) {
432             Log.w(TAG, "Error calling NoMan", e);
433         }
434         return false;
435     }
436 
getNotificationHistory(String pkg, String attributionTag)437     public NotificationHistory getNotificationHistory(String pkg, String attributionTag) {
438         try {
439             return sINM.getNotificationHistory(pkg, attributionTag);
440         } catch (Exception e) {
441             Log.w(TAG, "Error calling NoMan", e);
442         }
443         return new NotificationHistory();
444     }
445 
recordAggregatedUsageEvents(Context context, AppRow appRow)446     protected void recordAggregatedUsageEvents(Context context, AppRow appRow) {
447         long now = System.currentTimeMillis();
448         long startTime = now - (DateUtils.DAY_IN_MILLIS * DAYS_TO_CHECK);
449         UsageEvents events = null;
450         try {
451             events = sUsageStatsManager.queryEventsForPackageForUser(
452                     startTime, now, appRow.userId, appRow.pkg, context.getPackageName());
453         } catch (RemoteException e) {
454             e.printStackTrace();
455         }
456         recordAggregatedUsageEvents(events, appRow);
457     }
458 
recordAggregatedUsageEvents(UsageEvents events, AppRow appRow)459     protected void recordAggregatedUsageEvents(UsageEvents events, AppRow appRow) {
460         appRow.sentByChannel = new HashMap<>();
461         appRow.sentByApp = new NotificationsSentState();
462         if (events != null) {
463             UsageEvents.Event event = new UsageEvents.Event();
464             while (events.hasNextEvent()) {
465                 events.getNextEvent(event);
466 
467                 if (event.getEventType() == UsageEvents.Event.NOTIFICATION_INTERRUPTION) {
468                     String channelId = event.mNotificationChannelId;
469                     if (channelId != null) {
470                         NotificationsSentState stats = appRow.sentByChannel.get(channelId);
471                         if (stats == null) {
472                             stats = new NotificationsSentState();
473                             appRow.sentByChannel.put(channelId, stats);
474                         }
475                         if (event.getTimeStamp() > stats.lastSent) {
476                             stats.lastSent = event.getTimeStamp();
477                             appRow.sentByApp.lastSent = event.getTimeStamp();
478                         }
479                         stats.sentCount++;
480                         appRow.sentByApp.sentCount++;
481                         calculateAvgSentCounts(stats);
482                     }
483                 }
484 
485             }
486             calculateAvgSentCounts(appRow.sentByApp);
487         }
488     }
489 
getSentSummary(Context context, NotificationsSentState state, boolean sortByRecency)490     public static CharSequence getSentSummary(Context context, NotificationsSentState state,
491             boolean sortByRecency) {
492         if (state == null) {
493             return null;
494         }
495         if (sortByRecency) {
496             if (state.lastSent == 0) {
497                 return context.getString(R.string.notifications_sent_never);
498             }
499             return StringUtil.formatRelativeTime(
500                     context, System.currentTimeMillis() - state.lastSent, true);
501         } else {
502             if (state.avgSentDaily > 0) {
503                 return StringUtil.getIcuPluralsString(context, state.avgSentDaily,
504                         R.string.notifications_sent_daily);
505             }
506             return StringUtil.getIcuPluralsString(context, state.avgSentWeekly,
507                     R.string.notifications_sent_weekly);
508         }
509     }
510 
calculateAvgSentCounts(NotificationsSentState stats)511     private void calculateAvgSentCounts(NotificationsSentState stats) {
512         if (stats != null) {
513             stats.avgSentDaily = Math.round((float) stats.sentCount / DAYS_TO_CHECK);
514             if (stats.sentCount < DAYS_TO_CHECK) {
515                 stats.avgSentWeekly = stats.sentCount;
516             }
517         }
518     }
519 
getAllowedNotificationAssistant()520     public ComponentName getAllowedNotificationAssistant() {
521         try {
522             return sINM.getAllowedNotificationAssistant();
523         } catch (Exception e) {
524             Log.w(TAG, "Error calling NoMan", e);
525             return null;
526         }
527     }
528 
getDefaultNotificationAssistant()529     public ComponentName getDefaultNotificationAssistant() {
530         try {
531             return sINM.getDefaultNotificationAssistant();
532         } catch (Exception e) {
533             Log.w(TAG, "Error calling NoMan", e);
534             return null;
535         }
536     }
537 
setNASMigrationDoneAndResetDefault(int userId, boolean loadFromConfig)538     public void setNASMigrationDoneAndResetDefault(int userId, boolean loadFromConfig) {
539         try {
540             sINM.setNASMigrationDoneAndResetDefault(userId, loadFromConfig);
541         } catch (Exception e) {
542             Log.w(TAG, "Error calling NoMan", e);
543         }
544     }
545 
setNotificationAssistantGranted(ComponentName cn)546     public boolean setNotificationAssistantGranted(ComponentName cn) {
547         try {
548             sINM.setNotificationAssistantAccessGranted(cn, true);
549             if (cn == null) {
550                 return sINM.getAllowedNotificationAssistant() == null;
551             } else {
552                 return cn.equals(sINM.getAllowedNotificationAssistant());
553             }
554         } catch (Exception e) {
555             Log.w(TAG, "Error calling NoMan", e);
556             return false;
557         }
558     }
559 
createConversationNotificationChannel(String pkg, int uid, NotificationChannel parent, String conversationId)560     public void createConversationNotificationChannel(String pkg, int uid,
561             NotificationChannel parent, String conversationId) {
562         try {
563             sINM.createConversationNotificationChannelForPackage(pkg, uid, parent, conversationId);
564         } catch (Exception e) {
565             Log.w(TAG, "Error calling NoMan", e);
566         }
567     }
568 
getConversationInfo(Context context, String pkg, int uid, String id)569     public ShortcutInfo getConversationInfo(Context context, String pkg, int uid, String id) {
570         LauncherApps la = context.getSystemService(LauncherApps.class);
571 
572         LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery()
573                 .setPackage(pkg)
574                 .setQueryFlags(FLAG_MATCH_DYNAMIC
575                         | FLAG_MATCH_PINNED_BY_ANY_LAUNCHER | FLAG_MATCH_CACHED)
576                 .setShortcutIds(Arrays.asList(id));
577         List<ShortcutInfo> shortcuts = la.getShortcuts(
578                 query, UserHandle.of(UserHandle.getUserId(uid)));
579         if (shortcuts != null && !shortcuts.isEmpty()) {
580            return shortcuts.get(0);
581         }
582         return null;
583     }
584 
getConversationDrawable(Context context, ShortcutInfo info, String pkg, int uid, boolean important)585     public Drawable getConversationDrawable(Context context, ShortcutInfo info, String pkg,
586             int uid, boolean important) {
587         if (info == null) {
588             return null;
589         }
590         ConversationIconFactory iconFactory = new ConversationIconFactory(context,
591                 context.getSystemService(LauncherApps.class),
592                 context.getPackageManager(),
593                 IconDrawableFactory.newInstance(context, false),
594                 context.getResources().getDimensionPixelSize(
595                         R.dimen.conversation_icon_size));
596         return iconFactory.getConversationDrawable(info, pkg, uid, important);
597     }
598 
requestPinShortcut(Context context, ShortcutInfo shortcutInfo)599     public void requestPinShortcut(Context context, ShortcutInfo shortcutInfo) {
600         ShortcutManager sm = context.getSystemService(ShortcutManager.class);
601         sm.requestPinShortcut(shortcutInfo, null);
602     }
603 
resetNotificationImportance()604     public void resetNotificationImportance() {
605         try {
606             sINM.unlockAllNotificationChannels();
607         } catch (Exception e) {
608             Log.w(TAG, "Error calling NoMan", e);
609         }
610     }
611 
getListenerFilter(ComponentName cn, int userId)612     public NotificationListenerFilter getListenerFilter(ComponentName cn, int userId) {
613         NotificationListenerFilter nlf = null;
614         try {
615             nlf = sINM.getListenerFilter(cn, userId);
616         } catch (Exception e) {
617             Log.w(TAG, "Error calling NoMan", e);
618         }
619         return nlf != null ? nlf : new NotificationListenerFilter();
620     }
621 
setListenerFilter(ComponentName cn, int userId, NotificationListenerFilter nlf)622     public void setListenerFilter(ComponentName cn, int userId, NotificationListenerFilter nlf) {
623         try {
624             sINM.setListenerFilter(cn, userId, nlf);
625         } catch (Exception e) {
626             Log.w(TAG, "Error calling NoMan", e);
627         }
628     }
629 
isNotificationListenerAccessGranted(ComponentName cn)630     public boolean isNotificationListenerAccessGranted(ComponentName cn) {
631         try {
632             return sINM.isNotificationListenerAccessGranted(cn);
633         } catch (Exception e) {
634             Log.w(TAG, "Error calling NoMan", e);
635         }
636         return false;
637     }
638 
639     @VisibleForTesting
setNm(INotificationManager inm)640     void setNm(INotificationManager inm) {
641         sINM = inm;
642     }
643 
644     /**
645      * NotificationsSentState contains how often an app sends notifications and how recently it sent
646      * one.
647      */
648     public static class NotificationsSentState {
649         public int avgSentDaily = 0;
650         public int avgSentWeekly = 0;
651         public long lastSent = 0;
652         public int sentCount = 0;
653     }
654 
655     static class Row {
656         public String section;
657     }
658 
659     public static class AppRow extends Row {
660         public String pkg;
661         public int uid;
662         public Drawable icon;
663         public CharSequence label;
664         public Intent settingsIntent;
665         public boolean banned;
666         public boolean first;  // first app in section
667         public boolean systemApp;
668         public boolean lockedImportance;
669         public boolean showBadge;
670         // For apps target T but have not but has not requested the permission
671         // we cannot change the permission state
672         public boolean permissionStateLocked;
673         public int bubblePreference = NotificationManager.BUBBLE_PREFERENCE_NONE;
674         public int userId;
675         public int blockedChannelCount;
676         public int channelCount;
677         public Map<String, NotificationsSentState> sentByChannel;
678         public NotificationsSentState sentByApp;
679     }
680 }
681