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