• 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.role.RoleManager;
30 import android.app.usage.IUsageStatsManager;
31 import android.app.usage.UsageEvents;
32 import android.companion.ICompanionDeviceManager;
33 import android.content.ComponentName;
34 import android.content.Context;
35 import android.content.Intent;
36 import android.content.pm.ApplicationInfo;
37 import android.content.pm.LauncherApps;
38 import android.content.pm.PackageInfo;
39 import android.content.pm.PackageManager;
40 import android.content.pm.ParceledListSlice;
41 import android.content.pm.ShortcutInfo;
42 import android.content.pm.ShortcutManager;
43 import android.graphics.drawable.Drawable;
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.settingslib.R;
56 import com.android.settingslib.Utils;
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 
isBlockable(Context context, ApplicationInfo info)100     public boolean isBlockable(Context context, ApplicationInfo info) {
101         final boolean blocked = getNotificationsBanned(info.packageName, info.uid);
102         final boolean systemApp = isSystemApp(context, info);
103         return !systemApp || (systemApp && blocked);
104     }
105 
loadAppRow(Context context, PackageManager pm, RoleManager roleManager, PackageInfo app)106     public AppRow loadAppRow(Context context, PackageManager pm,
107             RoleManager roleManager, PackageInfo app) {
108         final AppRow row = loadAppRow(context, pm, app.applicationInfo);
109         recordCanBeBlocked(context, pm, roleManager, app, row);
110         return row;
111     }
112 
recordCanBeBlocked(Context context, PackageManager pm, RoleManager rm, PackageInfo app, AppRow row)113     void recordCanBeBlocked(Context context, PackageManager pm, RoleManager rm, PackageInfo app,
114             AppRow row) {
115         row.systemApp = Utils.isSystemPackage(context.getResources(), pm, app);
116         List<String> roles = rm.getHeldRolesFromController(app.packageName);
117         if (roles.contains(RoleManager.ROLE_DIALER)
118                 || roles.contains(RoleManager.ROLE_EMERGENCY)) {
119             row.systemApp = true;
120         }
121         final String[] nonBlockablePkgs = context.getResources().getStringArray(
122                 com.android.internal.R.array.config_nonBlockableNotificationPackages);
123         markAppRowWithBlockables(nonBlockablePkgs, row, app.packageName);
124     }
125 
markAppRowWithBlockables(String[] nonBlockablePkgs, AppRow row, String packageName)126     @VisibleForTesting static void markAppRowWithBlockables(String[] nonBlockablePkgs, AppRow row,
127             String packageName) {
128         if (nonBlockablePkgs != null) {
129             int N = nonBlockablePkgs.length;
130             for (int i = 0; i < N; i++) {
131                 String pkg = nonBlockablePkgs[i];
132                 if (pkg == null) {
133                     continue;
134                 } else if (pkg.contains(":")) {
135                     // handled by NotificationChannel.isImportanceLockedByOEM()
136                     continue;
137                 } else if (packageName.equals(nonBlockablePkgs[i])) {
138                     row.systemApp = row.lockedImportance = true;
139                 }
140             }
141         }
142     }
143 
getDeviceList(ICompanionDeviceManager cdm, LocalBluetoothManager lbm, String pkg, int userId)144     static public CharSequence getDeviceList(ICompanionDeviceManager cdm, LocalBluetoothManager lbm,
145             String pkg, int userId) {
146         boolean multiple = false;
147         StringBuilder sb = new StringBuilder();
148 
149         try {
150             List<String> associatedMacAddrs = cdm.getAssociations(pkg, userId);
151             if (associatedMacAddrs != null) {
152                 for (String assocMac : associatedMacAddrs) {
153                     final Collection<CachedBluetoothDevice> cachedDevices =
154                             lbm.getCachedDeviceManager().getCachedDevicesCopy();
155                     for (CachedBluetoothDevice cachedBluetoothDevice : cachedDevices) {
156                         if (Objects.equals(assocMac, cachedBluetoothDevice.getAddress())) {
157                             if (multiple) {
158                                 sb.append(", ");
159                             } else {
160                                 multiple = true;
161                             }
162                             sb.append(cachedBluetoothDevice.getName());
163                         }
164                     }
165                 }
166             }
167         } catch (RemoteException e) {
168             Log.w(TAG, "Error calling CDM", e);
169         }
170         return sb.toString();
171     }
172 
isSystemApp(Context context, ApplicationInfo app)173     public boolean isSystemApp(Context context, ApplicationInfo app) {
174         try {
175             PackageInfo info = context.getPackageManager().getPackageInfo(
176                     app.packageName, PackageManager.GET_SIGNATURES);
177             RoleManager rm = context.getSystemService(RoleManager.class);
178             final AppRow row = new AppRow();
179             recordCanBeBlocked(context, context.getPackageManager(), rm, info, row);
180             return row.systemApp;
181         } catch (PackageManager.NameNotFoundException e) {
182             e.printStackTrace();
183         }
184         return false;
185     }
186 
getNotificationsBanned(String pkg, int uid)187     public boolean getNotificationsBanned(String pkg, int uid) {
188         try {
189             final boolean enabled = sINM.areNotificationsEnabledForPackage(pkg, uid);
190             return !enabled;
191         } catch (Exception e) {
192             Log.w(TAG, "Error calling NoMan", e);
193             return false;
194         }
195     }
196 
setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled)197     public boolean setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
198         try {
199             if (onlyHasDefaultChannel(pkg, uid)) {
200                 NotificationChannel defaultChannel =
201                         getChannel(pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID, null);
202                 defaultChannel.setImportance(enabled ? IMPORTANCE_UNSPECIFIED : IMPORTANCE_NONE);
203                 updateChannel(pkg, uid, defaultChannel);
204             }
205             sINM.setNotificationsEnabledForPackage(pkg, uid, enabled);
206             return true;
207         } catch (Exception e) {
208             Log.w(TAG, "Error calling NoMan", e);
209             return false;
210         }
211     }
212 
canShowBadge(String pkg, int uid)213     public boolean canShowBadge(String pkg, int uid) {
214         try {
215             return sINM.canShowBadge(pkg, uid);
216         } catch (Exception e) {
217             Log.w(TAG, "Error calling NoMan", e);
218             return false;
219         }
220     }
221 
setShowBadge(String pkg, int uid, boolean showBadge)222     public boolean setShowBadge(String pkg, int uid, boolean showBadge) {
223         try {
224             sINM.setShowBadge(pkg, uid, showBadge);
225             return true;
226         } catch (Exception e) {
227             Log.w(TAG, "Error calling NoMan", e);
228             return false;
229         }
230     }
231 
getBubblePreference(String pkg, int uid)232     public int getBubblePreference(String pkg, int uid) {
233         try {
234             return sINM.getBubblePreferenceForPackage(pkg, uid);
235         } catch (Exception e) {
236             Log.w(TAG, "Error calling NoMan", e);
237             return -1;
238         }
239     }
240 
setAllowBubbles(String pkg, int uid, int preference)241     public boolean setAllowBubbles(String pkg, int uid, int preference) {
242         try {
243             sINM.setBubblesAllowed(pkg, uid, preference);
244             return true;
245         } catch (Exception e) {
246             Log.w(TAG, "Error calling NoMan", e);
247             return false;
248         }
249     }
250 
getChannel(String pkg, int uid, String channelId)251     public NotificationChannel getChannel(String pkg, int uid, String channelId) {
252         return getChannel(pkg, uid, channelId, null);
253     }
254 
getChannel(String pkg, int uid, String channelId, String conversationId)255     public NotificationChannel getChannel(String pkg, int uid, String channelId,
256             String conversationId) {
257         if (channelId == null) {
258             return null;
259         }
260         try {
261             return sINM.getNotificationChannelForPackage(pkg, uid, channelId, conversationId, true);
262         } catch (Exception e) {
263             Log.w(TAG, "Error calling NoMan", e);
264             return null;
265         }
266     }
267 
getGroup(String pkg, int uid, String groupId)268     public NotificationChannelGroup getGroup(String pkg, int uid, String groupId) {
269         if (groupId == null) {
270             return null;
271         }
272         try {
273             return sINM.getNotificationChannelGroupForPackage(groupId, pkg, uid);
274         } catch (Exception e) {
275             Log.w(TAG, "Error calling NoMan", e);
276             return null;
277         }
278     }
279 
getGroups(String pkg, int uid)280     public ParceledListSlice<NotificationChannelGroup> getGroups(String pkg, int uid) {
281         try {
282             return sINM.getNotificationChannelGroupsForPackage(pkg, uid, false);
283         } catch (Exception e) {
284             Log.w(TAG, "Error calling NoMan", e);
285             return ParceledListSlice.emptyList();
286         }
287     }
288 
getConversations(String pkg, int uid)289     public ParceledListSlice<ConversationChannelWrapper> getConversations(String pkg, int uid) {
290         try {
291             return sINM.getConversationsForPackage(pkg, uid);
292         } catch (Exception e) {
293             Log.w(TAG, "Error calling NoMan", e);
294             return ParceledListSlice.emptyList();
295         }
296     }
297 
getConversations(boolean onlyImportant)298     public ParceledListSlice<ConversationChannelWrapper> getConversations(boolean onlyImportant) {
299         try {
300             return sINM.getConversations(onlyImportant);
301         } catch (Exception e) {
302             Log.w(TAG, "Error calling NoMan", e);
303             return ParceledListSlice.emptyList();
304         }
305     }
306 
hasSentValidMsg(String pkg, int uid)307     public boolean hasSentValidMsg(String pkg, int uid) {
308         try {
309             return sINM.hasSentValidMsg(pkg, uid);
310         } catch (Exception e) {
311             Log.w(TAG, "Error calling NoMan", e);
312             return false;
313         }
314     }
315 
isInInvalidMsgState(String pkg, int uid)316     public boolean isInInvalidMsgState(String pkg, int uid) {
317         try {
318             return sINM.isInInvalidMsgState(pkg, uid);
319         } catch (Exception e) {
320             Log.w(TAG, "Error calling NoMan", e);
321             return false;
322         }
323     }
324 
hasUserDemotedInvalidMsgApp(String pkg, int uid)325     public boolean hasUserDemotedInvalidMsgApp(String pkg, int uid) {
326         try {
327             return sINM.hasUserDemotedInvalidMsgApp(pkg, uid);
328         } catch (Exception e) {
329             Log.w(TAG, "Error calling NoMan", e);
330             return false;
331         }
332     }
333 
setInvalidMsgAppDemoted(String pkg, int uid, boolean isDemoted)334     public void setInvalidMsgAppDemoted(String pkg, int uid, boolean isDemoted) {
335         try {
336              sINM.setInvalidMsgAppDemoted(pkg, uid, isDemoted);
337         } catch (Exception e) {
338             Log.w(TAG, "Error calling NoMan", e);
339         }
340     }
341 
342     /**
343      * Returns all notification channels associated with the package and uid that will bypass DND
344      */
getNotificationChannelsBypassingDnd(String pkg, int uid)345     public ParceledListSlice<NotificationChannel> getNotificationChannelsBypassingDnd(String pkg,
346             int uid) {
347         try {
348             return sINM.getNotificationChannelsBypassingDnd(pkg, uid);
349         } catch (Exception e) {
350             Log.w(TAG, "Error calling NoMan", e);
351             return ParceledListSlice.emptyList();
352         }
353     }
354 
updateChannel(String pkg, int uid, NotificationChannel channel)355     public void updateChannel(String pkg, int uid, NotificationChannel channel) {
356         try {
357             sINM.updateNotificationChannelForPackage(pkg, uid, channel);
358         } catch (Exception e) {
359             Log.w(TAG, "Error calling NoMan", e);
360         }
361     }
362 
updateChannelGroup(String pkg, int uid, NotificationChannelGroup group)363     public void updateChannelGroup(String pkg, int uid, NotificationChannelGroup group) {
364         try {
365             sINM.updateNotificationChannelGroupForPackage(pkg, uid, group);
366         } catch (Exception e) {
367             Log.w(TAG, "Error calling NoMan", e);
368         }
369     }
370 
getDeletedChannelCount(String pkg, int uid)371     public int getDeletedChannelCount(String pkg, int uid) {
372         try {
373             return sINM.getDeletedChannelCount(pkg, uid);
374         } catch (Exception e) {
375             Log.w(TAG, "Error calling NoMan", e);
376             return 0;
377         }
378     }
379 
getBlockedChannelCount(String pkg, int uid)380     public int getBlockedChannelCount(String pkg, int uid) {
381         try {
382             return sINM.getBlockedChannelCount(pkg, uid);
383         } catch (Exception e) {
384             Log.w(TAG, "Error calling NoMan", e);
385             return 0;
386         }
387     }
388 
onlyHasDefaultChannel(String pkg, int uid)389     public boolean onlyHasDefaultChannel(String pkg, int uid) {
390         try {
391             return sINM.onlyHasDefaultChannel(pkg, uid);
392         } catch (Exception e) {
393             Log.w(TAG, "Error calling NoMan", e);
394             return false;
395         }
396     }
397 
getChannelCount(String pkg, int uid)398     public int getChannelCount(String pkg, int uid) {
399         try {
400             return sINM.getNumNotificationChannelsForPackage(pkg, uid, false);
401         } catch (Exception e) {
402             Log.w(TAG, "Error calling NoMan", e);
403             return 0;
404         }
405     }
406 
getNumAppsBypassingDnd(int uid)407     public int getNumAppsBypassingDnd(int uid) {
408         try {
409             return sINM.getAppsBypassingDndCount(uid);
410         } catch (Exception e) {
411             Log.w(TAG, "Error calling NoMan", e);
412             return 0;
413         }
414     }
415 
getBlockedAppCount()416     public int getBlockedAppCount() {
417         try {
418             return sINM.getBlockedAppCount(UserHandle.myUserId());
419         } catch (Exception e) {
420             Log.w(TAG, "Error calling NoMan", e);
421             return 0;
422         }
423     }
424 
shouldHideSilentStatusBarIcons(Context context)425     public boolean shouldHideSilentStatusBarIcons(Context context) {
426         try {
427             return sINM.shouldHideSilentStatusIcons(context.getPackageName());
428         } catch (Exception e) {
429             Log.w(TAG, "Error calling NoMan", e);
430             return false;
431         }
432     }
433 
setHideSilentStatusIcons(boolean hide)434     public void setHideSilentStatusIcons(boolean hide) {
435         try {
436             sINM.setHideSilentStatusIcons(hide);
437         } catch (Exception e) {
438             Log.w(TAG, "Error calling NoMan", e);
439         }
440     }
441 
allowAssistantAdjustment(String capability, boolean allowed)442     public void allowAssistantAdjustment(String capability, boolean allowed) {
443         try {
444             if (allowed) {
445                 sINM.allowAssistantAdjustment(capability);
446             } else {
447                 sINM.disallowAssistantAdjustment(capability);
448             }
449         } catch (Exception e) {
450             Log.w(TAG, "Error calling NoMan", e);
451         }
452     }
453 
getAssistantAdjustments(String pkg)454     public List<String> getAssistantAdjustments(String pkg) {
455         try {
456             return sINM.getAllowedAssistantAdjustments(pkg);
457         } catch (Exception e) {
458             Log.w(TAG, "Error calling NoMan", e);
459         }
460         return new ArrayList<>();
461     }
462 
showSilentInStatusBar(String pkg)463     public boolean showSilentInStatusBar(String pkg) {
464         try {
465             return !sINM.shouldHideSilentStatusIcons(pkg);
466         } catch (Exception e) {
467             Log.w(TAG, "Error calling NoMan", e);
468         }
469         return false;
470     }
471 
getNotificationHistory(String pkg, String attributionTag)472     public NotificationHistory getNotificationHistory(String pkg, String attributionTag) {
473         try {
474             return sINM.getNotificationHistory(pkg, attributionTag);
475         } catch (Exception e) {
476             Log.w(TAG, "Error calling NoMan", e);
477         }
478         return new NotificationHistory();
479     }
480 
recordAggregatedUsageEvents(Context context, AppRow appRow)481     protected void recordAggregatedUsageEvents(Context context, AppRow appRow) {
482         long now = System.currentTimeMillis();
483         long startTime = now - (DateUtils.DAY_IN_MILLIS * DAYS_TO_CHECK);
484         UsageEvents events = null;
485         try {
486             events = sUsageStatsManager.queryEventsForPackageForUser(
487                     startTime, now, appRow.userId, appRow.pkg, context.getPackageName());
488         } catch (RemoteException e) {
489             e.printStackTrace();
490         }
491         recordAggregatedUsageEvents(events, appRow);
492     }
493 
recordAggregatedUsageEvents(UsageEvents events, AppRow appRow)494     protected void recordAggregatedUsageEvents(UsageEvents events, AppRow appRow) {
495         appRow.sentByChannel = new HashMap<>();
496         appRow.sentByApp = new NotificationsSentState();
497         if (events != null) {
498             UsageEvents.Event event = new UsageEvents.Event();
499             while (events.hasNextEvent()) {
500                 events.getNextEvent(event);
501 
502                 if (event.getEventType() == UsageEvents.Event.NOTIFICATION_INTERRUPTION) {
503                     String channelId = event.mNotificationChannelId;
504                     if (channelId != null) {
505                         NotificationsSentState stats = appRow.sentByChannel.get(channelId);
506                         if (stats == null) {
507                             stats = new NotificationsSentState();
508                             appRow.sentByChannel.put(channelId, stats);
509                         }
510                         if (event.getTimeStamp() > stats.lastSent) {
511                             stats.lastSent = event.getTimeStamp();
512                             appRow.sentByApp.lastSent = event.getTimeStamp();
513                         }
514                         stats.sentCount++;
515                         appRow.sentByApp.sentCount++;
516                         calculateAvgSentCounts(stats);
517                     }
518                 }
519 
520             }
521             calculateAvgSentCounts(appRow.sentByApp);
522         }
523     }
524 
getSentSummary(Context context, NotificationsSentState state, boolean sortByRecency)525     public static CharSequence getSentSummary(Context context, NotificationsSentState state,
526             boolean sortByRecency) {
527         if (state == null) {
528             return null;
529         }
530         if (sortByRecency) {
531             if (state.lastSent == 0) {
532                 return context.getString(R.string.notifications_sent_never);
533             }
534             return StringUtil.formatRelativeTime(
535                     context, System.currentTimeMillis() - state.lastSent, true);
536         } else {
537             if (state.avgSentDaily > 0) {
538                 return context.getResources().getQuantityString(R.plurals.notifications_sent_daily,
539                         state.avgSentDaily, state.avgSentDaily);
540             }
541             return context.getResources().getQuantityString(R.plurals.notifications_sent_weekly,
542                     state.avgSentWeekly, state.avgSentWeekly);
543         }
544     }
545 
calculateAvgSentCounts(NotificationsSentState stats)546     private void calculateAvgSentCounts(NotificationsSentState stats) {
547         if (stats != null) {
548             stats.avgSentDaily = Math.round((float) stats.sentCount / DAYS_TO_CHECK);
549             if (stats.sentCount < DAYS_TO_CHECK) {
550                 stats.avgSentWeekly = stats.sentCount;
551             }
552         }
553     }
554 
getAllowedNotificationAssistant()555     public ComponentName getAllowedNotificationAssistant() {
556         try {
557             return sINM.getAllowedNotificationAssistant();
558         } catch (Exception e) {
559             Log.w(TAG, "Error calling NoMan", e);
560             return null;
561         }
562     }
563 
getDefaultNotificationAssistant()564     public ComponentName getDefaultNotificationAssistant() {
565         try {
566             return sINM.getDefaultNotificationAssistant();
567         } catch (Exception e) {
568             Log.w(TAG, "Error calling NoMan", e);
569             return null;
570         }
571     }
572 
setNASMigrationDoneAndResetDefault(int userId, boolean loadFromConfig)573     public void setNASMigrationDoneAndResetDefault(int userId, boolean loadFromConfig) {
574         try {
575             sINM.setNASMigrationDoneAndResetDefault(userId, loadFromConfig);
576         } catch (Exception e) {
577             Log.w(TAG, "Error calling NoMan", e);
578         }
579     }
580 
setNotificationAssistantGranted(ComponentName cn)581     public boolean setNotificationAssistantGranted(ComponentName cn) {
582         try {
583             sINM.setNotificationAssistantAccessGranted(cn, true);
584             if (cn == null) {
585                 return sINM.getAllowedNotificationAssistant() == null;
586             } else {
587                 return cn.equals(sINM.getAllowedNotificationAssistant());
588             }
589         } catch (Exception e) {
590             Log.w(TAG, "Error calling NoMan", e);
591             return false;
592         }
593     }
594 
createConversationNotificationChannel(String pkg, int uid, NotificationChannel parent, String conversationId)595     public void createConversationNotificationChannel(String pkg, int uid,
596             NotificationChannel parent, String conversationId) {
597         try {
598             sINM.createConversationNotificationChannelForPackage(pkg, uid, parent, conversationId);
599         } catch (Exception e) {
600             Log.w(TAG, "Error calling NoMan", e);
601         }
602     }
603 
getConversationInfo(Context context, String pkg, int uid, String id)604     public ShortcutInfo getConversationInfo(Context context, String pkg, int uid, String id) {
605         LauncherApps la = context.getSystemService(LauncherApps.class);
606 
607         LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery()
608                 .setPackage(pkg)
609                 .setQueryFlags(FLAG_MATCH_DYNAMIC
610                         | FLAG_MATCH_PINNED_BY_ANY_LAUNCHER | FLAG_MATCH_CACHED)
611                 .setShortcutIds(Arrays.asList(id));
612         List<ShortcutInfo> shortcuts = la.getShortcuts(
613                 query, UserHandle.of(UserHandle.getUserId(uid)));
614         if (shortcuts != null && !shortcuts.isEmpty()) {
615            return shortcuts.get(0);
616         }
617         return null;
618     }
619 
getConversationDrawable(Context context, ShortcutInfo info, String pkg, int uid, boolean important)620     public Drawable getConversationDrawable(Context context, ShortcutInfo info, String pkg,
621             int uid, boolean important) {
622         if (info == null) {
623             return null;
624         }
625         ConversationIconFactory iconFactory = new ConversationIconFactory(context,
626                 context.getSystemService(LauncherApps.class),
627                 context.getPackageManager(),
628                 IconDrawableFactory.newInstance(context, false),
629                 context.getResources().getDimensionPixelSize(
630                         R.dimen.conversation_icon_size));
631         return iconFactory.getConversationDrawable(info, pkg, uid, important);
632     }
633 
requestPinShortcut(Context context, ShortcutInfo shortcutInfo)634     public void requestPinShortcut(Context context, ShortcutInfo shortcutInfo) {
635         ShortcutManager sm = context.getSystemService(ShortcutManager.class);
636         sm.requestPinShortcut(shortcutInfo, null);
637     }
638 
resetNotificationImportance()639     public void resetNotificationImportance() {
640         try {
641             sINM.unlockAllNotificationChannels();
642         } catch (Exception e) {
643             Log.w(TAG, "Error calling NoMan", e);
644         }
645     }
646 
getListenerFilter(ComponentName cn, int userId)647     public NotificationListenerFilter getListenerFilter(ComponentName cn, int userId) {
648         NotificationListenerFilter nlf = null;
649         try {
650             nlf = sINM.getListenerFilter(cn, userId);
651         } catch (Exception e) {
652             Log.w(TAG, "Error calling NoMan", e);
653         }
654         return nlf != null ? nlf : new NotificationListenerFilter();
655     }
656 
setListenerFilter(ComponentName cn, int userId, NotificationListenerFilter nlf)657     public void setListenerFilter(ComponentName cn, int userId, NotificationListenerFilter nlf) {
658         try {
659             sINM.setListenerFilter(cn, userId, nlf);
660         } catch (Exception e) {
661             Log.w(TAG, "Error calling NoMan", e);
662         }
663     }
664 
isNotificationListenerAccessGranted(ComponentName cn)665     public boolean isNotificationListenerAccessGranted(ComponentName cn) {
666         try {
667             return sINM.isNotificationListenerAccessGranted(cn);
668         } catch (Exception e) {
669             Log.w(TAG, "Error calling NoMan", e);
670         }
671         return false;
672     }
673 
674     /**
675      * NotificationsSentState contains how often an app sends notifications and how recently it sent
676      * one.
677      */
678     public static class NotificationsSentState {
679         public int avgSentDaily = 0;
680         public int avgSentWeekly = 0;
681         public long lastSent = 0;
682         public int sentCount = 0;
683     }
684 
685     static class Row {
686         public String section;
687     }
688 
689     public static class AppRow extends Row {
690         public String pkg;
691         public int uid;
692         public Drawable icon;
693         public CharSequence label;
694         public Intent settingsIntent;
695         public boolean banned;
696         public boolean first;  // first app in section
697         public boolean systemApp;
698         public boolean lockedImportance;
699         public boolean showBadge;
700         public int bubblePreference = NotificationManager.BUBBLE_PREFERENCE_NONE;
701         public int userId;
702         public int blockedChannelCount;
703         public int channelCount;
704         public Map<String, NotificationsSentState> sentByChannel;
705         public NotificationsSentState sentByApp;
706     }
707 }
708