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