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