1 /* 2 * Copyright (C) 2007 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 17 package com.android.server.notification; 18 19 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS; 20 import static android.service.notification.NotificationListenerService.TRIM_FULL; 21 import static android.service.notification.NotificationListenerService.TRIM_LIGHT; 22 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; 23 import static org.xmlpull.v1.XmlPullParser.END_TAG; 24 import static org.xmlpull.v1.XmlPullParser.START_TAG; 25 26 import android.app.ActivityManager; 27 import android.app.ActivityManagerNative; 28 import android.app.AppGlobals; 29 import android.app.AppOpsManager; 30 import android.app.IActivityManager; 31 import android.app.INotificationManager; 32 import android.app.ITransientNotification; 33 import android.app.Notification; 34 import android.app.NotificationManager; 35 import android.app.NotificationManager.Policy; 36 import android.app.PendingIntent; 37 import android.app.StatusBarManager; 38 import android.app.backup.BackupManager; 39 import android.app.usage.UsageEvents; 40 import android.app.usage.UsageStatsManagerInternal; 41 import android.content.BroadcastReceiver; 42 import android.content.ComponentName; 43 import android.content.ContentResolver; 44 import android.content.Context; 45 import android.content.Intent; 46 import android.content.IntentFilter; 47 import android.content.pm.ApplicationInfo; 48 import android.content.pm.IPackageManager; 49 import android.content.pm.PackageInfo; 50 import android.content.pm.PackageManager; 51 import android.content.pm.PackageManager.NameNotFoundException; 52 import android.content.pm.ParceledListSlice; 53 import android.content.pm.UserInfo; 54 import android.content.res.Resources; 55 import android.database.ContentObserver; 56 import android.media.AudioAttributes; 57 import android.media.AudioManager; 58 import android.media.AudioManagerInternal; 59 import android.media.AudioSystem; 60 import android.media.IRingtonePlayer; 61 import android.net.Uri; 62 import android.os.Binder; 63 import android.os.Build; 64 import android.os.Bundle; 65 import android.os.Environment; 66 import android.os.Handler; 67 import android.os.HandlerThread; 68 import android.os.IBinder; 69 import android.os.IInterface; 70 import android.os.Looper; 71 import android.os.Message; 72 import android.os.Process; 73 import android.os.RemoteException; 74 import android.os.SystemProperties; 75 import android.os.UserHandle; 76 import android.os.UserManager; 77 import android.os.Vibrator; 78 import android.provider.Settings; 79 import android.service.notification.Condition; 80 import android.service.notification.IConditionListener; 81 import android.service.notification.IConditionProvider; 82 import android.service.notification.INotificationListener; 83 import android.service.notification.IStatusBarNotificationHolder; 84 import android.service.notification.NotificationListenerService; 85 import android.service.notification.NotificationRankingUpdate; 86 import android.service.notification.StatusBarNotification; 87 import android.service.notification.ZenModeConfig; 88 import android.telephony.PhoneStateListener; 89 import android.telephony.TelephonyManager; 90 import android.text.TextUtils; 91 import android.util.ArrayMap; 92 import android.util.ArraySet; 93 import android.util.AtomicFile; 94 import android.util.Log; 95 import android.util.Slog; 96 import android.util.Xml; 97 import android.view.accessibility.AccessibilityEvent; 98 import android.view.accessibility.AccessibilityManager; 99 import android.widget.Toast; 100 101 import com.android.internal.R; 102 import com.android.internal.statusbar.NotificationVisibility; 103 import com.android.internal.util.FastXmlSerializer; 104 import com.android.server.EventLogTags; 105 import com.android.server.LocalServices; 106 import com.android.server.SystemService; 107 import com.android.server.lights.Light; 108 import com.android.server.lights.LightsManager; 109 import com.android.server.notification.ManagedServices.ManagedServiceInfo; 110 import com.android.server.notification.ManagedServices.UserProfiles; 111 import com.android.server.statusbar.StatusBarManagerInternal; 112 113 import libcore.io.IoUtils; 114 115 import org.json.JSONArray; 116 import org.json.JSONException; 117 import org.json.JSONObject; 118 import org.xmlpull.v1.XmlPullParser; 119 import org.xmlpull.v1.XmlPullParserException; 120 import org.xmlpull.v1.XmlSerializer; 121 122 import java.io.ByteArrayInputStream; 123 import java.io.ByteArrayOutputStream; 124 import java.io.File; 125 import java.io.FileDescriptor; 126 import java.io.FileInputStream; 127 import java.io.FileNotFoundException; 128 import java.io.FileOutputStream; 129 import java.io.IOException; 130 import java.io.InputStream; 131 import java.io.OutputStream; 132 import java.io.PrintWriter; 133 import java.nio.charset.StandardCharsets; 134 import java.util.ArrayDeque; 135 import java.util.ArrayList; 136 import java.util.HashSet; 137 import java.util.Iterator; 138 import java.util.List; 139 import java.util.Map.Entry; 140 import java.util.Objects; 141 142 /** {@hide} */ 143 public class NotificationManagerService extends SystemService { 144 static final String TAG = "NotificationService"; 145 static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); 146 public static final boolean ENABLE_CHILD_NOTIFICATIONS = Build.IS_DEBUGGABLE 147 && SystemProperties.getBoolean("debug.child_notifs", false); 148 149 static final int MAX_PACKAGE_NOTIFICATIONS = 50; 150 151 // message codes 152 static final int MESSAGE_TIMEOUT = 2; 153 static final int MESSAGE_SAVE_POLICY_FILE = 3; 154 static final int MESSAGE_RECONSIDER_RANKING = 4; 155 static final int MESSAGE_RANKING_CONFIG_CHANGE = 5; 156 static final int MESSAGE_SEND_RANKING_UPDATE = 6; 157 static final int MESSAGE_LISTENER_HINTS_CHANGED = 7; 158 static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 8; 159 160 static final int LONG_DELAY = 3500; // 3.5 seconds 161 static final int SHORT_DELAY = 2000; // 2 seconds 162 163 static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250}; 164 165 static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps 166 167 static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION; 168 static final boolean SCORE_ONGOING_HIGHER = false; 169 170 static final int JUNK_SCORE = -1000; 171 static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; 172 static final int SCORE_DISPLAY_THRESHOLD = Notification.PRIORITY_MIN * NOTIFICATION_PRIORITY_MULTIPLIER; 173 174 // Notifications with scores below this will not interrupt the user, either via LED or 175 // sound or vibration 176 static final int SCORE_INTERRUPTION_THRESHOLD = 177 Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER; 178 179 static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true; 180 static final boolean ENABLE_BLOCKED_TOASTS = true; 181 182 // When #matchesCallFilter is called from the ringer, wait at most 183 // 3s to resolve the contacts. This timeout is required since 184 // ContactsProvider might take a long time to start up. 185 // 186 // Return STARRED_CONTACT when the timeout is hit in order to avoid 187 // missed calls in ZEN mode "Important". 188 static final int MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS = 3000; 189 static final float MATCHES_CALL_FILTER_TIMEOUT_AFFINITY = 190 ValidateNotificationPeople.STARRED_CONTACT; 191 192 /** notification_enqueue status value for a newly enqueued notification. */ 193 private static final int EVENTLOG_ENQUEUE_STATUS_NEW = 0; 194 195 /** notification_enqueue status value for an existing notification. */ 196 private static final int EVENTLOG_ENQUEUE_STATUS_UPDATE = 1; 197 198 /** notification_enqueue status value for an ignored notification. */ 199 private static final int EVENTLOG_ENQUEUE_STATUS_IGNORED = 2; 200 201 private IActivityManager mAm; 202 AudioManager mAudioManager; 203 AudioManagerInternal mAudioManagerInternal; 204 StatusBarManagerInternal mStatusBar; 205 Vibrator mVibrator; 206 207 final IBinder mForegroundToken = new Binder(); 208 private WorkerHandler mHandler; 209 private final HandlerThread mRankingThread = new HandlerThread("ranker", 210 Process.THREAD_PRIORITY_BACKGROUND); 211 212 private Light mNotificationLight; 213 Light mAttentionLight; 214 private int mDefaultNotificationColor; 215 private int mDefaultNotificationLedOn; 216 217 private int mDefaultNotificationLedOff; 218 private long[] mDefaultVibrationPattern; 219 220 private long[] mFallbackVibrationPattern; 221 private boolean mUseAttentionLight; 222 boolean mSystemReady; 223 224 private boolean mDisableNotificationEffects; 225 private int mCallState; 226 private String mSoundNotificationKey; 227 private String mVibrateNotificationKey; 228 229 private final ArraySet<ManagedServiceInfo> mListenersDisablingEffects = new ArraySet<>(); 230 private ComponentName mEffectsSuppressor; 231 private int mListenerHints; // right now, all hints are global 232 private int mInterruptionFilter = NotificationListenerService.INTERRUPTION_FILTER_UNKNOWN; 233 234 // for enabling and disabling notification pulse behavior 235 private boolean mScreenOn = true; 236 private boolean mInCall = false; 237 private boolean mNotificationPulseEnabled; 238 239 // used as a mutex for access to all active notifications & listeners 240 final ArrayList<NotificationRecord> mNotificationList = 241 new ArrayList<NotificationRecord>(); 242 final ArrayMap<String, NotificationRecord> mNotificationsByKey = 243 new ArrayMap<String, NotificationRecord>(); 244 final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>(); 245 final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>(); 246 final PolicyAccess mPolicyAccess = new PolicyAccess(); 247 248 // The last key in this list owns the hardware. 249 ArrayList<String> mLights = new ArrayList<>(); 250 251 private AppOpsManager mAppOps; 252 private UsageStatsManagerInternal mAppUsageStats; 253 254 private Archive mArchive; 255 256 // Persistent storage for notification policy 257 private AtomicFile mPolicyFile; 258 259 // Temporary holder for <blocked-packages> config coming from old policy files. 260 private HashSet<String> mBlockedPackages = new HashSet<String>(); 261 262 private static final int DB_VERSION = 1; 263 264 private static final String TAG_NOTIFICATION_POLICY = "notification-policy"; 265 private static final String ATTR_VERSION = "version"; 266 267 // Obsolete: converted if present, but not resaved to disk. 268 private static final String TAG_BLOCKED_PKGS = "blocked-packages"; 269 private static final String TAG_PACKAGE = "package"; 270 private static final String ATTR_NAME = "name"; 271 272 private RankingHelper mRankingHelper; 273 274 private final UserProfiles mUserProfiles = new UserProfiles(); 275 private NotificationListeners mListeners; 276 private ConditionProviders mConditionProviders; 277 private NotificationUsageStats mUsageStats; 278 279 private static final int MY_UID = Process.myUid(); 280 private static final int MY_PID = Process.myPid(); 281 private static final int REASON_DELEGATE_CLICK = 1; 282 private static final int REASON_DELEGATE_CANCEL = 2; 283 private static final int REASON_DELEGATE_CANCEL_ALL = 3; 284 private static final int REASON_DELEGATE_ERROR = 4; 285 private static final int REASON_PACKAGE_CHANGED = 5; 286 private static final int REASON_USER_STOPPED = 6; 287 private static final int REASON_PACKAGE_BANNED = 7; 288 private static final int REASON_NOMAN_CANCEL = 8; 289 private static final int REASON_NOMAN_CANCEL_ALL = 9; 290 private static final int REASON_LISTENER_CANCEL = 10; 291 private static final int REASON_LISTENER_CANCEL_ALL = 11; 292 private static final int REASON_GROUP_SUMMARY_CANCELED = 12; 293 private static final int REASON_GROUP_OPTIMIZATION = 13; 294 295 private static class Archive { 296 final int mBufferSize; 297 final ArrayDeque<StatusBarNotification> mBuffer; 298 299 public Archive(int size) { 300 mBufferSize = size; 301 mBuffer = new ArrayDeque<StatusBarNotification>(mBufferSize); 302 } 303 304 public String toString() { 305 final StringBuilder sb = new StringBuilder(); 306 final int N = mBuffer.size(); 307 sb.append("Archive ("); 308 sb.append(N); 309 sb.append(" notification"); 310 sb.append((N==1)?")":"s)"); 311 return sb.toString(); 312 } 313 314 public void record(StatusBarNotification nr) { 315 if (mBuffer.size() == mBufferSize) { 316 mBuffer.removeFirst(); 317 } 318 319 // We don't want to store the heavy bits of the notification in the archive, 320 // but other clients in the system process might be using the object, so we 321 // store a (lightened) copy. 322 mBuffer.addLast(nr.cloneLight()); 323 } 324 325 public Iterator<StatusBarNotification> descendingIterator() { 326 return mBuffer.descendingIterator(); 327 } 328 329 public StatusBarNotification[] getArray(int count) { 330 if (count == 0) count = mBufferSize; 331 final StatusBarNotification[] a 332 = new StatusBarNotification[Math.min(count, mBuffer.size())]; 333 Iterator<StatusBarNotification> iter = descendingIterator(); 334 int i=0; 335 while (iter.hasNext() && i < count) { 336 a[i++] = iter.next(); 337 } 338 return a; 339 } 340 341 } 342 343 private void readPolicyXml(InputStream stream, boolean forRestore) 344 throws XmlPullParserException, NumberFormatException, IOException { 345 final XmlPullParser parser = Xml.newPullParser(); 346 parser.setInput(stream, StandardCharsets.UTF_8.name()); 347 348 int type; 349 String tag; 350 int version = DB_VERSION; 351 while ((type = parser.next()) != END_DOCUMENT) { 352 tag = parser.getName(); 353 if (type == START_TAG) { 354 if (TAG_NOTIFICATION_POLICY.equals(tag)) { 355 version = Integer.parseInt( 356 parser.getAttributeValue(null, ATTR_VERSION)); 357 } else if (TAG_BLOCKED_PKGS.equals(tag)) { 358 while ((type = parser.next()) != END_DOCUMENT) { 359 tag = parser.getName(); 360 if (TAG_PACKAGE.equals(tag)) { 361 mBlockedPackages.add( 362 parser.getAttributeValue(null, ATTR_NAME)); 363 } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) { 364 break; 365 } 366 } 367 } 368 } 369 mZenModeHelper.readXml(parser, forRestore); 370 mRankingHelper.readXml(parser, forRestore); 371 } 372 } 373 374 private void loadPolicyFile() { 375 if (DBG) Slog.d(TAG, "loadPolicyFile"); 376 synchronized(mPolicyFile) { 377 mBlockedPackages.clear(); 378 379 FileInputStream infile = null; 380 try { 381 infile = mPolicyFile.openRead(); 382 readPolicyXml(infile, false /*forRestore*/); 383 } catch (FileNotFoundException e) { 384 // No data yet 385 } catch (IOException e) { 386 Log.wtf(TAG, "Unable to read notification policy", e); 387 } catch (NumberFormatException e) { 388 Log.wtf(TAG, "Unable to parse notification policy", e); 389 } catch (XmlPullParserException e) { 390 Log.wtf(TAG, "Unable to parse notification policy", e); 391 } finally { 392 IoUtils.closeQuietly(infile); 393 } 394 } 395 } 396 397 public void savePolicyFile() { 398 mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE); 399 mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE); 400 } 401 402 private void handleSavePolicyFile() { 403 if (DBG) Slog.d(TAG, "handleSavePolicyFile"); 404 synchronized (mPolicyFile) { 405 final FileOutputStream stream; 406 try { 407 stream = mPolicyFile.startWrite(); 408 } catch (IOException e) { 409 Slog.w(TAG, "Failed to save policy file", e); 410 return; 411 } 412 413 try { 414 writePolicyXml(stream, false /*forBackup*/); 415 mPolicyFile.finishWrite(stream); 416 } catch (IOException e) { 417 Slog.w(TAG, "Failed to save policy file, restoring backup", e); 418 mPolicyFile.failWrite(stream); 419 } 420 } 421 BackupManager.dataChanged(getContext().getPackageName()); 422 } 423 424 private void writePolicyXml(OutputStream stream, boolean forBackup) throws IOException { 425 final XmlSerializer out = new FastXmlSerializer(); 426 out.setOutput(stream, StandardCharsets.UTF_8.name()); 427 out.startDocument(null, true); 428 out.startTag(null, TAG_NOTIFICATION_POLICY); 429 out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION)); 430 mZenModeHelper.writeXml(out, forBackup); 431 mRankingHelper.writeXml(out, forBackup); 432 out.endTag(null, TAG_NOTIFICATION_POLICY); 433 out.endDocument(); 434 } 435 436 /** Use this when you actually want to post a notification or toast. 437 * 438 * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*(). 439 */ 440 private boolean noteNotificationOp(String pkg, int uid) { 441 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg) 442 != AppOpsManager.MODE_ALLOWED) { 443 Slog.v(TAG, "notifications are disabled by AppOps for " + pkg); 444 return false; 445 } 446 return true; 447 } 448 449 /** Use this to check if a package can post a notification or toast. */ 450 private boolean checkNotificationOp(String pkg, int uid) { 451 return mAppOps.checkOp(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg) 452 == AppOpsManager.MODE_ALLOWED; 453 } 454 455 private static final class ToastRecord 456 { 457 final int pid; 458 final String pkg; 459 final ITransientNotification callback; 460 int duration; 461 462 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration) 463 { 464 this.pid = pid; 465 this.pkg = pkg; 466 this.callback = callback; 467 this.duration = duration; 468 } 469 470 void update(int duration) { 471 this.duration = duration; 472 } 473 474 void dump(PrintWriter pw, String prefix, DumpFilter filter) { 475 if (filter != null && !filter.matches(pkg)) return; 476 pw.println(prefix + this); 477 } 478 479 @Override 480 public final String toString() 481 { 482 return "ToastRecord{" 483 + Integer.toHexString(System.identityHashCode(this)) 484 + " pkg=" + pkg 485 + " callback=" + callback 486 + " duration=" + duration; 487 } 488 } 489 490 private final NotificationDelegate mNotificationDelegate = new NotificationDelegate() { 491 492 @Override 493 public void onSetDisabled(int status) { 494 synchronized (mNotificationList) { 495 mDisableNotificationEffects = 496 (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0; 497 if (disableNotificationEffects(null) != null) { 498 // cancel whatever's going on 499 long identity = Binder.clearCallingIdentity(); 500 try { 501 final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); 502 if (player != null) { 503 player.stopAsync(); 504 } 505 } catch (RemoteException e) { 506 } finally { 507 Binder.restoreCallingIdentity(identity); 508 } 509 510 identity = Binder.clearCallingIdentity(); 511 try { 512 mVibrator.cancel(); 513 } finally { 514 Binder.restoreCallingIdentity(identity); 515 } 516 } 517 } 518 } 519 520 @Override 521 public void onClearAll(int callingUid, int callingPid, int userId) { 522 synchronized (mNotificationList) { 523 cancelAllLocked(callingUid, callingPid, userId, REASON_DELEGATE_CANCEL_ALL, null, 524 /*includeCurrentProfiles*/ true); 525 } 526 } 527 528 @Override 529 public void onNotificationClick(int callingUid, int callingPid, String key) { 530 synchronized (mNotificationList) { 531 NotificationRecord r = mNotificationsByKey.get(key); 532 if (r == null) { 533 Log.w(TAG, "No notification with key: " + key); 534 return; 535 } 536 final long now = System.currentTimeMillis(); 537 EventLogTags.writeNotificationClicked(key, 538 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now)); 539 540 StatusBarNotification sbn = r.sbn; 541 cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(), 542 sbn.getId(), Notification.FLAG_AUTO_CANCEL, 543 Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(), 544 REASON_DELEGATE_CLICK, null); 545 } 546 } 547 548 @Override 549 public void onNotificationActionClick(int callingUid, int callingPid, String key, 550 int actionIndex) { 551 synchronized (mNotificationList) { 552 NotificationRecord r = mNotificationsByKey.get(key); 553 if (r == null) { 554 Log.w(TAG, "No notification with key: " + key); 555 return; 556 } 557 final long now = System.currentTimeMillis(); 558 EventLogTags.writeNotificationActionClicked(key, actionIndex, 559 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now)); 560 // TODO: Log action click via UsageStats. 561 } 562 } 563 564 @Override 565 public void onNotificationClear(int callingUid, int callingPid, 566 String pkg, String tag, int id, int userId) { 567 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 568 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE, 569 true, userId, REASON_DELEGATE_CANCEL, null); 570 } 571 572 @Override 573 public void onPanelRevealed(boolean clearEffects, int items) { 574 EventLogTags.writeNotificationPanelRevealed(items); 575 if (clearEffects) { 576 clearEffects(); 577 } 578 } 579 580 @Override 581 public void onPanelHidden() { 582 EventLogTags.writeNotificationPanelHidden(); 583 } 584 585 @Override 586 public void clearEffects() { 587 synchronized (mNotificationList) { 588 if (DBG) Slog.d(TAG, "clearEffects"); 589 590 // sound 591 mSoundNotificationKey = null; 592 593 long identity = Binder.clearCallingIdentity(); 594 try { 595 final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); 596 if (player != null) { 597 player.stopAsync(); 598 } 599 } catch (RemoteException e) { 600 } finally { 601 Binder.restoreCallingIdentity(identity); 602 } 603 604 // vibrate 605 mVibrateNotificationKey = null; 606 identity = Binder.clearCallingIdentity(); 607 try { 608 mVibrator.cancel(); 609 } finally { 610 Binder.restoreCallingIdentity(identity); 611 } 612 613 // light 614 mLights.clear(); 615 updateLightsLocked(); 616 } 617 } 618 619 @Override 620 public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id, 621 int uid, int initialPid, String message, int userId) { 622 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id 623 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")"); 624 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId, 625 REASON_DELEGATE_ERROR, null); 626 long ident = Binder.clearCallingIdentity(); 627 try { 628 ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg, 629 "Bad notification posted from package " + pkg 630 + ": " + message); 631 } catch (RemoteException e) { 632 } 633 Binder.restoreCallingIdentity(ident); 634 } 635 636 @Override 637 public void onNotificationVisibilityChanged(NotificationVisibility[] newlyVisibleKeys, 638 NotificationVisibility[] noLongerVisibleKeys) { 639 synchronized (mNotificationList) { 640 for (NotificationVisibility nv : newlyVisibleKeys) { 641 NotificationRecord r = mNotificationsByKey.get(nv.key); 642 if (r == null) continue; 643 r.setVisibility(true, nv.rank); 644 nv.recycle(); 645 } 646 // Note that we might receive this event after notifications 647 // have already left the system, e.g. after dismissing from the 648 // shade. Hence not finding notifications in 649 // mNotificationsByKey is not an exceptional condition. 650 for (NotificationVisibility nv : noLongerVisibleKeys) { 651 NotificationRecord r = mNotificationsByKey.get(nv.key); 652 if (r == null) continue; 653 r.setVisibility(false, nv.rank); 654 nv.recycle(); 655 } 656 } 657 } 658 659 @Override 660 public void onNotificationExpansionChanged(String key, 661 boolean userAction, boolean expanded) { 662 synchronized (mNotificationList) { 663 NotificationRecord r = mNotificationsByKey.get(key); 664 if (r != null) { 665 r.stats.onExpansionChanged(userAction, expanded); 666 final long now = System.currentTimeMillis(); 667 EventLogTags.writeNotificationExpansion(key, 668 userAction ? 1 : 0, expanded ? 1 : 0, 669 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now)); 670 } 671 } 672 } 673 }; 674 675 private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() { 676 @Override 677 public void onReceive(Context context, Intent intent) { 678 String action = intent.getAction(); 679 if (action == null) { 680 return; 681 } 682 683 boolean queryRestart = false; 684 boolean queryRemove = false; 685 boolean packageChanged = false; 686 boolean cancelNotifications = true; 687 688 if (action.equals(Intent.ACTION_PACKAGE_ADDED) 689 || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED)) 690 || action.equals(Intent.ACTION_PACKAGE_RESTARTED) 691 || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED)) 692 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART)) 693 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { 694 int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 695 UserHandle.USER_ALL); 696 String pkgList[] = null; 697 boolean queryReplace = queryRemove && 698 intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); 699 if (DBG) Slog.i(TAG, "action=" + action + " queryReplace=" + queryReplace); 700 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { 701 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); 702 } else if (queryRestart) { 703 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES); 704 } else { 705 Uri uri = intent.getData(); 706 if (uri == null) { 707 return; 708 } 709 String pkgName = uri.getSchemeSpecificPart(); 710 if (pkgName == null) { 711 return; 712 } 713 if (packageChanged) { 714 // We cancel notifications for packages which have just been disabled 715 try { 716 final IPackageManager pm = AppGlobals.getPackageManager(); 717 final int enabled = pm.getApplicationEnabledSetting(pkgName, 718 changeUserId != UserHandle.USER_ALL ? changeUserId : 719 UserHandle.USER_OWNER); 720 if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED 721 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) { 722 cancelNotifications = false; 723 } 724 } catch (IllegalArgumentException e) { 725 // Package doesn't exist; probably racing with uninstall. 726 // cancelNotifications is already true, so nothing to do here. 727 if (DBG) { 728 Slog.i(TAG, "Exception trying to look up app enabled setting", e); 729 } 730 } catch (RemoteException e) { 731 // Failed to talk to PackageManagerService Should never happen! 732 } 733 } 734 pkgList = new String[]{pkgName}; 735 } 736 737 if (pkgList != null && (pkgList.length > 0)) { 738 for (String pkgName : pkgList) { 739 if (cancelNotifications) { 740 cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart, 741 changeUserId, REASON_PACKAGE_CHANGED, null); 742 } 743 } 744 } 745 mListeners.onPackagesChanged(queryReplace, pkgList); 746 mConditionProviders.onPackagesChanged(queryReplace, pkgList); 747 mRankingHelper.onPackagesChanged(queryReplace, pkgList); 748 } 749 } 750 }; 751 752 private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 753 @Override 754 public void onReceive(Context context, Intent intent) { 755 String action = intent.getAction(); 756 757 if (action.equals(Intent.ACTION_SCREEN_ON)) { 758 // Keep track of screen on/off state, but do not turn off the notification light 759 // until user passes through the lock screen or views the notification. 760 mScreenOn = true; 761 updateNotificationPulse(); 762 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { 763 mScreenOn = false; 764 updateNotificationPulse(); 765 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) { 766 mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK 767 .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE)); 768 updateNotificationPulse(); 769 } else if (action.equals(Intent.ACTION_USER_STOPPED)) { 770 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); 771 if (userHandle >= 0) { 772 cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle, 773 REASON_USER_STOPPED, null); 774 } 775 } else if (action.equals(Intent.ACTION_USER_PRESENT)) { 776 // turn off LED when user passes through lock screen 777 mNotificationLight.turnOff(); 778 mStatusBar.notificationLightOff(); 779 } else if (action.equals(Intent.ACTION_USER_SWITCHED)) { 780 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); 781 // reload per-user settings 782 mSettingsObserver.update(null); 783 mUserProfiles.updateCache(context); 784 // Refresh managed services 785 mConditionProviders.onUserSwitched(user); 786 mListeners.onUserSwitched(user); 787 mZenModeHelper.onUserSwitched(user); 788 } else if (action.equals(Intent.ACTION_USER_ADDED)) { 789 mUserProfiles.updateCache(context); 790 } else if (action.equals(Intent.ACTION_USER_REMOVED)) { 791 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); 792 mZenModeHelper.onUserRemoved(user); 793 } 794 } 795 }; 796 797 private final class SettingsObserver extends ContentObserver { 798 private final Uri NOTIFICATION_LIGHT_PULSE_URI 799 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE); 800 801 SettingsObserver(Handler handler) { 802 super(handler); 803 } 804 805 void observe() { 806 ContentResolver resolver = getContext().getContentResolver(); 807 resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI, 808 false, this, UserHandle.USER_ALL); 809 update(null); 810 } 811 812 @Override public void onChange(boolean selfChange, Uri uri) { 813 update(uri); 814 } 815 816 public void update(Uri uri) { 817 ContentResolver resolver = getContext().getContentResolver(); 818 if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) { 819 boolean pulseEnabled = Settings.System.getInt(resolver, 820 Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0; 821 if (mNotificationPulseEnabled != pulseEnabled) { 822 mNotificationPulseEnabled = pulseEnabled; 823 updateNotificationPulse(); 824 } 825 } 826 } 827 } 828 829 private SettingsObserver mSettingsObserver; 830 private ZenModeHelper mZenModeHelper; 831 832 private final Runnable mBuzzBeepBlinked = new Runnable() { 833 @Override 834 public void run() { 835 mStatusBar.buzzBeepBlinked(); 836 } 837 }; 838 839 static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) { 840 int[] ar = r.getIntArray(resid); 841 if (ar == null) { 842 return def; 843 } 844 final int len = ar.length > maxlen ? maxlen : ar.length; 845 long[] out = new long[len]; 846 for (int i=0; i<len; i++) { 847 out[i] = ar[i]; 848 } 849 return out; 850 } 851 852 public NotificationManagerService(Context context) { 853 super(context); 854 } 855 856 @Override 857 public void onStart() { 858 Resources resources = getContext().getResources(); 859 860 mAm = ActivityManagerNative.getDefault(); 861 mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE); 862 mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE); 863 mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class); 864 865 mHandler = new WorkerHandler(); 866 mRankingThread.start(); 867 String[] extractorNames; 868 try { 869 extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors); 870 } catch (Resources.NotFoundException e) { 871 extractorNames = new String[0]; 872 } 873 mUsageStats = new NotificationUsageStats(getContext()); 874 mRankingHelper = new RankingHelper(getContext(), 875 new RankingWorkerHandler(mRankingThread.getLooper()), 876 mUsageStats, 877 extractorNames); 878 mConditionProviders = new ConditionProviders(getContext(), mHandler, mUserProfiles); 879 mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders); 880 mZenModeHelper.addCallback(new ZenModeHelper.Callback() { 881 @Override 882 public void onConfigChanged() { 883 savePolicyFile(); 884 } 885 886 @Override 887 void onZenModeChanged() { 888 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED); 889 synchronized(mNotificationList) { 890 updateInterruptionFilterLocked(); 891 } 892 } 893 894 @Override 895 void onPolicyChanged() { 896 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED); 897 } 898 }); 899 final File systemDir = new File(Environment.getDataDirectory(), "system"); 900 mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml")); 901 902 importOldBlockDb(); 903 904 mListeners = new NotificationListeners(); 905 mStatusBar = getLocalService(StatusBarManagerInternal.class); 906 mStatusBar.setNotificationDelegate(mNotificationDelegate); 907 908 final LightsManager lights = getLocalService(LightsManager.class); 909 mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS); 910 mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION); 911 912 mDefaultNotificationColor = resources.getColor( 913 R.color.config_defaultNotificationColor); 914 mDefaultNotificationLedOn = resources.getInteger( 915 R.integer.config_defaultNotificationLedOn); 916 mDefaultNotificationLedOff = resources.getInteger( 917 R.integer.config_defaultNotificationLedOff); 918 919 mDefaultVibrationPattern = getLongArray(resources, 920 R.array.config_defaultNotificationVibePattern, 921 VIBRATE_PATTERN_MAXLEN, 922 DEFAULT_VIBRATE_PATTERN); 923 924 mFallbackVibrationPattern = getLongArray(resources, 925 R.array.config_notificationFallbackVibePattern, 926 VIBRATE_PATTERN_MAXLEN, 927 DEFAULT_VIBRATE_PATTERN); 928 929 mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight); 930 931 // Don't start allowing notifications until the setup wizard has run once. 932 // After that, including subsequent boots, init with notifications turned on. 933 // This works on the first boot because the setup wizard will toggle this 934 // flag at least once and we'll go back to 0 after that. 935 if (0 == Settings.Global.getInt(getContext().getContentResolver(), 936 Settings.Global.DEVICE_PROVISIONED, 0)) { 937 mDisableNotificationEffects = true; 938 } 939 mZenModeHelper.initZenMode(); 940 mInterruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter(); 941 942 mUserProfiles.updateCache(getContext()); 943 listenForCallState(); 944 945 // register for various Intents 946 IntentFilter filter = new IntentFilter(); 947 filter.addAction(Intent.ACTION_SCREEN_ON); 948 filter.addAction(Intent.ACTION_SCREEN_OFF); 949 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); 950 filter.addAction(Intent.ACTION_USER_PRESENT); 951 filter.addAction(Intent.ACTION_USER_STOPPED); 952 filter.addAction(Intent.ACTION_USER_SWITCHED); 953 filter.addAction(Intent.ACTION_USER_ADDED); 954 filter.addAction(Intent.ACTION_USER_REMOVED); 955 getContext().registerReceiver(mIntentReceiver, filter); 956 957 IntentFilter pkgFilter = new IntentFilter(); 958 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED); 959 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 960 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); 961 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); 962 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); 963 pkgFilter.addDataScheme("package"); 964 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null, 965 null); 966 967 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); 968 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null, 969 null); 970 971 mSettingsObserver = new SettingsObserver(mHandler); 972 973 mArchive = new Archive(resources.getInteger( 974 R.integer.config_notificationServiceArchiveSize)); 975 976 publishBinderService(Context.NOTIFICATION_SERVICE, mService); 977 publishLocalService(NotificationManagerInternal.class, mInternalService); 978 } 979 980 private void sendRegisteredOnlyBroadcast(String action) { 981 getContext().sendBroadcastAsUser(new Intent(action) 982 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL, null); 983 } 984 985 /** 986 * Read the old XML-based app block database and import those blockages into the AppOps system. 987 */ 988 private void importOldBlockDb() { 989 loadPolicyFile(); 990 991 PackageManager pm = getContext().getPackageManager(); 992 for (String pkg : mBlockedPackages) { 993 PackageInfo info = null; 994 try { 995 info = pm.getPackageInfo(pkg, 0); 996 setNotificationsEnabledForPackageImpl(pkg, info.applicationInfo.uid, false); 997 } catch (NameNotFoundException e) { 998 // forget you 999 } 1000 } 1001 mBlockedPackages.clear(); 1002 } 1003 1004 @Override 1005 public void onBootPhase(int phase) { 1006 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { 1007 // no beeping until we're basically done booting 1008 mSystemReady = true; 1009 1010 // Grab our optional AudioService 1011 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); 1012 mAudioManagerInternal = getLocalService(AudioManagerInternal.class); 1013 mZenModeHelper.onSystemReady(); 1014 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { 1015 // This observer will force an update when observe is called, causing us to 1016 // bind to listener services. 1017 mSettingsObserver.observe(); 1018 mListeners.onBootPhaseAppsCanStart(); 1019 mConditionProviders.onBootPhaseAppsCanStart(); 1020 } 1021 } 1022 1023 void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) { 1024 Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg); 1025 1026 mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg, 1027 enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED); 1028 1029 // Now, cancel any outstanding notifications that are part of a just-disabled app 1030 if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) { 1031 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid), 1032 REASON_PACKAGE_BANNED, null); 1033 } 1034 } 1035 1036 private void updateListenerHintsLocked() { 1037 final int hints = mListenersDisablingEffects.isEmpty() ? 0 : HINT_HOST_DISABLE_EFFECTS; 1038 if (hints == mListenerHints) return; 1039 ZenLog.traceListenerHintsChanged(mListenerHints, hints, mListenersDisablingEffects.size()); 1040 mListenerHints = hints; 1041 scheduleListenerHintsChanged(hints); 1042 } 1043 1044 private void updateEffectsSuppressorLocked() { 1045 final ComponentName suppressor = !mListenersDisablingEffects.isEmpty() 1046 ? mListenersDisablingEffects.valueAt(0).component : null; 1047 if (Objects.equals(suppressor, mEffectsSuppressor)) return; 1048 ZenLog.traceEffectsSuppressorChanged(mEffectsSuppressor, suppressor); 1049 mEffectsSuppressor = suppressor; 1050 mZenModeHelper.setEffectsSuppressed(suppressor != null); 1051 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED); 1052 } 1053 1054 private void updateInterruptionFilterLocked() { 1055 int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter(); 1056 if (interruptionFilter == mInterruptionFilter) return; 1057 mInterruptionFilter = interruptionFilter; 1058 scheduleInterruptionFilterChanged(interruptionFilter); 1059 } 1060 1061 private final IBinder mService = new INotificationManager.Stub() { 1062 // Toasts 1063 // ============================================================================ 1064 1065 @Override 1066 public void enqueueToast(String pkg, ITransientNotification callback, int duration) 1067 { 1068 if (DBG) { 1069 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback 1070 + " duration=" + duration); 1071 } 1072 1073 if (pkg == null || callback == null) { 1074 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback); 1075 return ; 1076 } 1077 1078 final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg)); 1079 1080 if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) { 1081 if (!isSystemToast) { 1082 Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request."); 1083 return; 1084 } 1085 } 1086 1087 synchronized (mToastQueue) { 1088 int callingPid = Binder.getCallingPid(); 1089 long callingId = Binder.clearCallingIdentity(); 1090 try { 1091 ToastRecord record; 1092 int index = indexOfToastLocked(pkg, callback); 1093 // If it's already in the queue, we update it in place, we don't 1094 // move it to the end of the queue. 1095 if (index >= 0) { 1096 record = mToastQueue.get(index); 1097 record.update(duration); 1098 } else { 1099 // Limit the number of toasts that any given package except the android 1100 // package can enqueue. Prevents DOS attacks and deals with leaks. 1101 if (!isSystemToast) { 1102 int count = 0; 1103 final int N = mToastQueue.size(); 1104 for (int i=0; i<N; i++) { 1105 final ToastRecord r = mToastQueue.get(i); 1106 if (r.pkg.equals(pkg)) { 1107 count++; 1108 if (count >= MAX_PACKAGE_NOTIFICATIONS) { 1109 Slog.e(TAG, "Package has already posted " + count 1110 + " toasts. Not showing more. Package=" + pkg); 1111 return; 1112 } 1113 } 1114 } 1115 } 1116 1117 record = new ToastRecord(callingPid, pkg, callback, duration); 1118 mToastQueue.add(record); 1119 index = mToastQueue.size() - 1; 1120 keepProcessAliveLocked(callingPid); 1121 } 1122 // If it's at index 0, it's the current toast. It doesn't matter if it's 1123 // new or just been updated. Call back and tell it to show itself. 1124 // If the callback fails, this will remove it from the list, so don't 1125 // assume that it's valid after this. 1126 if (index == 0) { 1127 showNextToastLocked(); 1128 } 1129 } finally { 1130 Binder.restoreCallingIdentity(callingId); 1131 } 1132 } 1133 } 1134 1135 @Override 1136 public void cancelToast(String pkg, ITransientNotification callback) { 1137 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback); 1138 1139 if (pkg == null || callback == null) { 1140 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback); 1141 return ; 1142 } 1143 1144 synchronized (mToastQueue) { 1145 long callingId = Binder.clearCallingIdentity(); 1146 try { 1147 int index = indexOfToastLocked(pkg, callback); 1148 if (index >= 0) { 1149 cancelToastLocked(index); 1150 } else { 1151 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg 1152 + " callback=" + callback); 1153 } 1154 } finally { 1155 Binder.restoreCallingIdentity(callingId); 1156 } 1157 } 1158 } 1159 1160 @Override 1161 public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id, 1162 Notification notification, int[] idOut, int userId) throws RemoteException { 1163 enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(), 1164 Binder.getCallingPid(), tag, id, notification, idOut, userId); 1165 } 1166 1167 @Override 1168 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) { 1169 checkCallerIsSystemOrSameApp(pkg); 1170 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 1171 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg); 1172 // Don't allow client applications to cancel foreground service notis. 1173 cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0, 1174 Binder.getCallingUid() == Process.SYSTEM_UID 1175 ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_NOMAN_CANCEL, 1176 null); 1177 } 1178 1179 @Override 1180 public void cancelAllNotifications(String pkg, int userId) { 1181 checkCallerIsSystemOrSameApp(pkg); 1182 1183 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 1184 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg); 1185 1186 // Calling from user space, don't allow the canceling of actively 1187 // running foreground services. 1188 cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(), 1189 pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId, 1190 REASON_NOMAN_CANCEL_ALL, null); 1191 } 1192 1193 @Override 1194 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) { 1195 checkCallerIsSystem(); 1196 1197 setNotificationsEnabledForPackageImpl(pkg, uid, enabled); 1198 } 1199 1200 /** 1201 * Use this when you just want to know if notifications are OK for this package. 1202 */ 1203 @Override 1204 public boolean areNotificationsEnabledForPackage(String pkg, int uid) { 1205 checkCallerIsSystem(); 1206 return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg) 1207 == AppOpsManager.MODE_ALLOWED); 1208 } 1209 1210 @Override 1211 public void setPackagePriority(String pkg, int uid, int priority) { 1212 checkCallerIsSystem(); 1213 mRankingHelper.setPackagePriority(pkg, uid, priority); 1214 savePolicyFile(); 1215 } 1216 1217 @Override 1218 public int getPackagePriority(String pkg, int uid) { 1219 checkCallerIsSystem(); 1220 return mRankingHelper.getPackagePriority(pkg, uid); 1221 } 1222 1223 @Override 1224 public void setPackagePeekable(String pkg, int uid, boolean peekable) { 1225 checkCallerIsSystem(); 1226 1227 mRankingHelper.setPackagePeekable(pkg, uid, peekable); 1228 } 1229 1230 @Override 1231 public boolean getPackagePeekable(String pkg, int uid) { 1232 checkCallerIsSystem(); 1233 return mRankingHelper.getPackagePeekable(pkg, uid); 1234 } 1235 1236 @Override 1237 public void setPackageVisibilityOverride(String pkg, int uid, int visibility) { 1238 checkCallerIsSystem(); 1239 mRankingHelper.setPackageVisibilityOverride(pkg, uid, visibility); 1240 savePolicyFile(); 1241 } 1242 1243 @Override 1244 public int getPackageVisibilityOverride(String pkg, int uid) { 1245 checkCallerIsSystem(); 1246 return mRankingHelper.getPackageVisibilityOverride(pkg, uid); 1247 } 1248 1249 /** 1250 * System-only API for getting a list of current (i.e. not cleared) notifications. 1251 * 1252 * Requires ACCESS_NOTIFICATIONS which is signature|system. 1253 * @returns A list of all the notifications, in natural order. 1254 */ 1255 @Override 1256 public StatusBarNotification[] getActiveNotifications(String callingPkg) { 1257 // enforce() will ensure the calling uid has the correct permission 1258 getContext().enforceCallingOrSelfPermission( 1259 android.Manifest.permission.ACCESS_NOTIFICATIONS, 1260 "NotificationManagerService.getActiveNotifications"); 1261 1262 StatusBarNotification[] tmp = null; 1263 int uid = Binder.getCallingUid(); 1264 1265 // noteOp will check to make sure the callingPkg matches the uid 1266 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg) 1267 == AppOpsManager.MODE_ALLOWED) { 1268 synchronized (mNotificationList) { 1269 tmp = new StatusBarNotification[mNotificationList.size()]; 1270 final int N = mNotificationList.size(); 1271 for (int i=0; i<N; i++) { 1272 tmp[i] = mNotificationList.get(i).sbn; 1273 } 1274 } 1275 } 1276 return tmp; 1277 } 1278 1279 /** 1280 * Public API for getting a list of current notifications for the calling package/uid. 1281 * 1282 * @returns A list of all the package's notifications, in natural order. 1283 */ 1284 @Override 1285 public ParceledListSlice<StatusBarNotification> getAppActiveNotifications(String pkg, 1286 int incomingUserId) { 1287 checkCallerIsSystemOrSameApp(pkg); 1288 int userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 1289 Binder.getCallingUid(), incomingUserId, true, false, 1290 "getAppActiveNotifications", pkg); 1291 1292 final int N = mNotificationList.size(); 1293 final ArrayList<StatusBarNotification> list = new ArrayList<StatusBarNotification>(N); 1294 1295 synchronized (mNotificationList) { 1296 for (int i = 0; i < N; i++) { 1297 final StatusBarNotification sbn = mNotificationList.get(i).sbn; 1298 if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId) { 1299 // We could pass back a cloneLight() but clients might get confused and 1300 // try to send this thing back to notify() again, which would not work 1301 // very well. 1302 final StatusBarNotification sbnOut = new StatusBarNotification( 1303 sbn.getPackageName(), 1304 sbn.getOpPkg(), 1305 sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(), 1306 0, // hide score from apps 1307 sbn.getNotification().clone(), 1308 sbn.getUser(), sbn.getPostTime()); 1309 list.add(sbnOut); 1310 } 1311 } 1312 } 1313 1314 return new ParceledListSlice<StatusBarNotification>(list); 1315 } 1316 1317 /** 1318 * System-only API for getting a list of recent (cleared, no longer shown) notifications. 1319 * 1320 * Requires ACCESS_NOTIFICATIONS which is signature|system. 1321 */ 1322 @Override 1323 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) { 1324 // enforce() will ensure the calling uid has the correct permission 1325 getContext().enforceCallingOrSelfPermission( 1326 android.Manifest.permission.ACCESS_NOTIFICATIONS, 1327 "NotificationManagerService.getHistoricalNotifications"); 1328 1329 StatusBarNotification[] tmp = null; 1330 int uid = Binder.getCallingUid(); 1331 1332 // noteOp will check to make sure the callingPkg matches the uid 1333 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg) 1334 == AppOpsManager.MODE_ALLOWED) { 1335 synchronized (mArchive) { 1336 tmp = mArchive.getArray(count); 1337 } 1338 } 1339 return tmp; 1340 } 1341 1342 /** 1343 * Register a listener binder directly with the notification manager. 1344 * 1345 * Only works with system callers. Apps should extend 1346 * {@link android.service.notification.NotificationListenerService}. 1347 */ 1348 @Override 1349 public void registerListener(final INotificationListener listener, 1350 final ComponentName component, final int userid) { 1351 enforceSystemOrSystemUI("INotificationManager.registerListener"); 1352 mListeners.registerService(listener, component, userid); 1353 } 1354 1355 /** 1356 * Remove a listener binder directly 1357 */ 1358 @Override 1359 public void unregisterListener(INotificationListener listener, int userid) { 1360 mListeners.unregisterService(listener, userid); 1361 } 1362 1363 /** 1364 * Allow an INotificationListener to simulate a "clear all" operation. 1365 * 1366 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications} 1367 * 1368 * @param token The binder for the listener, to check that the caller is allowed 1369 */ 1370 @Override 1371 public void cancelNotificationsFromListener(INotificationListener token, String[] keys) { 1372 final int callingUid = Binder.getCallingUid(); 1373 final int callingPid = Binder.getCallingPid(); 1374 long identity = Binder.clearCallingIdentity(); 1375 try { 1376 synchronized (mNotificationList) { 1377 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1378 if (keys != null) { 1379 final int N = keys.length; 1380 for (int i = 0; i < N; i++) { 1381 NotificationRecord r = mNotificationsByKey.get(keys[i]); 1382 if (r == null) continue; 1383 final int userId = r.sbn.getUserId(); 1384 if (userId != info.userid && userId != UserHandle.USER_ALL && 1385 !mUserProfiles.isCurrentProfile(userId)) { 1386 throw new SecurityException("Disallowed call from listener: " 1387 + info.service); 1388 } 1389 cancelNotificationFromListenerLocked(info, callingUid, callingPid, 1390 r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(), 1391 userId); 1392 } 1393 } else { 1394 cancelAllLocked(callingUid, callingPid, info.userid, 1395 REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles()); 1396 } 1397 } 1398 } finally { 1399 Binder.restoreCallingIdentity(identity); 1400 } 1401 } 1402 1403 @Override 1404 public void setNotificationsShownFromListener(INotificationListener token, String[] keys) { 1405 long identity = Binder.clearCallingIdentity(); 1406 try { 1407 synchronized (mNotificationList) { 1408 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1409 if (keys != null) { 1410 final int N = keys.length; 1411 for (int i = 0; i < N; i++) { 1412 NotificationRecord r = mNotificationsByKey.get(keys[i]); 1413 if (r == null) continue; 1414 final int userId = r.sbn.getUserId(); 1415 if (userId != info.userid && userId != UserHandle.USER_ALL && 1416 !mUserProfiles.isCurrentProfile(userId)) { 1417 throw new SecurityException("Disallowed call from listener: " 1418 + info.service); 1419 } 1420 if (!r.isSeen()) { 1421 if (DBG) Slog.d(TAG, "Marking notification as seen " + keys[i]); 1422 mAppUsageStats.reportEvent(r.sbn.getPackageName(), 1423 userId == UserHandle.USER_ALL ? UserHandle.USER_OWNER 1424 : userId, 1425 UsageEvents.Event.USER_INTERACTION); 1426 r.setSeen(); 1427 } 1428 } 1429 } 1430 } 1431 } finally { 1432 Binder.restoreCallingIdentity(identity); 1433 } 1434 } 1435 1436 private void cancelNotificationFromListenerLocked(ManagedServiceInfo info, 1437 int callingUid, int callingPid, String pkg, String tag, int id, int userId) { 1438 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 1439 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE, 1440 true, 1441 userId, REASON_LISTENER_CANCEL, info); 1442 } 1443 1444 /** 1445 * Allow an INotificationListener to simulate clearing (dismissing) a single notification. 1446 * 1447 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear} 1448 * 1449 * @param token The binder for the listener, to check that the caller is allowed 1450 */ 1451 @Override 1452 public void cancelNotificationFromListener(INotificationListener token, String pkg, 1453 String tag, int id) { 1454 final int callingUid = Binder.getCallingUid(); 1455 final int callingPid = Binder.getCallingPid(); 1456 long identity = Binder.clearCallingIdentity(); 1457 try { 1458 synchronized (mNotificationList) { 1459 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1460 if (info.supportsProfiles()) { 1461 Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) " 1462 + "from " + info.component 1463 + " use cancelNotification(key) instead."); 1464 } else { 1465 cancelNotificationFromListenerLocked(info, callingUid, callingPid, 1466 pkg, tag, id, info.userid); 1467 } 1468 } 1469 } finally { 1470 Binder.restoreCallingIdentity(identity); 1471 } 1472 } 1473 1474 /** 1475 * Allow an INotificationListener to request the list of outstanding notifications seen by 1476 * the current user. Useful when starting up, after which point the listener callbacks 1477 * should be used. 1478 * 1479 * @param token The binder for the listener, to check that the caller is allowed 1480 * @param keys An array of notification keys to fetch, or null to fetch everything 1481 * @returns The return value will contain the notifications specified in keys, in that 1482 * order, or if keys is null, all the notifications, in natural order. 1483 */ 1484 @Override 1485 public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener( 1486 INotificationListener token, String[] keys, int trim) { 1487 synchronized (mNotificationList) { 1488 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1489 final boolean getKeys = keys != null; 1490 final int N = getKeys ? keys.length : mNotificationList.size(); 1491 final ArrayList<StatusBarNotification> list 1492 = new ArrayList<StatusBarNotification>(N); 1493 for (int i=0; i<N; i++) { 1494 final NotificationRecord r = getKeys 1495 ? mNotificationsByKey.get(keys[i]) 1496 : mNotificationList.get(i); 1497 if (r == null) continue; 1498 StatusBarNotification sbn = r.sbn; 1499 if (!isVisibleToListener(sbn, info)) continue; 1500 StatusBarNotification sbnToSend = 1501 (trim == TRIM_FULL) ? sbn : sbn.cloneLight(); 1502 list.add(sbnToSend); 1503 } 1504 return new ParceledListSlice<StatusBarNotification>(list); 1505 } 1506 } 1507 1508 @Override 1509 public void requestHintsFromListener(INotificationListener token, int hints) { 1510 final long identity = Binder.clearCallingIdentity(); 1511 try { 1512 synchronized (mNotificationList) { 1513 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1514 final boolean disableEffects = (hints & HINT_HOST_DISABLE_EFFECTS) != 0; 1515 if (disableEffects) { 1516 mListenersDisablingEffects.add(info); 1517 } else { 1518 mListenersDisablingEffects.remove(info); 1519 } 1520 updateListenerHintsLocked(); 1521 updateEffectsSuppressorLocked(); 1522 } 1523 } finally { 1524 Binder.restoreCallingIdentity(identity); 1525 } 1526 } 1527 1528 @Override 1529 public int getHintsFromListener(INotificationListener token) { 1530 synchronized (mNotificationList) { 1531 return mListenerHints; 1532 } 1533 } 1534 1535 @Override 1536 public void requestInterruptionFilterFromListener(INotificationListener token, 1537 int interruptionFilter) throws RemoteException { 1538 final long identity = Binder.clearCallingIdentity(); 1539 try { 1540 synchronized (mNotificationList) { 1541 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1542 mZenModeHelper.requestFromListener(info.component, interruptionFilter); 1543 updateInterruptionFilterLocked(); 1544 } 1545 } finally { 1546 Binder.restoreCallingIdentity(identity); 1547 } 1548 } 1549 1550 @Override 1551 public int getInterruptionFilterFromListener(INotificationListener token) 1552 throws RemoteException { 1553 synchronized (mNotificationLight) { 1554 return mInterruptionFilter; 1555 } 1556 } 1557 1558 @Override 1559 public void setOnNotificationPostedTrimFromListener(INotificationListener token, int trim) 1560 throws RemoteException { 1561 synchronized (mNotificationList) { 1562 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1563 if (info == null) return; 1564 mListeners.setOnNotificationPostedTrimLocked(info, trim); 1565 } 1566 } 1567 1568 @Override 1569 public int getZenMode() { 1570 return mZenModeHelper.getZenMode(); 1571 } 1572 1573 @Override 1574 public ZenModeConfig getZenModeConfig() { 1575 enforceSystemOrSystemUIOrVolume("INotificationManager.getZenModeConfig"); 1576 return mZenModeHelper.getConfig(); 1577 } 1578 1579 @Override 1580 public boolean setZenModeConfig(ZenModeConfig config, String reason) { 1581 checkCallerIsSystem(); 1582 return mZenModeHelper.setConfig(config, reason); 1583 } 1584 1585 @Override 1586 public void setZenMode(int mode, Uri conditionId, String reason) throws RemoteException { 1587 enforceSystemOrSystemUIOrVolume("INotificationManager.setZenMode"); 1588 final long identity = Binder.clearCallingIdentity(); 1589 try { 1590 mZenModeHelper.setManualZenMode(mode, conditionId, reason); 1591 } finally { 1592 Binder.restoreCallingIdentity(identity); 1593 } 1594 } 1595 1596 @Override 1597 public void setInterruptionFilter(String pkg, int filter) throws RemoteException { 1598 enforcePolicyAccess(pkg, "setInterruptionFilter"); 1599 final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1); 1600 if (zen == -1) throw new IllegalArgumentException("Invalid filter: " + filter); 1601 final long identity = Binder.clearCallingIdentity(); 1602 try { 1603 mZenModeHelper.setManualZenMode(zen, null, "setInterruptionFilter"); 1604 } finally { 1605 Binder.restoreCallingIdentity(identity); 1606 } 1607 } 1608 1609 @Override 1610 public void notifyConditions(final String pkg, IConditionProvider provider, 1611 final Condition[] conditions) { 1612 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider); 1613 checkCallerIsSystemOrSameApp(pkg); 1614 mHandler.post(new Runnable() { 1615 @Override 1616 public void run() { 1617 mConditionProviders.notifyConditions(pkg, info, conditions); 1618 } 1619 }); 1620 } 1621 1622 @Override 1623 public void requestZenModeConditions(IConditionListener callback, int relevance) { 1624 enforceSystemOrSystemUIOrVolume("INotificationManager.requestZenModeConditions"); 1625 mZenModeHelper.requestZenModeConditions(callback, relevance); 1626 } 1627 1628 private void enforceSystemOrSystemUIOrVolume(String message) { 1629 if (mAudioManagerInternal != null) { 1630 final int vcuid = mAudioManagerInternal.getVolumeControllerUid(); 1631 if (vcuid > 0 && Binder.getCallingUid() == vcuid) { 1632 return; 1633 } 1634 } 1635 enforceSystemOrSystemUI(message); 1636 } 1637 1638 private void enforceSystemOrSystemUI(String message) { 1639 if (isCallerSystem()) return; 1640 getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE, 1641 message); 1642 } 1643 1644 private void enforcePolicyAccess(String pkg, String method) { 1645 if (!checkPolicyAccess(pkg)) { 1646 Slog.w(TAG, "Notification policy access denied calling " + method); 1647 throw new SecurityException("Notification policy access denied"); 1648 } 1649 } 1650 1651 private boolean checkPackagePolicyAccess(String pkg) { 1652 return mPolicyAccess.isPackageGranted(pkg); 1653 } 1654 1655 private boolean checkPolicyAccess(String pkg) { 1656 return checkPackagePolicyAccess(pkg) || mListeners.isComponentEnabledForPackage(pkg); 1657 } 1658 1659 @Override 1660 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1661 if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 1662 != PackageManager.PERMISSION_GRANTED) { 1663 pw.println("Permission Denial: can't dump NotificationManager from pid=" 1664 + Binder.getCallingPid() 1665 + ", uid=" + Binder.getCallingUid()); 1666 return; 1667 } 1668 1669 final DumpFilter filter = DumpFilter.parseFromArguments(args); 1670 if (filter != null && filter.stats) { 1671 dumpJson(pw, filter); 1672 } else { 1673 dumpImpl(pw, filter); 1674 } 1675 } 1676 1677 @Override 1678 public ComponentName getEffectsSuppressor() { 1679 enforceSystemOrSystemUIOrVolume("INotificationManager.getEffectsSuppressor"); 1680 return mEffectsSuppressor; 1681 } 1682 1683 @Override 1684 public boolean matchesCallFilter(Bundle extras) { 1685 enforceSystemOrSystemUI("INotificationManager.matchesCallFilter"); 1686 return mZenModeHelper.matchesCallFilter( 1687 UserHandle.getCallingUserHandle(), 1688 extras, 1689 mRankingHelper.findExtractor(ValidateNotificationPeople.class), 1690 MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS, 1691 MATCHES_CALL_FILTER_TIMEOUT_AFFINITY); 1692 } 1693 1694 @Override 1695 public boolean isSystemConditionProviderEnabled(String path) { 1696 enforceSystemOrSystemUIOrVolume("INotificationManager.isSystemConditionProviderEnabled"); 1697 return mConditionProviders.isSystemProviderEnabled(path); 1698 } 1699 1700 // Backup/restore interface 1701 @Override 1702 public byte[] getBackupPayload(int user) { 1703 if (DBG) Slog.d(TAG, "getBackupPayload u=" + user); 1704 if (user != UserHandle.USER_OWNER) { 1705 Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user); 1706 return null; 1707 } 1708 final ByteArrayOutputStream baos = new ByteArrayOutputStream(); 1709 try { 1710 writePolicyXml(baos, true /*forBackup*/); 1711 return baos.toByteArray(); 1712 } catch (IOException e) { 1713 Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e); 1714 } 1715 return null; 1716 } 1717 1718 @Override 1719 public void applyRestore(byte[] payload, int user) { 1720 if (DBG) Slog.d(TAG, "applyRestore u=" + user + " payload=" 1721 + (payload != null ? new String(payload, StandardCharsets.UTF_8) : null)); 1722 if (payload == null) { 1723 Slog.w(TAG, "applyRestore: no payload to restore for user " + user); 1724 return; 1725 } 1726 if (user != UserHandle.USER_OWNER) { 1727 Slog.w(TAG, "applyRestore: cannot restore policy for user " + user); 1728 return; 1729 } 1730 final ByteArrayInputStream bais = new ByteArrayInputStream(payload); 1731 try { 1732 readPolicyXml(bais, true /*forRestore*/); 1733 savePolicyFile(); 1734 } catch (NumberFormatException | XmlPullParserException | IOException e) { 1735 Slog.w(TAG, "applyRestore: error reading payload", e); 1736 } 1737 } 1738 1739 @Override 1740 public boolean isNotificationPolicyAccessGranted(String pkg) { 1741 return checkPolicyAccess(pkg); 1742 } 1743 1744 @Override 1745 public boolean isNotificationPolicyAccessGrantedForPackage(String pkg) { 1746 enforceSystemOrSystemUI("request policy access status for another package"); 1747 return checkPackagePolicyAccess(pkg); 1748 } 1749 1750 @Override 1751 public String[] getPackagesRequestingNotificationPolicyAccess() 1752 throws RemoteException { 1753 enforceSystemOrSystemUI("request policy access packages"); 1754 final long identity = Binder.clearCallingIdentity(); 1755 try { 1756 return mPolicyAccess.getRequestingPackages(); 1757 } finally { 1758 Binder.restoreCallingIdentity(identity); 1759 } 1760 } 1761 1762 @Override 1763 public void setNotificationPolicyAccessGranted(String pkg, boolean granted) 1764 throws RemoteException { 1765 enforceSystemOrSystemUI("grant notification policy access"); 1766 final long identity = Binder.clearCallingIdentity(); 1767 try { 1768 synchronized (mNotificationList) { 1769 mPolicyAccess.put(pkg, granted); 1770 } 1771 } finally { 1772 Binder.restoreCallingIdentity(identity); 1773 } 1774 } 1775 1776 @Override 1777 public Policy getNotificationPolicy(String pkg) { 1778 enforcePolicyAccess(pkg, "getNotificationPolicy"); 1779 final long identity = Binder.clearCallingIdentity(); 1780 try { 1781 return mZenModeHelper.getNotificationPolicy(); 1782 } finally { 1783 Binder.restoreCallingIdentity(identity); 1784 } 1785 } 1786 1787 @Override 1788 public void setNotificationPolicy(String pkg, Policy policy) { 1789 enforcePolicyAccess(pkg, "setNotificationPolicy"); 1790 final long identity = Binder.clearCallingIdentity(); 1791 try { 1792 mZenModeHelper.setNotificationPolicy(policy); 1793 } finally { 1794 Binder.restoreCallingIdentity(identity); 1795 } 1796 } 1797 }; 1798 1799 private String disableNotificationEffects(NotificationRecord record) { 1800 if (mDisableNotificationEffects) { 1801 return "booleanState"; 1802 } 1803 if ((mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0) { 1804 return "listenerHints"; 1805 } 1806 if (mCallState != TelephonyManager.CALL_STATE_IDLE && !mZenModeHelper.isCall(record)) { 1807 return "callState"; 1808 } 1809 return null; 1810 }; 1811 1812 private void dumpJson(PrintWriter pw, DumpFilter filter) { 1813 JSONObject dump = new JSONObject(); 1814 try { 1815 dump.put("service", "Notification Manager"); 1816 JSONArray bans = new JSONArray(); 1817 try { 1818 ArrayMap<Integer, ArrayList<String>> packageBans = getPackageBans(filter); 1819 for (Integer userId : packageBans.keySet()) { 1820 for (String packageName : packageBans.get(userId)) { 1821 JSONObject ban = new JSONObject(); 1822 ban.put("userId", userId); 1823 ban.put("packageName", packageName); 1824 bans.put(ban); 1825 } 1826 } 1827 } catch (NameNotFoundException e) { 1828 // pass 1829 } 1830 dump.put("bans", bans); 1831 dump.put("stats", mUsageStats.dumpJson(filter)); 1832 } catch (JSONException e) { 1833 e.printStackTrace(); 1834 } 1835 pw.println(dump); 1836 } 1837 1838 void dumpImpl(PrintWriter pw, DumpFilter filter) { 1839 pw.print("Current Notification Manager state"); 1840 if (filter.filtered) { 1841 pw.print(" (filtered to "); pw.print(filter); pw.print(")"); 1842 } 1843 pw.println(':'); 1844 int N; 1845 final boolean zenOnly = filter.filtered && filter.zen; 1846 1847 if (!zenOnly) { 1848 synchronized (mToastQueue) { 1849 N = mToastQueue.size(); 1850 if (N > 0) { 1851 pw.println(" Toast Queue:"); 1852 for (int i=0; i<N; i++) { 1853 mToastQueue.get(i).dump(pw, " ", filter); 1854 } 1855 pw.println(" "); 1856 } 1857 } 1858 } 1859 1860 synchronized (mNotificationList) { 1861 if (!zenOnly) { 1862 N = mNotificationList.size(); 1863 if (N > 0) { 1864 pw.println(" Notification List:"); 1865 for (int i=0; i<N; i++) { 1866 final NotificationRecord nr = mNotificationList.get(i); 1867 if (filter.filtered && !filter.matches(nr.sbn)) continue; 1868 nr.dump(pw, " ", getContext(), filter.redact); 1869 } 1870 pw.println(" "); 1871 } 1872 1873 if (!filter.filtered) { 1874 N = mLights.size(); 1875 if (N > 0) { 1876 pw.println(" Lights List:"); 1877 for (int i=0; i<N; i++) { 1878 if (i == N - 1) { 1879 pw.print(" > "); 1880 } else { 1881 pw.print(" "); 1882 } 1883 pw.println(mLights.get(i)); 1884 } 1885 pw.println(" "); 1886 } 1887 pw.println(" mUseAttentionLight=" + mUseAttentionLight); 1888 pw.println(" mNotificationPulseEnabled=" + mNotificationPulseEnabled); 1889 pw.println(" mSoundNotificationKey=" + mSoundNotificationKey); 1890 pw.println(" mVibrateNotificationKey=" + mVibrateNotificationKey); 1891 pw.println(" mDisableNotificationEffects=" + mDisableNotificationEffects); 1892 pw.println(" mCallState=" + callStateToString(mCallState)); 1893 pw.println(" mSystemReady=" + mSystemReady); 1894 } 1895 pw.println(" mArchive=" + mArchive.toString()); 1896 Iterator<StatusBarNotification> iter = mArchive.descendingIterator(); 1897 int i=0; 1898 while (iter.hasNext()) { 1899 final StatusBarNotification sbn = iter.next(); 1900 if (filter != null && !filter.matches(sbn)) continue; 1901 pw.println(" " + sbn); 1902 if (++i >= 5) { 1903 if (iter.hasNext()) pw.println(" ..."); 1904 break; 1905 } 1906 } 1907 } 1908 1909 if (!zenOnly) { 1910 pw.println("\n Usage Stats:"); 1911 mUsageStats.dump(pw, " ", filter); 1912 } 1913 1914 if (!filter.filtered || zenOnly) { 1915 pw.println("\n Zen Mode:"); 1916 pw.print(" mInterruptionFilter="); pw.println(mInterruptionFilter); 1917 mZenModeHelper.dump(pw, " "); 1918 1919 pw.println("\n Zen Log:"); 1920 ZenLog.dump(pw, " "); 1921 } 1922 1923 if (!zenOnly) { 1924 pw.println("\n Ranking Config:"); 1925 mRankingHelper.dump(pw, " ", filter); 1926 1927 pw.println("\n Notification listeners:"); 1928 mListeners.dump(pw, filter); 1929 pw.print(" mListenerHints: "); pw.println(mListenerHints); 1930 pw.print(" mListenersDisablingEffects: ("); 1931 N = mListenersDisablingEffects.size(); 1932 for (int i = 0; i < N; i++) { 1933 final ManagedServiceInfo listener = mListenersDisablingEffects.valueAt(i); 1934 if (i > 0) pw.print(','); 1935 pw.print(listener.component); 1936 } 1937 pw.println(')'); 1938 } 1939 pw.println("\n Policy access:"); 1940 pw.print(" mPolicyAccess: "); pw.println(mPolicyAccess); 1941 1942 pw.println("\n Condition providers:"); 1943 mConditionProviders.dump(pw, filter); 1944 1945 pw.println("\n Group summaries:"); 1946 for (Entry<String, NotificationRecord> entry : mSummaryByGroupKey.entrySet()) { 1947 NotificationRecord r = entry.getValue(); 1948 pw.println(" " + entry.getKey() + " -> " + r.getKey()); 1949 if (mNotificationsByKey.get(r.getKey()) != r) { 1950 pw.println("!!!!!!LEAK: Record not found in mNotificationsByKey."); 1951 r.dump(pw, " ", getContext(), filter.redact); 1952 } 1953 } 1954 1955 try { 1956 pw.println("\n Banned Packages:"); 1957 ArrayMap<Integer, ArrayList<String>> packageBans = getPackageBans(filter); 1958 for (Integer userId : packageBans.keySet()) { 1959 for (String packageName : packageBans.get(userId)) { 1960 pw.println(" " + userId + ": " + packageName); 1961 } 1962 } 1963 } catch (NameNotFoundException e) { 1964 // pass 1965 } 1966 } 1967 } 1968 1969 private ArrayMap<Integer, ArrayList<String>> getPackageBans(DumpFilter filter) 1970 throws NameNotFoundException { 1971 ArrayMap<Integer, ArrayList<String>> packageBans = new ArrayMap<>(); 1972 ArrayList<String> packageNames = new ArrayList<>(); 1973 for (UserInfo user : UserManager.get(getContext()).getUsers()) { 1974 final int userId = user.getUserHandle().getIdentifier(); 1975 final PackageManager packageManager = getContext().getPackageManager(); 1976 List<PackageInfo> packages = packageManager.getInstalledPackages(0, userId); 1977 final int packageCount = packages.size(); 1978 for (int p = 0; p < packageCount; p++) { 1979 final String packageName = packages.get(p).packageName; 1980 if (filter == null || filter.matches(packageName)) { 1981 final int uid = packageManager.getPackageUid(packageName, userId); 1982 if (!checkNotificationOp(packageName, uid)) { 1983 packageNames.add(packageName); 1984 } 1985 } 1986 } 1987 if (!packageNames.isEmpty()) { 1988 packageBans.put(userId, packageNames); 1989 packageNames = new ArrayList<>(); 1990 } 1991 } 1992 return packageBans; 1993 } 1994 1995 /** 1996 * The private API only accessible to the system process. 1997 */ 1998 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() { 1999 @Override 2000 public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid, 2001 String tag, int id, Notification notification, int[] idReceived, int userId) { 2002 enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification, 2003 idReceived, userId); 2004 } 2005 2006 @Override 2007 public void removeForegroundServiceFlagFromNotification(String pkg, int notificationId, 2008 int userId) { 2009 checkCallerIsSystem(); 2010 synchronized (mNotificationList) { 2011 int i = indexOfNotificationLocked(pkg, null, notificationId, userId); 2012 if (i < 0) { 2013 Log.d(TAG, "stripForegroundServiceFlag: Could not find notification with " 2014 + "pkg=" + pkg + " / id=" + notificationId + " / userId=" + userId); 2015 return; 2016 } 2017 NotificationRecord r = mNotificationList.get(i); 2018 StatusBarNotification sbn = r.sbn; 2019 // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees 2020 // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove FLAG_FOREGROUND_SERVICE, 2021 // we have to revert to the flags we received initially *and* force remove 2022 // FLAG_FOREGROUND_SERVICE. 2023 sbn.getNotification().flags = 2024 (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE); 2025 mRankingHelper.sort(mNotificationList); 2026 mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */); 2027 } 2028 } 2029 }; 2030 2031 void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid, 2032 final int callingPid, final String tag, final int id, final Notification notification, 2033 int[] idOut, int incomingUserId) { 2034 if (DBG) { 2035 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id 2036 + " notification=" + notification); 2037 } 2038 checkCallerIsSystemOrSameApp(pkg); 2039 final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg)); 2040 final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg); 2041 2042 final int userId = ActivityManager.handleIncomingUser(callingPid, 2043 callingUid, incomingUserId, true, false, "enqueueNotification", pkg); 2044 final UserHandle user = new UserHandle(userId); 2045 2046 // Limit the number of notifications that any given package except the android 2047 // package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks. 2048 if (!isSystemNotification && !isNotificationFromListener) { 2049 synchronized (mNotificationList) { 2050 int count = 0; 2051 final int N = mNotificationList.size(); 2052 for (int i=0; i<N; i++) { 2053 final NotificationRecord r = mNotificationList.get(i); 2054 if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) { 2055 if (r.sbn.getId() == id && TextUtils.equals(r.sbn.getTag(), tag)) { 2056 break; // Allow updating existing notification 2057 } 2058 count++; 2059 if (count >= MAX_PACKAGE_NOTIFICATIONS) { 2060 Slog.e(TAG, "Package has already posted " + count 2061 + " notifications. Not showing more. package=" + pkg); 2062 return; 2063 } 2064 } 2065 } 2066 } 2067 } 2068 2069 if (pkg == null || notification == null) { 2070 throw new IllegalArgumentException("null not allowed: pkg=" + pkg 2071 + " id=" + id + " notification=" + notification); 2072 } 2073 2074 if (notification.getSmallIcon() != null) { 2075 if (!notification.isValid()) { 2076 throw new IllegalArgumentException("Invalid notification (): pkg=" + pkg 2077 + " id=" + id + " notification=" + notification); 2078 } 2079 } 2080 2081 mHandler.post(new Runnable() { 2082 @Override 2083 public void run() { 2084 2085 synchronized (mNotificationList) { 2086 2087 // === Scoring === 2088 2089 // 0. Sanitize inputs 2090 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN, 2091 Notification.PRIORITY_MAX); 2092 // Migrate notification flags to scores 2093 if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) { 2094 if (notification.priority < Notification.PRIORITY_MAX) { 2095 notification.priority = Notification.PRIORITY_MAX; 2096 } 2097 } else if (SCORE_ONGOING_HIGHER && 2098 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) { 2099 if (notification.priority < Notification.PRIORITY_HIGH) { 2100 notification.priority = Notification.PRIORITY_HIGH; 2101 } 2102 } 2103 // force no heads up per package config 2104 if (!mRankingHelper.getPackagePeekable(pkg, callingUid)) { 2105 if (notification.extras == null) { 2106 notification.extras = new Bundle(); 2107 } 2108 notification.extras.putInt(Notification.EXTRA_AS_HEADS_UP, 2109 Notification.HEADS_UP_NEVER); 2110 } 2111 2112 // 1. initial score: buckets of 10, around the app [-20..20] 2113 final int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; 2114 2115 // 2. extract ranking signals from the notification data 2116 final StatusBarNotification n = new StatusBarNotification( 2117 pkg, opPkg, id, tag, callingUid, callingPid, score, notification, 2118 user); 2119 NotificationRecord r = new NotificationRecord(n, score); 2120 NotificationRecord old = mNotificationsByKey.get(n.getKey()); 2121 if (old != null) { 2122 // Retain ranking information from previous record 2123 r.copyRankingInformation(old); 2124 } 2125 2126 // Handle grouped notifications and bail out early if we 2127 // can to avoid extracting signals. 2128 handleGroupedNotificationLocked(r, old, callingUid, callingPid); 2129 boolean ignoreNotification = 2130 removeUnusedGroupedNotificationLocked(r, old, callingUid, callingPid); 2131 2132 // This conditional is a dirty hack to limit the logging done on 2133 // behalf of the download manager without affecting other apps. 2134 if (!pkg.equals("com.android.providers.downloads") 2135 || Log.isLoggable("DownloadManager", Log.VERBOSE)) { 2136 int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW; 2137 if (ignoreNotification) { 2138 enqueueStatus = EVENTLOG_ENQUEUE_STATUS_IGNORED; 2139 } else if (old != null) { 2140 enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE; 2141 } 2142 EventLogTags.writeNotificationEnqueue(callingUid, callingPid, 2143 pkg, id, tag, userId, notification.toString(), 2144 enqueueStatus); 2145 } 2146 2147 if (ignoreNotification) { 2148 return; 2149 } 2150 2151 mRankingHelper.extractSignals(r); 2152 2153 // 3. Apply local rules 2154 2155 // blocked apps 2156 if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) { 2157 if (!isSystemNotification) { 2158 r.score = JUNK_SCORE; 2159 Slog.e(TAG, "Suppressing notification from package " + pkg 2160 + " by user request."); 2161 mUsageStats.registerBlocked(r); 2162 } 2163 } 2164 2165 if (r.score < SCORE_DISPLAY_THRESHOLD) { 2166 // Notification will be blocked because the score is too low. 2167 return; 2168 } 2169 2170 int index = indexOfNotificationLocked(n.getKey()); 2171 if (index < 0) { 2172 mNotificationList.add(r); 2173 mUsageStats.registerPostedByApp(r); 2174 } else { 2175 old = mNotificationList.get(index); 2176 mNotificationList.set(index, r); 2177 mUsageStats.registerUpdatedByApp(r, old); 2178 // Make sure we don't lose the foreground service state. 2179 notification.flags |= 2180 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE; 2181 r.isUpdate = true; 2182 } 2183 2184 mNotificationsByKey.put(n.getKey(), r); 2185 2186 // Ensure if this is a foreground service that the proper additional 2187 // flags are set. 2188 if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) { 2189 notification.flags |= Notification.FLAG_ONGOING_EVENT 2190 | Notification.FLAG_NO_CLEAR; 2191 } 2192 2193 applyZenModeLocked(r); 2194 mRankingHelper.sort(mNotificationList); 2195 2196 if (notification.getSmallIcon() != null) { 2197 StatusBarNotification oldSbn = (old != null) ? old.sbn : null; 2198 mListeners.notifyPostedLocked(n, oldSbn); 2199 } else { 2200 Slog.e(TAG, "Not posting notification without small icon: " + notification); 2201 if (old != null && !old.isCanceled) { 2202 mListeners.notifyRemovedLocked(n); 2203 } 2204 // ATTENTION: in a future release we will bail out here 2205 // so that we do not play sounds, show lights, etc. for invalid 2206 // notifications 2207 Slog.e(TAG, "WARNING: In a future release this will crash the app: " 2208 + n.getPackageName()); 2209 } 2210 2211 buzzBeepBlinkLocked(r); 2212 } 2213 } 2214 }); 2215 2216 idOut[0] = id; 2217 } 2218 2219 /** 2220 * Ensures that grouped notification receive their special treatment. 2221 * 2222 * <p>Cancels group children if the new notification causes a group to lose 2223 * its summary.</p> 2224 * 2225 * <p>Updates mSummaryByGroupKey.</p> 2226 */ 2227 private void handleGroupedNotificationLocked(NotificationRecord r, NotificationRecord old, 2228 int callingUid, int callingPid) { 2229 StatusBarNotification sbn = r.sbn; 2230 Notification n = sbn.getNotification(); 2231 String group = sbn.getGroupKey(); 2232 boolean isSummary = n.isGroupSummary(); 2233 2234 Notification oldN = old != null ? old.sbn.getNotification() : null; 2235 String oldGroup = old != null ? old.sbn.getGroupKey() : null; 2236 boolean oldIsSummary = old != null && oldN.isGroupSummary(); 2237 2238 if (oldIsSummary) { 2239 NotificationRecord removedSummary = mSummaryByGroupKey.remove(oldGroup); 2240 if (removedSummary != old) { 2241 String removedKey = 2242 removedSummary != null ? removedSummary.getKey() : "<null>"; 2243 Slog.w(TAG, "Removed summary didn't match old notification: old=" + old.getKey() + 2244 ", removed=" + removedKey); 2245 } 2246 } 2247 if (isSummary) { 2248 mSummaryByGroupKey.put(group, r); 2249 } 2250 2251 // Clear out group children of the old notification if the update 2252 // causes the group summary to go away. This happens when the old 2253 // notification was a summary and the new one isn't, or when the old 2254 // notification was a summary and its group key changed. 2255 if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) { 2256 cancelGroupChildrenLocked(old, callingUid, callingPid, null, 2257 REASON_GROUP_SUMMARY_CANCELED); 2258 } 2259 } 2260 2261 /** 2262 * Performs group notification optimizations if SysUI is the only active 2263 * notification listener and returns whether the given notification should 2264 * be ignored. 2265 * 2266 * <p>Returns true if the given notification is a child of a group with a 2267 * summary, which means that SysUI will never show it, and hence the new 2268 * notification can be safely ignored. Also cancels any previous instance 2269 * of the ignored notification.</p> 2270 * 2271 * <p>For summaries, cancels all children of that group, as SysUI will 2272 * never show them anymore.</p> 2273 * 2274 * @return true if the given notification can be ignored as an optimization 2275 */ 2276 private boolean removeUnusedGroupedNotificationLocked(NotificationRecord r, 2277 NotificationRecord old, int callingUid, int callingPid) { 2278 if (!ENABLE_CHILD_NOTIFICATIONS) { 2279 // No optimizations are possible if listeners want groups. 2280 if (mListeners.notificationGroupsDesired()) { 2281 return false; 2282 } 2283 2284 StatusBarNotification sbn = r.sbn; 2285 String group = sbn.getGroupKey(); 2286 boolean isSummary = sbn.getNotification().isGroupSummary(); 2287 boolean isChild = sbn.getNotification().isGroupChild(); 2288 2289 NotificationRecord summary = mSummaryByGroupKey.get(group); 2290 if (isChild && summary != null) { 2291 // Child with an active summary -> ignore 2292 if (DBG) { 2293 Slog.d(TAG, "Ignoring group child " + sbn.getKey() + " due to existing summary " 2294 + summary.getKey()); 2295 } 2296 // Make sure we don't leave an old version of the notification around. 2297 if (old != null) { 2298 if (DBG) { 2299 Slog.d(TAG, "Canceling old version of ignored group child " + sbn.getKey()); 2300 } 2301 cancelNotificationLocked(old, false, REASON_GROUP_OPTIMIZATION); 2302 } 2303 return true; 2304 } else if (isSummary) { 2305 // Summary -> cancel children 2306 cancelGroupChildrenLocked(r, callingUid, callingPid, null, 2307 REASON_GROUP_OPTIMIZATION); 2308 } 2309 } 2310 return false; 2311 } 2312 2313 private void buzzBeepBlinkLocked(NotificationRecord record) { 2314 boolean buzz = false; 2315 boolean beep = false; 2316 boolean blink = false; 2317 2318 final Notification notification = record.sbn.getNotification(); 2319 2320 // Should this notification make noise, vibe, or use the LED? 2321 final boolean aboveThreshold = record.score >= SCORE_INTERRUPTION_THRESHOLD; 2322 final boolean canInterrupt = aboveThreshold && !record.isIntercepted(); 2323 if (DBG || record.isIntercepted()) 2324 Slog.v(TAG, 2325 "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt + 2326 " intercept=" + record.isIntercepted() 2327 ); 2328 2329 final int currentUser; 2330 final long token = Binder.clearCallingIdentity(); 2331 try { 2332 currentUser = ActivityManager.getCurrentUser(); 2333 } finally { 2334 Binder.restoreCallingIdentity(token); 2335 } 2336 2337 // If we're not supposed to beep, vibrate, etc. then don't. 2338 final String disableEffects = disableNotificationEffects(record); 2339 if (disableEffects != null) { 2340 ZenLog.traceDisableEffects(record, disableEffects); 2341 } 2342 if (disableEffects == null 2343 && (!(record.isUpdate 2344 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 )) 2345 && (record.getUserId() == UserHandle.USER_ALL || 2346 record.getUserId() == currentUser || 2347 mUserProfiles.isCurrentProfile(record.getUserId())) 2348 && canInterrupt 2349 && mSystemReady 2350 && mAudioManager != null) { 2351 if (DBG) Slog.v(TAG, "Interrupting!"); 2352 2353 sendAccessibilityEvent(notification, record.sbn.getPackageName()); 2354 2355 // sound 2356 2357 // should we use the default notification sound? (indicated either by 2358 // DEFAULT_SOUND or because notification.sound is pointing at 2359 // Settings.System.NOTIFICATION_SOUND) 2360 final boolean useDefaultSound = 2361 (notification.defaults & Notification.DEFAULT_SOUND) != 0 || 2362 Settings.System.DEFAULT_NOTIFICATION_URI 2363 .equals(notification.sound); 2364 2365 Uri soundUri = null; 2366 boolean hasValidSound = false; 2367 2368 if (useDefaultSound) { 2369 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI; 2370 2371 // check to see if the default notification sound is silent 2372 ContentResolver resolver = getContext().getContentResolver(); 2373 hasValidSound = Settings.System.getString(resolver, 2374 Settings.System.NOTIFICATION_SOUND) != null; 2375 } else if (notification.sound != null) { 2376 soundUri = notification.sound; 2377 hasValidSound = (soundUri != null); 2378 } 2379 2380 if (hasValidSound) { 2381 boolean looping = 2382 (notification.flags & Notification.FLAG_INSISTENT) != 0; 2383 AudioAttributes audioAttributes = audioAttributesForNotification(notification); 2384 mSoundNotificationKey = record.getKey(); 2385 // do not play notifications if stream volume is 0 (typically because 2386 // ringer mode is silent) or if there is a user of exclusive audio focus 2387 if ((mAudioManager.getStreamVolume( 2388 AudioAttributes.toLegacyStreamType(audioAttributes)) != 0) 2389 && !mAudioManager.isAudioFocusExclusive()) { 2390 final long identity = Binder.clearCallingIdentity(); 2391 try { 2392 final IRingtonePlayer player = 2393 mAudioManager.getRingtonePlayer(); 2394 if (player != null) { 2395 if (DBG) Slog.v(TAG, "Playing sound " + soundUri 2396 + " with attributes " + audioAttributes); 2397 player.playAsync(soundUri, record.sbn.getUser(), looping, 2398 audioAttributes); 2399 beep = true; 2400 } 2401 } catch (RemoteException e) { 2402 } finally { 2403 Binder.restoreCallingIdentity(identity); 2404 } 2405 } 2406 } 2407 2408 // vibrate 2409 // Does the notification want to specify its own vibration? 2410 final boolean hasCustomVibrate = notification.vibrate != null; 2411 2412 // new in 4.2: if there was supposed to be a sound and we're in vibrate 2413 // mode, and no other vibration is specified, we fall back to vibration 2414 final boolean convertSoundToVibration = 2415 !hasCustomVibrate 2416 && hasValidSound 2417 && (mAudioManager.getRingerModeInternal() 2418 == AudioManager.RINGER_MODE_VIBRATE); 2419 2420 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback. 2421 final boolean useDefaultVibrate = 2422 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; 2423 2424 if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate) 2425 && !(mAudioManager.getRingerModeInternal() 2426 == AudioManager.RINGER_MODE_SILENT)) { 2427 mVibrateNotificationKey = record.getKey(); 2428 2429 if (useDefaultVibrate || convertSoundToVibration) { 2430 // Escalate privileges so we can use the vibrator even if the 2431 // notifying app does not have the VIBRATE permission. 2432 long identity = Binder.clearCallingIdentity(); 2433 try { 2434 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(), 2435 useDefaultVibrate ? mDefaultVibrationPattern 2436 : mFallbackVibrationPattern, 2437 ((notification.flags & Notification.FLAG_INSISTENT) != 0) 2438 ? 0: -1, audioAttributesForNotification(notification)); 2439 buzz = true; 2440 } finally { 2441 Binder.restoreCallingIdentity(identity); 2442 } 2443 } else if (notification.vibrate.length > 1) { 2444 // If you want your own vibration pattern, you need the VIBRATE 2445 // permission 2446 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(), 2447 notification.vibrate, 2448 ((notification.flags & Notification.FLAG_INSISTENT) != 0) 2449 ? 0: -1, audioAttributesForNotification(notification)); 2450 buzz = true; 2451 } 2452 } 2453 } 2454 2455 // light 2456 // release the light 2457 boolean wasShowLights = mLights.remove(record.getKey()); 2458 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThreshold) { 2459 mLights.add(record.getKey()); 2460 updateLightsLocked(); 2461 if (mUseAttentionLight) { 2462 mAttentionLight.pulse(); 2463 } 2464 blink = true; 2465 } else if (wasShowLights) { 2466 updateLightsLocked(); 2467 } 2468 if (buzz || beep || blink) { 2469 EventLogTags.writeNotificationAlert(record.getKey(), 2470 buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0); 2471 mHandler.post(mBuzzBeepBlinked); 2472 } 2473 } 2474 2475 private static AudioAttributes audioAttributesForNotification(Notification n) { 2476 if (n.audioAttributes != null 2477 && !Notification.AUDIO_ATTRIBUTES_DEFAULT.equals(n.audioAttributes)) { 2478 // the audio attributes are set and different from the default, use them 2479 return n.audioAttributes; 2480 } else if (n.audioStreamType >= 0 && n.audioStreamType < AudioSystem.getNumStreamTypes()) { 2481 // the stream type is valid, use it 2482 return new AudioAttributes.Builder() 2483 .setInternalLegacyStreamType(n.audioStreamType) 2484 .build(); 2485 } else if (n.audioStreamType == AudioSystem.STREAM_DEFAULT) { 2486 return Notification.AUDIO_ATTRIBUTES_DEFAULT; 2487 } else { 2488 Log.w(TAG, String.format("Invalid stream type: %d", n.audioStreamType)); 2489 return Notification.AUDIO_ATTRIBUTES_DEFAULT; 2490 } 2491 } 2492 2493 void showNextToastLocked() { 2494 ToastRecord record = mToastQueue.get(0); 2495 while (record != null) { 2496 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback); 2497 try { 2498 record.callback.show(); 2499 scheduleTimeoutLocked(record); 2500 return; 2501 } catch (RemoteException e) { 2502 Slog.w(TAG, "Object died trying to show notification " + record.callback 2503 + " in package " + record.pkg); 2504 // remove it from the list and let the process die 2505 int index = mToastQueue.indexOf(record); 2506 if (index >= 0) { 2507 mToastQueue.remove(index); 2508 } 2509 keepProcessAliveLocked(record.pid); 2510 if (mToastQueue.size() > 0) { 2511 record = mToastQueue.get(0); 2512 } else { 2513 record = null; 2514 } 2515 } 2516 } 2517 } 2518 2519 void cancelToastLocked(int index) { 2520 ToastRecord record = mToastQueue.get(index); 2521 try { 2522 record.callback.hide(); 2523 } catch (RemoteException e) { 2524 Slog.w(TAG, "Object died trying to hide notification " + record.callback 2525 + " in package " + record.pkg); 2526 // don't worry about this, we're about to remove it from 2527 // the list anyway 2528 } 2529 mToastQueue.remove(index); 2530 keepProcessAliveLocked(record.pid); 2531 if (mToastQueue.size() > 0) { 2532 // Show the next one. If the callback fails, this will remove 2533 // it from the list, so don't assume that the list hasn't changed 2534 // after this point. 2535 showNextToastLocked(); 2536 } 2537 } 2538 2539 private void scheduleTimeoutLocked(ToastRecord r) 2540 { 2541 mHandler.removeCallbacksAndMessages(r); 2542 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r); 2543 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY; 2544 mHandler.sendMessageDelayed(m, delay); 2545 } 2546 2547 private void handleTimeout(ToastRecord record) 2548 { 2549 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback); 2550 synchronized (mToastQueue) { 2551 int index = indexOfToastLocked(record.pkg, record.callback); 2552 if (index >= 0) { 2553 cancelToastLocked(index); 2554 } 2555 } 2556 } 2557 2558 // lock on mToastQueue 2559 int indexOfToastLocked(String pkg, ITransientNotification callback) 2560 { 2561 IBinder cbak = callback.asBinder(); 2562 ArrayList<ToastRecord> list = mToastQueue; 2563 int len = list.size(); 2564 for (int i=0; i<len; i++) { 2565 ToastRecord r = list.get(i); 2566 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) { 2567 return i; 2568 } 2569 } 2570 return -1; 2571 } 2572 2573 // lock on mToastQueue 2574 void keepProcessAliveLocked(int pid) 2575 { 2576 int toastCount = 0; // toasts from this pid 2577 ArrayList<ToastRecord> list = mToastQueue; 2578 int N = list.size(); 2579 for (int i=0; i<N; i++) { 2580 ToastRecord r = list.get(i); 2581 if (r.pid == pid) { 2582 toastCount++; 2583 } 2584 } 2585 try { 2586 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0); 2587 } catch (RemoteException e) { 2588 // Shouldn't happen. 2589 } 2590 } 2591 2592 private void handleRankingReconsideration(Message message) { 2593 if (!(message.obj instanceof RankingReconsideration)) return; 2594 RankingReconsideration recon = (RankingReconsideration) message.obj; 2595 recon.run(); 2596 boolean changed; 2597 synchronized (mNotificationList) { 2598 final NotificationRecord record = mNotificationsByKey.get(recon.getKey()); 2599 if (record == null) { 2600 return; 2601 } 2602 int indexBefore = findNotificationRecordIndexLocked(record); 2603 boolean interceptBefore = record.isIntercepted(); 2604 int visibilityBefore = record.getPackageVisibilityOverride(); 2605 recon.applyChangesLocked(record); 2606 applyZenModeLocked(record); 2607 mRankingHelper.sort(mNotificationList); 2608 int indexAfter = findNotificationRecordIndexLocked(record); 2609 boolean interceptAfter = record.isIntercepted(); 2610 int visibilityAfter = record.getPackageVisibilityOverride(); 2611 changed = indexBefore != indexAfter || interceptBefore != interceptAfter 2612 || visibilityBefore != visibilityAfter; 2613 if (interceptBefore && !interceptAfter) { 2614 buzzBeepBlinkLocked(record); 2615 } 2616 } 2617 if (changed) { 2618 scheduleSendRankingUpdate(); 2619 } 2620 } 2621 2622 private void handleRankingConfigChange() { 2623 synchronized (mNotificationList) { 2624 final int N = mNotificationList.size(); 2625 ArrayList<String> orderBefore = new ArrayList<String>(N); 2626 int[] visibilities = new int[N]; 2627 for (int i = 0; i < N; i++) { 2628 final NotificationRecord r = mNotificationList.get(i); 2629 orderBefore.add(r.getKey()); 2630 visibilities[i] = r.getPackageVisibilityOverride(); 2631 mRankingHelper.extractSignals(r); 2632 } 2633 for (int i = 0; i < N; i++) { 2634 mRankingHelper.sort(mNotificationList); 2635 final NotificationRecord r = mNotificationList.get(i); 2636 if (!orderBefore.get(i).equals(r.getKey()) 2637 || visibilities[i] != r.getPackageVisibilityOverride()) { 2638 scheduleSendRankingUpdate(); 2639 return; 2640 } 2641 } 2642 } 2643 } 2644 2645 // let zen mode evaluate this record 2646 private void applyZenModeLocked(NotificationRecord record) { 2647 record.setIntercepted(mZenModeHelper.shouldIntercept(record)); 2648 } 2649 2650 // lock on mNotificationList 2651 private int findNotificationRecordIndexLocked(NotificationRecord target) { 2652 return mRankingHelper.indexOf(mNotificationList, target); 2653 } 2654 2655 private void scheduleSendRankingUpdate() { 2656 mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE); 2657 Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE); 2658 mHandler.sendMessage(m); 2659 } 2660 2661 private void handleSendRankingUpdate() { 2662 synchronized (mNotificationList) { 2663 mListeners.notifyRankingUpdateLocked(); 2664 } 2665 } 2666 2667 private void scheduleListenerHintsChanged(int state) { 2668 mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED); 2669 mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget(); 2670 } 2671 2672 private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) { 2673 mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED); 2674 mHandler.obtainMessage( 2675 MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED, 2676 listenerInterruptionFilter, 2677 0).sendToTarget(); 2678 } 2679 2680 private void handleListenerHintsChanged(int hints) { 2681 synchronized (mNotificationList) { 2682 mListeners.notifyListenerHintsChangedLocked(hints); 2683 } 2684 } 2685 2686 private void handleListenerInterruptionFilterChanged(int interruptionFilter) { 2687 synchronized (mNotificationList) { 2688 mListeners.notifyInterruptionFilterChanged(interruptionFilter); 2689 } 2690 } 2691 2692 private final class WorkerHandler extends Handler 2693 { 2694 @Override 2695 public void handleMessage(Message msg) 2696 { 2697 switch (msg.what) 2698 { 2699 case MESSAGE_TIMEOUT: 2700 handleTimeout((ToastRecord)msg.obj); 2701 break; 2702 case MESSAGE_SAVE_POLICY_FILE: 2703 handleSavePolicyFile(); 2704 break; 2705 case MESSAGE_SEND_RANKING_UPDATE: 2706 handleSendRankingUpdate(); 2707 break; 2708 case MESSAGE_LISTENER_HINTS_CHANGED: 2709 handleListenerHintsChanged(msg.arg1); 2710 break; 2711 case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED: 2712 handleListenerInterruptionFilterChanged(msg.arg1); 2713 break; 2714 } 2715 } 2716 2717 } 2718 2719 private final class RankingWorkerHandler extends Handler 2720 { 2721 public RankingWorkerHandler(Looper looper) { 2722 super(looper); 2723 } 2724 2725 @Override 2726 public void handleMessage(Message msg) { 2727 switch (msg.what) { 2728 case MESSAGE_RECONSIDER_RANKING: 2729 handleRankingReconsideration(msg); 2730 break; 2731 case MESSAGE_RANKING_CONFIG_CHANGE: 2732 handleRankingConfigChange(); 2733 break; 2734 } 2735 } 2736 } 2737 2738 // Notifications 2739 // ============================================================================ 2740 static int clamp(int x, int low, int high) { 2741 return (x < low) ? low : ((x > high) ? high : x); 2742 } 2743 2744 void sendAccessibilityEvent(Notification notification, CharSequence packageName) { 2745 AccessibilityManager manager = AccessibilityManager.getInstance(getContext()); 2746 if (!manager.isEnabled()) { 2747 return; 2748 } 2749 2750 AccessibilityEvent event = 2751 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); 2752 event.setPackageName(packageName); 2753 event.setClassName(Notification.class.getName()); 2754 event.setParcelableData(notification); 2755 CharSequence tickerText = notification.tickerText; 2756 if (!TextUtils.isEmpty(tickerText)) { 2757 event.getText().add(tickerText); 2758 } 2759 2760 manager.sendAccessibilityEvent(event); 2761 } 2762 2763 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) { 2764 // tell the app 2765 if (sendDelete) { 2766 if (r.getNotification().deleteIntent != null) { 2767 try { 2768 r.getNotification().deleteIntent.send(); 2769 } catch (PendingIntent.CanceledException ex) { 2770 // do nothing - there's no relevant way to recover, and 2771 // no reason to let this propagate 2772 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex); 2773 } 2774 } 2775 } 2776 2777 // status bar 2778 if (r.getNotification().getSmallIcon() != null) { 2779 r.isCanceled = true; 2780 mListeners.notifyRemovedLocked(r.sbn); 2781 } 2782 2783 final String canceledKey = r.getKey(); 2784 2785 // sound 2786 if (canceledKey.equals(mSoundNotificationKey)) { 2787 mSoundNotificationKey = null; 2788 final long identity = Binder.clearCallingIdentity(); 2789 try { 2790 final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); 2791 if (player != null) { 2792 player.stopAsync(); 2793 } 2794 } catch (RemoteException e) { 2795 } finally { 2796 Binder.restoreCallingIdentity(identity); 2797 } 2798 } 2799 2800 // vibrate 2801 if (canceledKey.equals(mVibrateNotificationKey)) { 2802 mVibrateNotificationKey = null; 2803 long identity = Binder.clearCallingIdentity(); 2804 try { 2805 mVibrator.cancel(); 2806 } 2807 finally { 2808 Binder.restoreCallingIdentity(identity); 2809 } 2810 } 2811 2812 // light 2813 mLights.remove(canceledKey); 2814 2815 // Record usage stats 2816 switch (reason) { 2817 case REASON_DELEGATE_CANCEL: 2818 case REASON_DELEGATE_CANCEL_ALL: 2819 case REASON_LISTENER_CANCEL: 2820 case REASON_LISTENER_CANCEL_ALL: 2821 mUsageStats.registerDismissedByUser(r); 2822 break; 2823 case REASON_NOMAN_CANCEL: 2824 case REASON_NOMAN_CANCEL_ALL: 2825 mUsageStats.registerRemovedByApp(r); 2826 break; 2827 } 2828 2829 mNotificationsByKey.remove(r.sbn.getKey()); 2830 String groupKey = r.getGroupKey(); 2831 NotificationRecord groupSummary = mSummaryByGroupKey.get(groupKey); 2832 if (groupSummary != null && groupSummary.getKey().equals(r.getKey())) { 2833 mSummaryByGroupKey.remove(groupKey); 2834 } 2835 2836 // Save it for users of getHistoricalNotifications() 2837 mArchive.record(r.sbn); 2838 2839 final long now = System.currentTimeMillis(); 2840 EventLogTags.writeNotificationCanceled(canceledKey, reason, 2841 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now)); 2842 } 2843 2844 /** 2845 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags} 2846 * and none of the {@code mustNotHaveFlags}. 2847 */ 2848 void cancelNotification(final int callingUid, final int callingPid, 2849 final String pkg, final String tag, final int id, 2850 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete, 2851 final int userId, final int reason, final ManagedServiceInfo listener) { 2852 // In enqueueNotificationInternal notifications are added by scheduling the 2853 // work on the worker handler. Hence, we also schedule the cancel on this 2854 // handler to avoid a scenario where an add notification call followed by a 2855 // remove notification call ends up in not removing the notification. 2856 mHandler.post(new Runnable() { 2857 @Override 2858 public void run() { 2859 String listenerName = listener == null ? null : listener.component.toShortString(); 2860 if (DBG) EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, 2861 userId, mustHaveFlags, mustNotHaveFlags, reason, listenerName); 2862 2863 synchronized (mNotificationList) { 2864 int index = indexOfNotificationLocked(pkg, tag, id, userId); 2865 if (index >= 0) { 2866 NotificationRecord r = mNotificationList.get(index); 2867 2868 // Ideally we'd do this in the caller of this method. However, that would 2869 // require the caller to also find the notification. 2870 if (reason == REASON_DELEGATE_CLICK) { 2871 mUsageStats.registerClickedByUser(r); 2872 } 2873 2874 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) { 2875 return; 2876 } 2877 if ((r.getNotification().flags & mustNotHaveFlags) != 0) { 2878 return; 2879 } 2880 2881 mNotificationList.remove(index); 2882 2883 cancelNotificationLocked(r, sendDelete, reason); 2884 cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName, 2885 REASON_GROUP_SUMMARY_CANCELED); 2886 updateLightsLocked(); 2887 } 2888 } 2889 } 2890 }); 2891 } 2892 2893 /** 2894 * Determine whether the userId applies to the notification in question, either because 2895 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard). 2896 */ 2897 private boolean notificationMatchesUserId(NotificationRecord r, int userId) { 2898 return 2899 // looking for USER_ALL notifications? match everything 2900 userId == UserHandle.USER_ALL 2901 // a notification sent to USER_ALL matches any query 2902 || r.getUserId() == UserHandle.USER_ALL 2903 // an exact user match 2904 || r.getUserId() == userId; 2905 } 2906 2907 /** 2908 * Determine whether the userId applies to the notification in question, either because 2909 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or 2910 * because it matches one of the users profiles. 2911 */ 2912 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) { 2913 return notificationMatchesUserId(r, userId) 2914 || mUserProfiles.isCurrentProfile(r.getUserId()); 2915 } 2916 2917 /** 2918 * Cancels all notifications from a given package that have all of the 2919 * {@code mustHaveFlags}. 2920 */ 2921 boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags, 2922 int mustNotHaveFlags, boolean doit, int userId, int reason, 2923 ManagedServiceInfo listener) { 2924 String listenerName = listener == null ? null : listener.component.toShortString(); 2925 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2926 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason, 2927 listenerName); 2928 2929 synchronized (mNotificationList) { 2930 final int N = mNotificationList.size(); 2931 ArrayList<NotificationRecord> canceledNotifications = null; 2932 for (int i = N-1; i >= 0; --i) { 2933 NotificationRecord r = mNotificationList.get(i); 2934 if (!notificationMatchesUserId(r, userId)) { 2935 continue; 2936 } 2937 // Don't remove notifications to all, if there's no package name specified 2938 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) { 2939 continue; 2940 } 2941 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) { 2942 continue; 2943 } 2944 if ((r.getFlags() & mustNotHaveFlags) != 0) { 2945 continue; 2946 } 2947 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) { 2948 continue; 2949 } 2950 if (canceledNotifications == null) { 2951 canceledNotifications = new ArrayList<>(); 2952 } 2953 canceledNotifications.add(r); 2954 if (!doit) { 2955 return true; 2956 } 2957 mNotificationList.remove(i); 2958 cancelNotificationLocked(r, false, reason); 2959 } 2960 if (doit && canceledNotifications != null) { 2961 final int M = canceledNotifications.size(); 2962 for (int i = 0; i < M; i++) { 2963 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid, 2964 listenerName, REASON_GROUP_SUMMARY_CANCELED); 2965 } 2966 } 2967 if (canceledNotifications != null) { 2968 updateLightsLocked(); 2969 } 2970 return canceledNotifications != null; 2971 } 2972 } 2973 2974 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason, 2975 ManagedServiceInfo listener, boolean includeCurrentProfiles) { 2976 String listenerName = listener == null ? null : listener.component.toShortString(); 2977 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2978 null, userId, 0, 0, reason, listenerName); 2979 2980 ArrayList<NotificationRecord> canceledNotifications = null; 2981 final int N = mNotificationList.size(); 2982 for (int i=N-1; i>=0; i--) { 2983 NotificationRecord r = mNotificationList.get(i); 2984 if (includeCurrentProfiles) { 2985 if (!notificationMatchesCurrentProfiles(r, userId)) { 2986 continue; 2987 } 2988 } else { 2989 if (!notificationMatchesUserId(r, userId)) { 2990 continue; 2991 } 2992 } 2993 2994 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT 2995 | Notification.FLAG_NO_CLEAR)) == 0) { 2996 mNotificationList.remove(i); 2997 cancelNotificationLocked(r, true, reason); 2998 // Make a note so we can cancel children later. 2999 if (canceledNotifications == null) { 3000 canceledNotifications = new ArrayList<>(); 3001 } 3002 canceledNotifications.add(r); 3003 } 3004 } 3005 int M = canceledNotifications != null ? canceledNotifications.size() : 0; 3006 for (int i = 0; i < M; i++) { 3007 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid, 3008 listenerName, REASON_GROUP_SUMMARY_CANCELED); 3009 } 3010 updateLightsLocked(); 3011 } 3012 3013 // Warning: The caller is responsible for invoking updateLightsLocked(). 3014 private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid, 3015 String listenerName, int reason) { 3016 Notification n = r.getNotification(); 3017 if (!n.isGroupSummary()) { 3018 return; 3019 } 3020 3021 String pkg = r.sbn.getPackageName(); 3022 int userId = r.getUserId(); 3023 3024 if (pkg == null) { 3025 if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey()); 3026 return; 3027 } 3028 3029 final int N = mNotificationList.size(); 3030 for (int i = N - 1; i >= 0; i--) { 3031 NotificationRecord childR = mNotificationList.get(i); 3032 StatusBarNotification childSbn = childR.sbn; 3033 if (childR.getNotification().isGroupChild() && 3034 childR.getGroupKey().equals(r.getGroupKey())) { 3035 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(), 3036 childSbn.getTag(), userId, 0, 0, reason, listenerName); 3037 mNotificationList.remove(i); 3038 cancelNotificationLocked(childR, false, reason); 3039 } 3040 } 3041 } 3042 3043 // lock on mNotificationList 3044 void updateLightsLocked() 3045 { 3046 // handle notification lights 3047 NotificationRecord ledNotification = null; 3048 while (ledNotification == null && !mLights.isEmpty()) { 3049 final String owner = mLights.get(mLights.size() - 1); 3050 ledNotification = mNotificationsByKey.get(owner); 3051 if (ledNotification == null) { 3052 Slog.wtfStack(TAG, "LED Notification does not exist: " + owner); 3053 mLights.remove(owner); 3054 } 3055 } 3056 3057 // Don't flash while we are in a call or screen is on 3058 if (ledNotification == null || mInCall || mScreenOn) { 3059 mNotificationLight.turnOff(); 3060 mStatusBar.notificationLightOff(); 3061 } else { 3062 final Notification ledno = ledNotification.sbn.getNotification(); 3063 int ledARGB = ledno.ledARGB; 3064 int ledOnMS = ledno.ledOnMS; 3065 int ledOffMS = ledno.ledOffMS; 3066 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) { 3067 ledARGB = mDefaultNotificationColor; 3068 ledOnMS = mDefaultNotificationLedOn; 3069 ledOffMS = mDefaultNotificationLedOff; 3070 } 3071 if (mNotificationPulseEnabled) { 3072 // pulse repeatedly 3073 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED, 3074 ledOnMS, ledOffMS); 3075 } 3076 // let SystemUI make an independent decision 3077 mStatusBar.notificationLightPulse(ledARGB, ledOnMS, ledOffMS); 3078 } 3079 } 3080 3081 // lock on mNotificationList 3082 int indexOfNotificationLocked(String pkg, String tag, int id, int userId) 3083 { 3084 ArrayList<NotificationRecord> list = mNotificationList; 3085 final int len = list.size(); 3086 for (int i=0; i<len; i++) { 3087 NotificationRecord r = list.get(i); 3088 if (notificationMatchesUserId(r, userId) && r.sbn.getId() == id && 3089 TextUtils.equals(r.sbn.getTag(), tag) && r.sbn.getPackageName().equals(pkg)) { 3090 return i; 3091 } 3092 } 3093 return -1; 3094 } 3095 3096 // lock on mNotificationList 3097 int indexOfNotificationLocked(String key) { 3098 final int N = mNotificationList.size(); 3099 for (int i = 0; i < N; i++) { 3100 if (key.equals(mNotificationList.get(i).getKey())) { 3101 return i; 3102 } 3103 } 3104 return -1; 3105 } 3106 3107 private void updateNotificationPulse() { 3108 synchronized (mNotificationList) { 3109 updateLightsLocked(); 3110 } 3111 } 3112 3113 private static boolean isUidSystem(int uid) { 3114 final int appid = UserHandle.getAppId(uid); 3115 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0); 3116 } 3117 3118 private static boolean isCallerSystem() { 3119 return isUidSystem(Binder.getCallingUid()); 3120 } 3121 3122 private static void checkCallerIsSystem() { 3123 if (isCallerSystem()) { 3124 return; 3125 } 3126 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid()); 3127 } 3128 3129 private static void checkCallerIsSystemOrSameApp(String pkg) { 3130 if (isCallerSystem()) { 3131 return; 3132 } 3133 final int uid = Binder.getCallingUid(); 3134 try { 3135 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo( 3136 pkg, 0, UserHandle.getCallingUserId()); 3137 if (ai == null) { 3138 throw new SecurityException("Unknown package " + pkg); 3139 } 3140 if (!UserHandle.isSameApp(ai.uid, uid)) { 3141 throw new SecurityException("Calling uid " + uid + " gave package" 3142 + pkg + " which is owned by uid " + ai.uid); 3143 } 3144 } catch (RemoteException re) { 3145 throw new SecurityException("Unknown package " + pkg + "\n" + re); 3146 } 3147 } 3148 3149 private static String callStateToString(int state) { 3150 switch (state) { 3151 case TelephonyManager.CALL_STATE_IDLE: return "CALL_STATE_IDLE"; 3152 case TelephonyManager.CALL_STATE_RINGING: return "CALL_STATE_RINGING"; 3153 case TelephonyManager.CALL_STATE_OFFHOOK: return "CALL_STATE_OFFHOOK"; 3154 default: return "CALL_STATE_UNKNOWN_" + state; 3155 } 3156 } 3157 3158 private void listenForCallState() { 3159 TelephonyManager.from(getContext()).listen(new PhoneStateListener() { 3160 @Override 3161 public void onCallStateChanged(int state, String incomingNumber) { 3162 if (mCallState == state) return; 3163 if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state)); 3164 mCallState = state; 3165 } 3166 }, PhoneStateListener.LISTEN_CALL_STATE); 3167 } 3168 3169 /** 3170 * Generates a NotificationRankingUpdate from 'sbns', considering only 3171 * notifications visible to the given listener. 3172 * 3173 * <p>Caller must hold a lock on mNotificationList.</p> 3174 */ 3175 private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) { 3176 int speedBumpIndex = -1; 3177 final int N = mNotificationList.size(); 3178 ArrayList<String> keys = new ArrayList<String>(N); 3179 ArrayList<String> interceptedKeys = new ArrayList<String>(N); 3180 Bundle visibilityOverrides = new Bundle(); 3181 for (int i = 0; i < N; i++) { 3182 NotificationRecord record = mNotificationList.get(i); 3183 if (!isVisibleToListener(record.sbn, info)) { 3184 continue; 3185 } 3186 keys.add(record.sbn.getKey()); 3187 if (record.isIntercepted()) { 3188 interceptedKeys.add(record.sbn.getKey()); 3189 } 3190 if (record.getPackageVisibilityOverride() 3191 != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) { 3192 visibilityOverrides.putInt(record.sbn.getKey(), 3193 record.getPackageVisibilityOverride()); 3194 } 3195 // Find first min-prio notification for speedbump placement. 3196 if (speedBumpIndex == -1 && 3197 // Intrusiveness trumps priority, hence ignore intrusives. 3198 !record.isRecentlyIntrusive() && 3199 // Currently, package priority is either PRIORITY_DEFAULT or PRIORITY_MAX, so 3200 // scanning for PRIORITY_MIN within the package bucket PRIORITY_DEFAULT 3201 // (or lower as a safeguard) is sufficient to find the speedbump index. 3202 // We'll have to revisit this when more package priority buckets are introduced. 3203 record.getPackagePriority() <= Notification.PRIORITY_DEFAULT && 3204 record.sbn.getNotification().priority == Notification.PRIORITY_MIN) { 3205 speedBumpIndex = keys.size() - 1; 3206 } 3207 } 3208 String[] keysAr = keys.toArray(new String[keys.size()]); 3209 String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]); 3210 return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides, 3211 speedBumpIndex); 3212 } 3213 3214 private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) { 3215 if (!listener.enabledAndUserMatches(sbn.getUserId())) { 3216 return false; 3217 } 3218 // TODO: remove this for older listeners. 3219 return true; 3220 } 3221 3222 public class NotificationListeners extends ManagedServices { 3223 3224 private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>(); 3225 private boolean mNotificationGroupsDesired; 3226 3227 public NotificationListeners() { 3228 super(getContext(), mHandler, mNotificationList, mUserProfiles); 3229 } 3230 3231 @Override 3232 protected Config getConfig() { 3233 Config c = new Config(); 3234 c.caption = "notification listener"; 3235 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE; 3236 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS; 3237 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE; 3238 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS; 3239 c.clientLabel = R.string.notification_listener_binding_label; 3240 return c; 3241 } 3242 3243 @Override 3244 protected IInterface asInterface(IBinder binder) { 3245 return INotificationListener.Stub.asInterface(binder); 3246 } 3247 3248 @Override 3249 public void onServiceAdded(ManagedServiceInfo info) { 3250 final INotificationListener listener = (INotificationListener) info.service; 3251 final NotificationRankingUpdate update; 3252 synchronized (mNotificationList) { 3253 updateNotificationGroupsDesiredLocked(); 3254 update = makeRankingUpdateLocked(info); 3255 } 3256 try { 3257 listener.onListenerConnected(update); 3258 } catch (RemoteException e) { 3259 // we tried 3260 } 3261 } 3262 3263 @Override 3264 protected void onServiceRemovedLocked(ManagedServiceInfo removed) { 3265 if (mListenersDisablingEffects.remove(removed)) { 3266 updateListenerHintsLocked(); 3267 updateEffectsSuppressorLocked(); 3268 } 3269 mLightTrimListeners.remove(removed); 3270 updateNotificationGroupsDesiredLocked(); 3271 } 3272 3273 public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) { 3274 if (trim == TRIM_LIGHT) { 3275 mLightTrimListeners.add(info); 3276 } else { 3277 mLightTrimListeners.remove(info); 3278 } 3279 } 3280 3281 public int getOnNotificationPostedTrim(ManagedServiceInfo info) { 3282 return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL; 3283 3284 } 3285 3286 /** 3287 * asynchronously notify all listeners about a new notification 3288 * 3289 * <p> 3290 * Also takes care of removing a notification that has been visible to a listener before, 3291 * but isn't anymore. 3292 */ 3293 public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) { 3294 // Lazily initialized snapshots of the notification. 3295 StatusBarNotification sbnClone = null; 3296 StatusBarNotification sbnCloneLight = null; 3297 3298 for (final ManagedServiceInfo info : mServices) { 3299 boolean sbnVisible = isVisibleToListener(sbn, info); 3300 boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false; 3301 // This notification hasn't been and still isn't visible -> ignore. 3302 if (!oldSbnVisible && !sbnVisible) { 3303 continue; 3304 } 3305 final NotificationRankingUpdate update = makeRankingUpdateLocked(info); 3306 3307 // This notification became invisible -> remove the old one. 3308 if (oldSbnVisible && !sbnVisible) { 3309 final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight(); 3310 mHandler.post(new Runnable() { 3311 @Override 3312 public void run() { 3313 notifyRemoved(info, oldSbnLightClone, update); 3314 } 3315 }); 3316 continue; 3317 } 3318 3319 final int trim = mListeners.getOnNotificationPostedTrim(info); 3320 3321 if (trim == TRIM_LIGHT && sbnCloneLight == null) { 3322 sbnCloneLight = sbn.cloneLight(); 3323 } else if (trim == TRIM_FULL && sbnClone == null) { 3324 sbnClone = sbn.clone(); 3325 } 3326 final StatusBarNotification sbnToPost = 3327 (trim == TRIM_FULL) ? sbnClone : sbnCloneLight; 3328 3329 mHandler.post(new Runnable() { 3330 @Override 3331 public void run() { 3332 notifyPosted(info, sbnToPost, update); 3333 } 3334 }); 3335 } 3336 } 3337 3338 /** 3339 * asynchronously notify all listeners about a removed notification 3340 */ 3341 public void notifyRemovedLocked(StatusBarNotification sbn) { 3342 // make a copy in case changes are made to the underlying Notification object 3343 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the 3344 // notification 3345 final StatusBarNotification sbnLight = sbn.cloneLight(); 3346 for (final ManagedServiceInfo info : mServices) { 3347 if (!isVisibleToListener(sbn, info)) { 3348 continue; 3349 } 3350 final NotificationRankingUpdate update = makeRankingUpdateLocked(info); 3351 mHandler.post(new Runnable() { 3352 @Override 3353 public void run() { 3354 notifyRemoved(info, sbnLight, update); 3355 } 3356 }); 3357 } 3358 } 3359 3360 /** 3361 * asynchronously notify all listeners about a reordering of notifications 3362 */ 3363 public void notifyRankingUpdateLocked() { 3364 for (final ManagedServiceInfo serviceInfo : mServices) { 3365 if (!serviceInfo.isEnabledForCurrentProfiles()) { 3366 continue; 3367 } 3368 final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo); 3369 mHandler.post(new Runnable() { 3370 @Override 3371 public void run() { 3372 notifyRankingUpdate(serviceInfo, update); 3373 } 3374 }); 3375 } 3376 } 3377 3378 public void notifyListenerHintsChangedLocked(final int hints) { 3379 for (final ManagedServiceInfo serviceInfo : mServices) { 3380 if (!serviceInfo.isEnabledForCurrentProfiles()) { 3381 continue; 3382 } 3383 mHandler.post(new Runnable() { 3384 @Override 3385 public void run() { 3386 notifyListenerHintsChanged(serviceInfo, hints); 3387 } 3388 }); 3389 } 3390 } 3391 3392 public void notifyInterruptionFilterChanged(final int interruptionFilter) { 3393 for (final ManagedServiceInfo serviceInfo : mServices) { 3394 if (!serviceInfo.isEnabledForCurrentProfiles()) { 3395 continue; 3396 } 3397 mHandler.post(new Runnable() { 3398 @Override 3399 public void run() { 3400 notifyInterruptionFilterChanged(serviceInfo, interruptionFilter); 3401 } 3402 }); 3403 } 3404 } 3405 3406 private void notifyPosted(final ManagedServiceInfo info, 3407 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) { 3408 final INotificationListener listener = (INotificationListener)info.service; 3409 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn); 3410 try { 3411 listener.onNotificationPosted(sbnHolder, rankingUpdate); 3412 } catch (RemoteException ex) { 3413 Log.e(TAG, "unable to notify listener (posted): " + listener, ex); 3414 } 3415 } 3416 3417 private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn, 3418 NotificationRankingUpdate rankingUpdate) { 3419 if (!info.enabledAndUserMatches(sbn.getUserId())) { 3420 return; 3421 } 3422 final INotificationListener listener = (INotificationListener) info.service; 3423 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn); 3424 try { 3425 listener.onNotificationRemoved(sbnHolder, rankingUpdate); 3426 } catch (RemoteException ex) { 3427 Log.e(TAG, "unable to notify listener (removed): " + listener, ex); 3428 } 3429 } 3430 3431 private void notifyRankingUpdate(ManagedServiceInfo info, 3432 NotificationRankingUpdate rankingUpdate) { 3433 final INotificationListener listener = (INotificationListener) info.service; 3434 try { 3435 listener.onNotificationRankingUpdate(rankingUpdate); 3436 } catch (RemoteException ex) { 3437 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex); 3438 } 3439 } 3440 3441 private void notifyListenerHintsChanged(ManagedServiceInfo info, int hints) { 3442 final INotificationListener listener = (INotificationListener) info.service; 3443 try { 3444 listener.onListenerHintsChanged(hints); 3445 } catch (RemoteException ex) { 3446 Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex); 3447 } 3448 } 3449 3450 private void notifyInterruptionFilterChanged(ManagedServiceInfo info, 3451 int interruptionFilter) { 3452 final INotificationListener listener = (INotificationListener) info.service; 3453 try { 3454 listener.onInterruptionFilterChanged(interruptionFilter); 3455 } catch (RemoteException ex) { 3456 Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex); 3457 } 3458 } 3459 3460 private boolean isListenerPackage(String packageName) { 3461 if (packageName == null) { 3462 return false; 3463 } 3464 // TODO: clean up locking object later 3465 synchronized (mNotificationList) { 3466 for (final ManagedServiceInfo serviceInfo : mServices) { 3467 if (packageName.equals(serviceInfo.component.getPackageName())) { 3468 return true; 3469 } 3470 } 3471 } 3472 return false; 3473 } 3474 3475 /** 3476 * Returns whether any of the currently registered listeners wants to receive notification 3477 * groups. 3478 * 3479 * <p>Currently we assume groups are desired by non-SystemUI listeners.</p> 3480 */ 3481 public boolean notificationGroupsDesired() { 3482 return mNotificationGroupsDesired; 3483 } 3484 3485 private void updateNotificationGroupsDesiredLocked() { 3486 mNotificationGroupsDesired = true; 3487 // No listeners, no groups. 3488 if (mServices.isEmpty()) { 3489 mNotificationGroupsDesired = false; 3490 return; 3491 } 3492 // One listener: Check whether it's SysUI. 3493 if (mServices.size() == 1 && 3494 mServices.get(0).component.getPackageName().equals("com.android.systemui")) { 3495 mNotificationGroupsDesired = false; 3496 return; 3497 } 3498 } 3499 } 3500 3501 public static final class DumpFilter { 3502 public boolean filtered = false; 3503 public String pkgFilter; 3504 public boolean zen; 3505 public long since; 3506 public boolean stats; 3507 public boolean redact = true; 3508 3509 public static DumpFilter parseFromArguments(String[] args) { 3510 final DumpFilter filter = new DumpFilter(); 3511 for (int ai = 0; ai < args.length; ai++) { 3512 final String a = args[ai]; 3513 if ("--noredact".equals(a) || "--reveal".equals(a)) { 3514 filter.redact = false; 3515 } else if ("p".equals(a) || "pkg".equals(a) || "--package".equals(a)) { 3516 if (ai < args.length-1) { 3517 ai++; 3518 filter.pkgFilter = args[ai].trim().toLowerCase(); 3519 if (filter.pkgFilter.isEmpty()) { 3520 filter.pkgFilter = null; 3521 } else { 3522 filter.filtered = true; 3523 } 3524 } 3525 } else if ("--zen".equals(a) || "zen".equals(a)) { 3526 filter.filtered = true; 3527 filter.zen = true; 3528 } else if ("--stats".equals(a)) { 3529 filter.stats = true; 3530 if (ai < args.length-1) { 3531 ai++; 3532 filter.since = Long.valueOf(args[ai]); 3533 } else { 3534 filter.since = 0; 3535 } 3536 } 3537 } 3538 return filter; 3539 } 3540 3541 public boolean matches(StatusBarNotification sbn) { 3542 if (!filtered) return true; 3543 return zen ? true : sbn != null 3544 && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg())); 3545 } 3546 3547 public boolean matches(ComponentName component) { 3548 if (!filtered) return true; 3549 return zen ? true : component != null && matches(component.getPackageName()); 3550 } 3551 3552 public boolean matches(String pkg) { 3553 if (!filtered) return true; 3554 return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter); 3555 } 3556 3557 @Override 3558 public String toString() { 3559 return stats ? "stats" : zen ? "zen" : ('\'' + pkgFilter + '\''); 3560 } 3561 } 3562 3563 /** 3564 * Wrapper for a StatusBarNotification object that allows transfer across a oneway 3565 * binder without sending large amounts of data over a oneway transaction. 3566 */ 3567 private static final class StatusBarNotificationHolder 3568 extends IStatusBarNotificationHolder.Stub { 3569 private StatusBarNotification mValue; 3570 3571 public StatusBarNotificationHolder(StatusBarNotification value) { 3572 mValue = value; 3573 } 3574 3575 /** Get the held value and clear it. This function should only be called once per holder */ 3576 @Override 3577 public StatusBarNotification get() { 3578 StatusBarNotification value = mValue; 3579 mValue = null; 3580 return value; 3581 } 3582 } 3583 3584 private final class PolicyAccess { 3585 private static final String SEPARATOR = ":"; 3586 private final String[] PERM = { 3587 android.Manifest.permission.ACCESS_NOTIFICATION_POLICY 3588 }; 3589 3590 public boolean isPackageGranted(String pkg) { 3591 return pkg != null && getGrantedPackages().contains(pkg); 3592 } 3593 3594 public void put(String pkg, boolean granted) { 3595 if (pkg == null) return; 3596 final ArraySet<String> pkgs = getGrantedPackages(); 3597 boolean changed; 3598 if (granted) { 3599 changed = pkgs.add(pkg); 3600 } else { 3601 changed = pkgs.remove(pkg); 3602 } 3603 if (!changed) return; 3604 final String setting = TextUtils.join(SEPARATOR, pkgs); 3605 final int currentUser = ActivityManager.getCurrentUser(); 3606 Settings.Secure.putStringForUser(getContext().getContentResolver(), 3607 Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES, 3608 setting, 3609 currentUser); 3610 getContext().sendBroadcastAsUser(new Intent(NotificationManager 3611 .ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED) 3612 .setPackage(pkg) 3613 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), new UserHandle(currentUser), null); 3614 } 3615 3616 public ArraySet<String> getGrantedPackages() { 3617 final ArraySet<String> pkgs = new ArraySet<>(); 3618 3619 long identity = Binder.clearCallingIdentity(); 3620 try { 3621 final String setting = Settings.Secure.getStringForUser( 3622 getContext().getContentResolver(), 3623 Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES, 3624 ActivityManager.getCurrentUser()); 3625 if (setting != null) { 3626 final String[] tokens = setting.split(SEPARATOR); 3627 for (int i = 0; i < tokens.length; i++) { 3628 String token = tokens[i]; 3629 if (token != null) { 3630 token.trim(); 3631 } 3632 if (TextUtils.isEmpty(token)) { 3633 continue; 3634 } 3635 pkgs.add(token); 3636 } 3637 } 3638 } finally { 3639 Binder.restoreCallingIdentity(identity); 3640 } 3641 return pkgs; 3642 } 3643 3644 public String[] getRequestingPackages() throws RemoteException { 3645 final ParceledListSlice list = AppGlobals.getPackageManager() 3646 .getPackagesHoldingPermissions(PERM, 0 /*flags*/, 3647 ActivityManager.getCurrentUser()); 3648 final List<PackageInfo> pkgs = list.getList(); 3649 if (pkgs == null || pkgs.isEmpty()) return new String[0]; 3650 final int N = pkgs.size(); 3651 final String[] rt = new String[N]; 3652 for (int i = 0; i < N; i++) { 3653 rt[i] = pkgs.get(i).packageName; 3654 } 3655 return rt; 3656 } 3657 } 3658 } 3659