NotificationManagerService.java revision b408e8ecd22853faa70e97d0596aac9e7dcf5596
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 17package com.android.server.notification; 18 19import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; 20import static org.xmlpull.v1.XmlPullParser.END_TAG; 21import static org.xmlpull.v1.XmlPullParser.START_TAG; 22 23import android.app.ActivityManager; 24import android.app.ActivityManagerNative; 25import android.app.AppOpsManager; 26import android.app.IActivityManager; 27import android.app.INotificationManager; 28import android.app.ITransientNotification; 29import android.app.Notification; 30import android.app.PendingIntent; 31import android.app.StatusBarManager; 32import android.content.BroadcastReceiver; 33import android.content.ComponentName; 34import android.content.ContentResolver; 35import android.content.Context; 36import android.content.Intent; 37import android.content.IntentFilter; 38import android.content.pm.PackageInfo; 39import android.content.pm.PackageManager; 40import android.content.pm.PackageManager.NameNotFoundException; 41import android.content.pm.UserInfo; 42import android.content.res.Resources; 43import android.database.ContentObserver; 44import android.graphics.Bitmap; 45import android.media.AudioManager; 46import android.media.IRingtonePlayer; 47import android.net.Uri; 48import android.os.Binder; 49import android.os.Environment; 50import android.os.Handler; 51import android.os.IBinder; 52import android.os.Message; 53import android.os.Process; 54import android.os.RemoteException; 55import android.os.UserHandle; 56import android.os.UserManager; 57import android.os.Vibrator; 58import android.provider.Settings; 59import android.service.notification.INotificationListener; 60import android.service.notification.StatusBarNotification; 61import android.service.notification.ZenModeConfig; 62import android.telephony.TelephonyManager; 63import android.text.TextUtils; 64import android.util.ArrayMap; 65import android.util.AtomicFile; 66import android.util.Log; 67import android.util.Slog; 68import android.util.SparseArray; 69import android.util.Xml; 70import android.view.accessibility.AccessibilityEvent; 71import android.view.accessibility.AccessibilityManager; 72import android.widget.Toast; 73 74import com.android.internal.R; 75import com.android.internal.notification.NotificationScorer; 76import com.android.internal.util.FastXmlSerializer; 77import com.android.server.EventLogTags; 78import com.android.server.SystemService; 79import com.android.server.lights.Light; 80import com.android.server.lights.LightsManager; 81import com.android.server.notification.NotificationListeners.NotificationListenerInfo; 82import com.android.server.notification.NotificationUsageStats.SingleNotificationStats; 83import com.android.server.statusbar.StatusBarManagerInternal; 84 85import libcore.io.IoUtils; 86 87import org.xmlpull.v1.XmlPullParser; 88import org.xmlpull.v1.XmlPullParserException; 89import org.xmlpull.v1.XmlSerializer; 90 91import java.io.File; 92import java.io.FileDescriptor; 93import java.io.FileInputStream; 94import java.io.FileNotFoundException; 95import java.io.FileOutputStream; 96import java.io.IOException; 97import java.io.PrintWriter; 98import java.lang.reflect.Array; 99import java.util.ArrayDeque; 100import java.util.ArrayList; 101import java.util.Arrays; 102import java.util.HashSet; 103import java.util.Iterator; 104import java.util.List; 105import java.util.NoSuchElementException; 106 107/** {@hide} */ 108public class NotificationManagerService extends SystemService { 109 static final String TAG = "NotificationService"; 110 static final boolean DBG = false; 111 112 static final int MAX_PACKAGE_NOTIFICATIONS = 50; 113 114 // message codes 115 static final int MESSAGE_TIMEOUT = 2; 116 static final int MESSAGE_SAVE_POLICY_FILE = 3; 117 118 static final int LONG_DELAY = 3500; // 3.5 seconds 119 static final int SHORT_DELAY = 2000; // 2 seconds 120 121 static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250}; 122 static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps 123 124 static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION; 125 static final boolean SCORE_ONGOING_HIGHER = false; 126 127 static final int JUNK_SCORE = -1000; 128 static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; 129 static final int SCORE_DISPLAY_THRESHOLD = Notification.PRIORITY_MIN * NOTIFICATION_PRIORITY_MULTIPLIER; 130 131 // Notifications with scores below this will not interrupt the user, either via LED or 132 // sound or vibration 133 static final int SCORE_INTERRUPTION_THRESHOLD = 134 Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER; 135 136 static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true; 137 static final boolean ENABLE_BLOCKED_TOASTS = true; 138 139 private IActivityManager mAm; 140 AudioManager mAudioManager; 141 StatusBarManagerInternal mStatusBar; 142 Vibrator mVibrator; 143 144 final IBinder mForegroundToken = new Binder(); 145 private WorkerHandler mHandler; 146 147 private Light mNotificationLight; 148 Light mAttentionLight; 149 private int mDefaultNotificationColor; 150 private int mDefaultNotificationLedOn; 151 152 private int mDefaultNotificationLedOff; 153 private long[] mDefaultVibrationPattern; 154 155 private long[] mFallbackVibrationPattern; 156 boolean mSystemReady; 157 158 private boolean mDisableNotificationAlerts; 159 NotificationRecord mSoundNotification; 160 NotificationRecord mVibrateNotification; 161 162 // for enabling and disabling notification pulse behavior 163 private boolean mScreenOn = true; 164 private boolean mInCall = false; 165 private boolean mNotificationPulseEnabled; 166 167 // used as a mutex for access to all active notifications & listeners 168 final ArrayList<NotificationRecord> mNotificationList = 169 new ArrayList<NotificationRecord>(); 170 final ArrayMap<String, NotificationRecord> mNotificationsByKey = 171 new ArrayMap<String, NotificationRecord>(); 172 final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>(); 173 174 ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>(); 175 NotificationRecord mLedNotification; 176 177 private AppOpsManager mAppOps; 178 179 // Notification control database. For now just contains disabled packages. 180 private AtomicFile mPolicyFile; 181 private HashSet<String> mBlockedPackages = new HashSet<String>(); 182 183 private static final int DB_VERSION = 1; 184 185 private static final String TAG_BODY = "notification-policy"; 186 private static final String ATTR_VERSION = "version"; 187 188 private static final String TAG_BLOCKED_PKGS = "blocked-packages"; 189 private static final String TAG_PACKAGE = "package"; 190 private static final String ATTR_NAME = "name"; 191 192 final ArrayList<NotificationScorer> mScorers = new ArrayList<NotificationScorer>(); 193 194 private NotificationListeners mListeners; 195 private final UserProfiles mUserProfiles = new UserProfiles(); 196 197 private final NotificationUsageStats mUsageStats = new NotificationUsageStats(); 198 199 private static final String EXTRA_INTERCEPT = "android.intercept"; 200 201 private static final int MY_UID = Process.myUid(); 202 private static final int MY_PID = Process.myPid(); 203 private static final int REASON_DELEGATE_CLICK = 1; 204 private static final int REASON_DELEGATE_CANCEL = 2; 205 private static final int REASON_DELEGATE_CANCEL_ALL = 3; 206 private static final int REASON_DELEGATE_ERROR = 4; 207 private static final int REASON_PACKAGE_CHANGED = 5; 208 private static final int REASON_USER_STOPPED = 6; 209 private static final int REASON_PACKAGE_BANNED = 7; 210 private static final int REASON_NOMAN_CANCEL = 8; 211 private static final int REASON_NOMAN_CANCEL_ALL = 9; 212 private static final int REASON_LISTENER_CANCEL = 10; 213 private static final int REASON_LISTENER_CANCEL_ALL = 11; 214 215 private static class Archive { 216 static final int BUFFER_SIZE = 250; 217 ArrayDeque<StatusBarNotification> mBuffer = new ArrayDeque<StatusBarNotification>(BUFFER_SIZE); 218 219 public Archive() { 220 } 221 222 public String toString() { 223 final StringBuilder sb = new StringBuilder(); 224 final int N = mBuffer.size(); 225 sb.append("Archive ("); 226 sb.append(N); 227 sb.append(" notification"); 228 sb.append((N==1)?")":"s)"); 229 return sb.toString(); 230 } 231 232 public void record(StatusBarNotification nr) { 233 if (mBuffer.size() == BUFFER_SIZE) { 234 mBuffer.removeFirst(); 235 } 236 237 // We don't want to store the heavy bits of the notification in the archive, 238 // but other clients in the system process might be using the object, so we 239 // store a (lightened) copy. 240 mBuffer.addLast(nr.cloneLight()); 241 } 242 243 244 public void clear() { 245 mBuffer.clear(); 246 } 247 248 public Iterator<StatusBarNotification> descendingIterator() { 249 return mBuffer.descendingIterator(); 250 } 251 public Iterator<StatusBarNotification> ascendingIterator() { 252 return mBuffer.iterator(); 253 } 254 public Iterator<StatusBarNotification> filter( 255 final Iterator<StatusBarNotification> iter, final String pkg, final int userId) { 256 return new Iterator<StatusBarNotification>() { 257 StatusBarNotification mNext = findNext(); 258 259 private StatusBarNotification findNext() { 260 while (iter.hasNext()) { 261 StatusBarNotification nr = iter.next(); 262 if ((pkg == null || nr.getPackageName() == pkg) 263 && (userId == UserHandle.USER_ALL || nr.getUserId() == userId)) { 264 return nr; 265 } 266 } 267 return null; 268 } 269 270 @Override 271 public boolean hasNext() { 272 return mNext == null; 273 } 274 275 @Override 276 public StatusBarNotification next() { 277 StatusBarNotification next = mNext; 278 if (next == null) { 279 throw new NoSuchElementException(); 280 } 281 mNext = findNext(); 282 return next; 283 } 284 285 @Override 286 public void remove() { 287 iter.remove(); 288 } 289 }; 290 } 291 292 public StatusBarNotification[] getArray(int count) { 293 if (count == 0) count = Archive.BUFFER_SIZE; 294 final StatusBarNotification[] a 295 = new StatusBarNotification[Math.min(count, mBuffer.size())]; 296 Iterator<StatusBarNotification> iter = descendingIterator(); 297 int i=0; 298 while (iter.hasNext() && i < count) { 299 a[i++] = iter.next(); 300 } 301 return a; 302 } 303 304 public StatusBarNotification[] getArray(int count, String pkg, int userId) { 305 if (count == 0) count = Archive.BUFFER_SIZE; 306 final StatusBarNotification[] a 307 = new StatusBarNotification[Math.min(count, mBuffer.size())]; 308 Iterator<StatusBarNotification> iter = filter(descendingIterator(), pkg, userId); 309 int i=0; 310 while (iter.hasNext() && i < count) { 311 a[i++] = iter.next(); 312 } 313 return a; 314 } 315 316 } 317 318 Archive mArchive = new Archive(); 319 320 private void loadPolicyFile() { 321 synchronized(mPolicyFile) { 322 mBlockedPackages.clear(); 323 324 FileInputStream infile = null; 325 try { 326 infile = mPolicyFile.openRead(); 327 final XmlPullParser parser = Xml.newPullParser(); 328 parser.setInput(infile, null); 329 330 int type; 331 String tag; 332 int version = DB_VERSION; 333 while ((type = parser.next()) != END_DOCUMENT) { 334 tag = parser.getName(); 335 if (type == START_TAG) { 336 if (TAG_BODY.equals(tag)) { 337 version = Integer.parseInt( 338 parser.getAttributeValue(null, ATTR_VERSION)); 339 } else if (TAG_BLOCKED_PKGS.equals(tag)) { 340 while ((type = parser.next()) != END_DOCUMENT) { 341 tag = parser.getName(); 342 if (TAG_PACKAGE.equals(tag)) { 343 mBlockedPackages.add( 344 parser.getAttributeValue(null, ATTR_NAME)); 345 } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) { 346 break; 347 } 348 } 349 } 350 } 351 mZenModeHelper.readXml(parser); 352 } 353 } catch (FileNotFoundException e) { 354 // No data yet 355 } catch (IOException e) { 356 Log.wtf(TAG, "Unable to read notification policy", e); 357 } catch (NumberFormatException e) { 358 Log.wtf(TAG, "Unable to parse notification policy", e); 359 } catch (XmlPullParserException e) { 360 Log.wtf(TAG, "Unable to parse notification policy", e); 361 } finally { 362 IoUtils.closeQuietly(infile); 363 } 364 } 365 } 366 367 public void savePolicyFile() { 368 mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE); 369 mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE); 370 } 371 372 private void handleSavePolicyFile() { 373 Slog.d(TAG, "handleSavePolicyFile"); 374 synchronized (mPolicyFile) { 375 final FileOutputStream stream; 376 try { 377 stream = mPolicyFile.startWrite(); 378 } catch (IOException e) { 379 Slog.w(TAG, "Failed to save policy file", e); 380 return; 381 } 382 383 try { 384 final XmlSerializer out = new FastXmlSerializer(); 385 out.setOutput(stream, "utf-8"); 386 out.startDocument(null, true); 387 out.startTag(null, TAG_BODY); 388 out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION)); 389 mZenModeHelper.writeXml(out); 390 out.endTag(null, TAG_BODY); 391 out.endDocument(); 392 mPolicyFile.finishWrite(stream); 393 } catch (IOException e) { 394 Slog.w(TAG, "Failed to save policy file, restoring backup", e); 395 mPolicyFile.failWrite(stream); 396 } 397 } 398 } 399 400 /** Use this when you actually want to post a notification or toast. 401 * 402 * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*(). 403 */ 404 private boolean noteNotificationOp(String pkg, int uid) { 405 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg) 406 != AppOpsManager.MODE_ALLOWED) { 407 Slog.v(TAG, "notifications are disabled by AppOps for " + pkg); 408 return false; 409 } 410 return true; 411 } 412 413 private static String idDebugString(Context baseContext, String packageName, int id) { 414 Context c = null; 415 416 if (packageName != null) { 417 try { 418 c = baseContext.createPackageContext(packageName, 0); 419 } catch (NameNotFoundException e) { 420 c = baseContext; 421 } 422 } else { 423 c = baseContext; 424 } 425 426 String pkg; 427 String type; 428 String name; 429 430 Resources r = c.getResources(); 431 try { 432 return r.getResourceName(id); 433 } catch (Resources.NotFoundException e) { 434 return "<name unknown>"; 435 } 436 } 437 438 439 440 public static final class NotificationRecord 441 { 442 final StatusBarNotification sbn; 443 final SingleNotificationStats stats = new SingleNotificationStats(); 444 IBinder statusBarKey; 445 446 NotificationRecord(StatusBarNotification sbn) 447 { 448 this.sbn = sbn; 449 } 450 451 public Notification getNotification() { return sbn.getNotification(); } 452 public int getFlags() { return sbn.getNotification().flags; } 453 public int getUserId() { return sbn.getUserId(); } 454 455 void dump(PrintWriter pw, String prefix, Context baseContext) { 456 final Notification notification = sbn.getNotification(); 457 pw.println(prefix + this); 458 pw.println(prefix + " uid=" + sbn.getUid() + " userId=" + sbn.getUserId()); 459 pw.println(prefix + " icon=0x" + Integer.toHexString(notification.icon) 460 + " / " + idDebugString(baseContext, sbn.getPackageName(), notification.icon)); 461 pw.println(prefix + " pri=" + notification.priority + " score=" + sbn.getScore()); 462 pw.println(prefix + " key=" + sbn.getKey()); 463 pw.println(prefix + " contentIntent=" + notification.contentIntent); 464 pw.println(prefix + " deleteIntent=" + notification.deleteIntent); 465 pw.println(prefix + " tickerText=" + notification.tickerText); 466 pw.println(prefix + " contentView=" + notification.contentView); 467 pw.println(prefix + String.format(" defaults=0x%08x flags=0x%08x", 468 notification.defaults, notification.flags)); 469 pw.println(prefix + " sound=" + notification.sound); 470 pw.println(prefix + " vibrate=" + Arrays.toString(notification.vibrate)); 471 pw.println(prefix + String.format(" led=0x%08x onMs=%d offMs=%d", 472 notification.ledARGB, notification.ledOnMS, notification.ledOffMS)); 473 if (notification.actions != null && notification.actions.length > 0) { 474 pw.println(prefix + " actions={"); 475 final int N = notification.actions.length; 476 for (int i=0; i<N; i++) { 477 final Notification.Action action = notification.actions[i]; 478 pw.println(String.format("%s [%d] \"%s\" -> %s", 479 prefix, 480 i, 481 action.title, 482 action.actionIntent.toString() 483 )); 484 } 485 pw.println(prefix + " }"); 486 } 487 if (notification.extras != null && notification.extras.size() > 0) { 488 pw.println(prefix + " extras={"); 489 for (String key : notification.extras.keySet()) { 490 pw.print(prefix + " " + key + "="); 491 Object val = notification.extras.get(key); 492 if (val == null) { 493 pw.println("null"); 494 } else { 495 pw.print(val.toString()); 496 if (val instanceof Bitmap) { 497 pw.print(String.format(" (%dx%d)", 498 ((Bitmap) val).getWidth(), 499 ((Bitmap) val).getHeight())); 500 } else if (val.getClass().isArray()) { 501 pw.println(" {"); 502 final int N = Array.getLength(val); 503 for (int i=0; i<N; i++) { 504 if (i > 0) pw.println(","); 505 pw.print(prefix + " " + Array.get(val, i)); 506 } 507 pw.print("\n" + prefix + " }"); 508 } 509 pw.println(); 510 } 511 } 512 pw.println(prefix + " }"); 513 } 514 pw.println(prefix + " stats=" + stats.toString()); 515 } 516 517 @Override 518 public final String toString() { 519 return String.format( 520 "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s score=%d key=%s: %s)", 521 System.identityHashCode(this), 522 this.sbn.getPackageName(), this.sbn.getUser(), this.sbn.getId(), 523 this.sbn.getTag(), this.sbn.getScore(), this.sbn.getKey(), 524 this.sbn.getNotification()); 525 } 526 } 527 528 private static final class ToastRecord 529 { 530 final int pid; 531 final String pkg; 532 final ITransientNotification callback; 533 int duration; 534 535 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration) 536 { 537 this.pid = pid; 538 this.pkg = pkg; 539 this.callback = callback; 540 this.duration = duration; 541 } 542 543 void update(int duration) { 544 this.duration = duration; 545 } 546 547 void dump(PrintWriter pw, String prefix) { 548 pw.println(prefix + this); 549 } 550 551 @Override 552 public final String toString() 553 { 554 return "ToastRecord{" 555 + Integer.toHexString(System.identityHashCode(this)) 556 + " pkg=" + pkg 557 + " callback=" + callback 558 + " duration=" + duration; 559 } 560 } 561 562 private final NotificationDelegate mNotificationDelegate = new NotificationDelegate() { 563 564 @Override 565 public void onSetDisabled(int status) { 566 synchronized (mNotificationList) { 567 mDisableNotificationAlerts = (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0; 568 if (mDisableNotificationAlerts) { 569 // cancel whatever's going on 570 long identity = Binder.clearCallingIdentity(); 571 try { 572 final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); 573 if (player != null) { 574 player.stopAsync(); 575 } 576 } catch (RemoteException e) { 577 } finally { 578 Binder.restoreCallingIdentity(identity); 579 } 580 581 identity = Binder.clearCallingIdentity(); 582 try { 583 mVibrator.cancel(); 584 } finally { 585 Binder.restoreCallingIdentity(identity); 586 } 587 } 588 } 589 } 590 591 @Override 592 public void onClearAll(int callingUid, int callingPid, int userId) { 593 synchronized (mNotificationList) { 594 cancelAllLocked(callingUid, callingPid, userId, REASON_DELEGATE_CANCEL_ALL, null, 595 /*includeCurrentProfiles*/ true); 596 } 597 } 598 599 @Override 600 public void onNotificationClick(int callingUid, int callingPid, 601 String pkg, String tag, int id, int userId) { 602 cancelNotification(callingUid, callingPid, pkg, tag, id, Notification.FLAG_AUTO_CANCEL, 603 Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_DELEGATE_CLICK, null); 604 } 605 606 @Override 607 public void onNotificationClear(int callingUid, int callingPid, 608 String pkg, String tag, int id, int userId) { 609 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 610 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE, 611 true, userId, REASON_DELEGATE_CANCEL, null); 612 } 613 614 @Override 615 public void onPanelRevealed() { 616 EventLogTags.writeNotificationPanelRevealed(); 617 synchronized (mNotificationList) { 618 // sound 619 mSoundNotification = null; 620 621 long identity = Binder.clearCallingIdentity(); 622 try { 623 final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); 624 if (player != null) { 625 player.stopAsync(); 626 } 627 } catch (RemoteException e) { 628 } finally { 629 Binder.restoreCallingIdentity(identity); 630 } 631 632 // vibrate 633 mVibrateNotification = null; 634 identity = Binder.clearCallingIdentity(); 635 try { 636 mVibrator.cancel(); 637 } finally { 638 Binder.restoreCallingIdentity(identity); 639 } 640 641 // light 642 mLights.clear(); 643 mLedNotification = null; 644 updateLightsLocked(); 645 } 646 } 647 648 @Override 649 public void onPanelHidden() { 650 EventLogTags.writeNotificationPanelHidden(); 651 } 652 653 @Override 654 public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id, 655 int uid, int initialPid, String message, int userId) { 656 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id 657 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")"); 658 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId, 659 REASON_DELEGATE_ERROR, null); 660 long ident = Binder.clearCallingIdentity(); 661 try { 662 ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg, 663 "Bad notification posted from package " + pkg 664 + ": " + message); 665 } catch (RemoteException e) { 666 } 667 Binder.restoreCallingIdentity(ident); 668 } 669 670 @Override 671 public boolean allowDisable(int what, IBinder token, String pkg) { 672 return mZenModeHelper.allowDisable(what, token, pkg); 673 } 674 675 @Override 676 public void onNotificationVisibilityChanged( 677 String[] newlyVisibleKeys, String[] noLongerVisibleKeys) { 678 // Using ';' as separator since eventlogs uses ',' to separate 679 // args. 680 EventLogTags.writeNotificationVisibilityChanged( 681 TextUtils.join(";", newlyVisibleKeys), 682 TextUtils.join(";", noLongerVisibleKeys)); 683 } 684 }; 685 686 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 687 @Override 688 public void onReceive(Context context, Intent intent) { 689 String action = intent.getAction(); 690 691 boolean queryRestart = false; 692 boolean queryRemove = false; 693 boolean packageChanged = false; 694 boolean cancelNotifications = true; 695 696 if (action.equals(Intent.ACTION_PACKAGE_ADDED) 697 || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED)) 698 || action.equals(Intent.ACTION_PACKAGE_RESTARTED) 699 || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED)) 700 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART)) 701 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { 702 String pkgList[] = null; 703 boolean queryReplace = queryRemove && 704 intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); 705 if (DBG) Slog.i(TAG, "queryReplace=" + queryReplace); 706 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { 707 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); 708 } else if (queryRestart) { 709 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES); 710 } else { 711 Uri uri = intent.getData(); 712 if (uri == null) { 713 return; 714 } 715 String pkgName = uri.getSchemeSpecificPart(); 716 if (pkgName == null) { 717 return; 718 } 719 if (packageChanged) { 720 // We cancel notifications for packages which have just been disabled 721 try { 722 final int enabled = getContext().getPackageManager() 723 .getApplicationEnabledSetting(pkgName); 724 if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED 725 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) { 726 cancelNotifications = false; 727 } 728 } catch (IllegalArgumentException e) { 729 // Package doesn't exist; probably racing with uninstall. 730 // cancelNotifications is already true, so nothing to do here. 731 if (DBG) { 732 Slog.i(TAG, "Exception trying to look up app enabled setting", e); 733 } 734 } 735 } 736 pkgList = new String[]{pkgName}; 737 } 738 739 if (pkgList != null && (pkgList.length > 0)) { 740 for (String pkgName : pkgList) { 741 if (cancelNotifications) { 742 cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart, 743 UserHandle.USER_ALL, REASON_PACKAGE_CHANGED, null); 744 } 745 } 746 } 747 mListeners.onPackagesChanged(queryReplace, pkgList); 748 } else if (action.equals(Intent.ACTION_SCREEN_ON)) { 749 // Keep track of screen on/off state, but do not turn off the notification light 750 // until user passes through the lock screen or views the notification. 751 mScreenOn = true; 752 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { 753 mScreenOn = false; 754 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) { 755 mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK 756 .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE)); 757 updateNotificationPulse(); 758 } else if (action.equals(Intent.ACTION_USER_STOPPED)) { 759 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); 760 if (userHandle >= 0) { 761 cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle, 762 REASON_USER_STOPPED, null); 763 } 764 } else if (action.equals(Intent.ACTION_USER_PRESENT)) { 765 // turn off LED when user passes through lock screen 766 mNotificationLight.turnOff(); 767 } else if (action.equals(Intent.ACTION_USER_SWITCHED)) { 768 // reload per-user settings 769 mSettingsObserver.update(null); 770 mUserProfiles.updateCache(context); 771 } else if (action.equals(Intent.ACTION_USER_ADDED)) { 772 mUserProfiles.updateCache(context); 773 } 774 } 775 }; 776 777 class SettingsObserver extends ContentObserver { 778 private final Uri NOTIFICATION_LIGHT_PULSE_URI 779 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE); 780 781 SettingsObserver(Handler handler) { 782 super(handler); 783 } 784 785 void observe() { 786 ContentResolver resolver = getContext().getContentResolver(); 787 resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI, 788 false, this, UserHandle.USER_ALL); 789 update(null); 790 } 791 792 @Override public void onChange(boolean selfChange, Uri uri) { 793 update(uri); 794 } 795 796 public void update(Uri uri) { 797 ContentResolver resolver = getContext().getContentResolver(); 798 if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) { 799 boolean pulseEnabled = Settings.System.getInt(resolver, 800 Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0; 801 if (mNotificationPulseEnabled != pulseEnabled) { 802 mNotificationPulseEnabled = pulseEnabled; 803 updateNotificationPulse(); 804 } 805 } 806 } 807 } 808 809 private SettingsObserver mSettingsObserver; 810 private ZenModeHelper mZenModeHelper; 811 812 static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) { 813 int[] ar = r.getIntArray(resid); 814 if (ar == null) { 815 return def; 816 } 817 final int len = ar.length > maxlen ? maxlen : ar.length; 818 long[] out = new long[len]; 819 for (int i=0; i<len; i++) { 820 out[i] = ar[i]; 821 } 822 return out; 823 } 824 825 public NotificationManagerService(Context context) { 826 super(context); 827 } 828 829 @Override 830 public void onStart() { 831 mAm = ActivityManagerNative.getDefault(); 832 mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE); 833 mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE); 834 835 mHandler = new WorkerHandler(); 836 mZenModeHelper = new ZenModeHelper(getContext(), mHandler); 837 mZenModeHelper.setCallback(new ZenModeHelper.Callback() { 838 @Override 839 public void onConfigChanged() { 840 savePolicyFile(); 841 } 842 }); 843 final File systemDir = new File(Environment.getDataDirectory(), "system"); 844 mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml")); 845 846 importOldBlockDb(); 847 848 mListeners = new NotificationListeners(getContext(), 849 mHandler, mNotificationList, mUserProfiles) { 850 @Override 851 public void onServiceAdded(INotificationListener listener) { 852 final String[] keys = getActiveNotificationKeysFromListener(listener); 853 try { 854 listener.onListenerConnected(keys); 855 } catch (RemoteException e) { 856 // we tried 857 } 858 } 859 }; 860 mStatusBar = getLocalService(StatusBarManagerInternal.class); 861 mStatusBar.setNotificationDelegate(mNotificationDelegate); 862 863 final LightsManager lights = getLocalService(LightsManager.class); 864 mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS); 865 mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION); 866 867 Resources resources = getContext().getResources(); 868 mDefaultNotificationColor = resources.getColor( 869 R.color.config_defaultNotificationColor); 870 mDefaultNotificationLedOn = resources.getInteger( 871 R.integer.config_defaultNotificationLedOn); 872 mDefaultNotificationLedOff = resources.getInteger( 873 R.integer.config_defaultNotificationLedOff); 874 875 mDefaultVibrationPattern = getLongArray(resources, 876 R.array.config_defaultNotificationVibePattern, 877 VIBRATE_PATTERN_MAXLEN, 878 DEFAULT_VIBRATE_PATTERN); 879 880 mFallbackVibrationPattern = getLongArray(resources, 881 R.array.config_notificationFallbackVibePattern, 882 VIBRATE_PATTERN_MAXLEN, 883 DEFAULT_VIBRATE_PATTERN); 884 885 // Don't start allowing notifications until the setup wizard has run once. 886 // After that, including subsequent boots, init with notifications turned on. 887 // This works on the first boot because the setup wizard will toggle this 888 // flag at least once and we'll go back to 0 after that. 889 if (0 == Settings.Global.getInt(getContext().getContentResolver(), 890 Settings.Global.DEVICE_PROVISIONED, 0)) { 891 mDisableNotificationAlerts = true; 892 } 893 mZenModeHelper.updateZenMode(); 894 895 mUserProfiles.updateCache(getContext()); 896 897 // register for various Intents 898 IntentFilter filter = new IntentFilter(); 899 filter.addAction(Intent.ACTION_SCREEN_ON); 900 filter.addAction(Intent.ACTION_SCREEN_OFF); 901 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); 902 filter.addAction(Intent.ACTION_USER_PRESENT); 903 filter.addAction(Intent.ACTION_USER_STOPPED); 904 filter.addAction(Intent.ACTION_USER_SWITCHED); 905 filter.addAction(Intent.ACTION_USER_ADDED); 906 getContext().registerReceiver(mIntentReceiver, filter); 907 IntentFilter pkgFilter = new IntentFilter(); 908 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED); 909 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 910 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); 911 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); 912 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); 913 pkgFilter.addDataScheme("package"); 914 getContext().registerReceiver(mIntentReceiver, pkgFilter); 915 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); 916 getContext().registerReceiver(mIntentReceiver, sdFilter); 917 918 mSettingsObserver = new SettingsObserver(mHandler); 919 920 // spin up NotificationScorers 921 String[] notificationScorerNames = resources.getStringArray( 922 R.array.config_notificationScorers); 923 for (String scorerName : notificationScorerNames) { 924 try { 925 Class<?> scorerClass = getContext().getClassLoader().loadClass(scorerName); 926 NotificationScorer scorer = (NotificationScorer) scorerClass.newInstance(); 927 scorer.initialize(getContext()); 928 mScorers.add(scorer); 929 } catch (ClassNotFoundException e) { 930 Slog.w(TAG, "Couldn't find scorer " + scorerName + ".", e); 931 } catch (InstantiationException e) { 932 Slog.w(TAG, "Couldn't instantiate scorer " + scorerName + ".", e); 933 } catch (IllegalAccessException e) { 934 Slog.w(TAG, "Problem accessing scorer " + scorerName + ".", e); 935 } 936 } 937 938 publishBinderService(Context.NOTIFICATION_SERVICE, mService); 939 publishLocalService(NotificationManagerInternal.class, mInternalService); 940 } 941 942 /** 943 * Read the old XML-based app block database and import those blockages into the AppOps system. 944 */ 945 private void importOldBlockDb() { 946 loadPolicyFile(); 947 948 PackageManager pm = getContext().getPackageManager(); 949 for (String pkg : mBlockedPackages) { 950 PackageInfo info = null; 951 try { 952 info = pm.getPackageInfo(pkg, 0); 953 setNotificationsEnabledForPackageImpl(pkg, info.applicationInfo.uid, false); 954 } catch (NameNotFoundException e) { 955 // forget you 956 } 957 } 958 mBlockedPackages.clear(); 959 } 960 961 @Override 962 public void onBootPhase(int phase) { 963 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { 964 // no beeping until we're basically done booting 965 mSystemReady = true; 966 967 // Grab our optional AudioService 968 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); 969 970 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { 971 // This observer will force an update when observe is called, causing us to 972 // bind to listener services. 973 mSettingsObserver.observe(); 974 mListeners.onBootPhaseAppsCanStart(); 975 } 976 } 977 978 void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) { 979 Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg); 980 981 mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg, 982 enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED); 983 984 // Now, cancel any outstanding notifications that are part of a just-disabled app 985 if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) { 986 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid), 987 REASON_PACKAGE_BANNED, null); 988 } 989 } 990 991 private final IBinder mService = new INotificationManager.Stub() { 992 // Toasts 993 // ============================================================================ 994 995 @Override 996 public void enqueueToast(String pkg, ITransientNotification callback, int duration) 997 { 998 if (DBG) { 999 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback 1000 + " duration=" + duration); 1001 } 1002 1003 if (pkg == null || callback == null) { 1004 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback); 1005 return ; 1006 } 1007 1008 final boolean isSystemToast = 1009 NotificationUtil.isCallerSystem() || ("android".equals(pkg)); 1010 1011 if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) { 1012 if (!isSystemToast) { 1013 Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request."); 1014 return; 1015 } 1016 } 1017 1018 synchronized (mToastQueue) { 1019 int callingPid = Binder.getCallingPid(); 1020 long callingId = Binder.clearCallingIdentity(); 1021 try { 1022 ToastRecord record; 1023 int index = indexOfToastLocked(pkg, callback); 1024 // If it's already in the queue, we update it in place, we don't 1025 // move it to the end of the queue. 1026 if (index >= 0) { 1027 record = mToastQueue.get(index); 1028 record.update(duration); 1029 } else { 1030 // Limit the number of toasts that any given package except the android 1031 // package can enqueue. Prevents DOS attacks and deals with leaks. 1032 if (!isSystemToast) { 1033 int count = 0; 1034 final int N = mToastQueue.size(); 1035 for (int i=0; i<N; i++) { 1036 final ToastRecord r = mToastQueue.get(i); 1037 if (r.pkg.equals(pkg)) { 1038 count++; 1039 if (count >= MAX_PACKAGE_NOTIFICATIONS) { 1040 Slog.e(TAG, "Package has already posted " + count 1041 + " toasts. Not showing more. Package=" + pkg); 1042 return; 1043 } 1044 } 1045 } 1046 } 1047 1048 record = new ToastRecord(callingPid, pkg, callback, duration); 1049 mToastQueue.add(record); 1050 index = mToastQueue.size() - 1; 1051 keepProcessAliveLocked(callingPid); 1052 } 1053 // If it's at index 0, it's the current toast. It doesn't matter if it's 1054 // new or just been updated. Call back and tell it to show itself. 1055 // If the callback fails, this will remove it from the list, so don't 1056 // assume that it's valid after this. 1057 if (index == 0) { 1058 showNextToastLocked(); 1059 } 1060 } finally { 1061 Binder.restoreCallingIdentity(callingId); 1062 } 1063 } 1064 } 1065 1066 @Override 1067 public void cancelToast(String pkg, ITransientNotification callback) { 1068 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback); 1069 1070 if (pkg == null || callback == null) { 1071 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback); 1072 return ; 1073 } 1074 1075 synchronized (mToastQueue) { 1076 long callingId = Binder.clearCallingIdentity(); 1077 try { 1078 int index = indexOfToastLocked(pkg, callback); 1079 if (index >= 0) { 1080 cancelToastLocked(index); 1081 } else { 1082 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg 1083 + " callback=" + callback); 1084 } 1085 } finally { 1086 Binder.restoreCallingIdentity(callingId); 1087 } 1088 } 1089 } 1090 1091 @Override 1092 public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id, 1093 Notification notification, int[] idOut, int userId) throws RemoteException { 1094 enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(), 1095 Binder.getCallingPid(), tag, id, notification, idOut, userId); 1096 } 1097 1098 @Override 1099 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) { 1100 NotificationUtil.checkCallerIsSystemOrSameApp(pkg); 1101 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 1102 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg); 1103 // Don't allow client applications to cancel foreground service notis. 1104 cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0, 1105 Binder.getCallingUid() == Process.SYSTEM_UID 1106 ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_NOMAN_CANCEL, 1107 null); 1108 } 1109 1110 @Override 1111 public void cancelAllNotifications(String pkg, int userId) { 1112 NotificationUtil.checkCallerIsSystemOrSameApp(pkg); 1113 1114 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 1115 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg); 1116 1117 // Calling from user space, don't allow the canceling of actively 1118 // running foreground services. 1119 cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(), 1120 pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId, 1121 REASON_NOMAN_CANCEL_ALL, null); 1122 } 1123 1124 @Override 1125 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) { 1126 NotificationUtil.checkCallerIsSystem(); 1127 1128 setNotificationsEnabledForPackageImpl(pkg, uid, enabled); 1129 } 1130 1131 /** 1132 * Use this when you just want to know if notifications are OK for this package. 1133 */ 1134 @Override 1135 public boolean areNotificationsEnabledForPackage(String pkg, int uid) { 1136 NotificationUtil.checkCallerIsSystem(); 1137 return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg) 1138 == AppOpsManager.MODE_ALLOWED); 1139 } 1140 1141 /** 1142 * System-only API for getting a list of current (i.e. not cleared) notifications. 1143 * 1144 * Requires ACCESS_NOTIFICATIONS which is signature|system. 1145 */ 1146 @Override 1147 public StatusBarNotification[] getActiveNotifications(String callingPkg) { 1148 // enforce() will ensure the calling uid has the correct permission 1149 getContext().enforceCallingOrSelfPermission( 1150 android.Manifest.permission.ACCESS_NOTIFICATIONS, 1151 "NotificationManagerService.getActiveNotifications"); 1152 1153 StatusBarNotification[] tmp = null; 1154 int uid = Binder.getCallingUid(); 1155 1156 // noteOp will check to make sure the callingPkg matches the uid 1157 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg) 1158 == AppOpsManager.MODE_ALLOWED) { 1159 synchronized (mNotificationList) { 1160 tmp = new StatusBarNotification[mNotificationList.size()]; 1161 final int N = mNotificationList.size(); 1162 for (int i=0; i<N; i++) { 1163 tmp[i] = mNotificationList.get(i).sbn; 1164 } 1165 } 1166 } 1167 return tmp; 1168 } 1169 1170 /** 1171 * System-only API for getting a list of recent (cleared, no longer shown) notifications. 1172 * 1173 * Requires ACCESS_NOTIFICATIONS which is signature|system. 1174 */ 1175 @Override 1176 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) { 1177 // enforce() will ensure the calling uid has the correct permission 1178 getContext().enforceCallingOrSelfPermission( 1179 android.Manifest.permission.ACCESS_NOTIFICATIONS, 1180 "NotificationManagerService.getHistoricalNotifications"); 1181 1182 StatusBarNotification[] tmp = null; 1183 int uid = Binder.getCallingUid(); 1184 1185 // noteOp will check to make sure the callingPkg matches the uid 1186 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg) 1187 == AppOpsManager.MODE_ALLOWED) { 1188 synchronized (mArchive) { 1189 tmp = mArchive.getArray(count); 1190 } 1191 } 1192 return tmp; 1193 } 1194 1195 /** 1196 * Register a listener binder directly with the notification manager. 1197 * 1198 * Only works with system callers. Apps should extend 1199 * {@link android.service.notification.NotificationListenerService}. 1200 */ 1201 @Override 1202 public void registerListener(final INotificationListener listener, 1203 final ComponentName component, final int userid) { 1204 NotificationUtil.checkCallerIsSystem(); 1205 mListeners.registerListener(listener, component, userid); 1206 } 1207 1208 /** 1209 * Remove a listener binder directly 1210 */ 1211 @Override 1212 public void unregisterListener(INotificationListener listener, int userid) { 1213 mListeners.unregisterListener(listener, userid); 1214 } 1215 1216 /** 1217 * Allow an INotificationListener to simulate a "clear all" operation. 1218 * 1219 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications} 1220 * 1221 * @param token The binder for the listener, to check that the caller is allowed 1222 */ 1223 @Override 1224 public void cancelNotificationsFromListener(INotificationListener token, String[] keys) { 1225 final int callingUid = Binder.getCallingUid(); 1226 final int callingPid = Binder.getCallingPid(); 1227 long identity = Binder.clearCallingIdentity(); 1228 try { 1229 synchronized (mNotificationList) { 1230 final NotificationListenerInfo info = 1231 mListeners.checkListenerTokenLocked(token); 1232 if (keys != null) { 1233 final int N = keys.length; 1234 for (int i = 0; i < N; i++) { 1235 NotificationRecord r = mNotificationsByKey.get(keys[i]); 1236 final int userId = r.sbn.getUserId(); 1237 if (userId != info.userid && userId != UserHandle.USER_ALL && 1238 !mUserProfiles.isCurrentProfile(userId)) { 1239 throw new SecurityException("Disallowed call from listener: " 1240 + info.listener); 1241 } 1242 if (r != null) { 1243 cancelNotificationFromListenerLocked(info, callingUid, callingPid, 1244 r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(), 1245 userId); 1246 } 1247 } 1248 } else { 1249 cancelAllLocked(callingUid, callingPid, info.userid, 1250 REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles()); 1251 } 1252 } 1253 } finally { 1254 Binder.restoreCallingIdentity(identity); 1255 } 1256 } 1257 1258 private void cancelNotificationFromListenerLocked(NotificationListenerInfo info, 1259 int callingUid, int callingPid, String pkg, String tag, int id, int userId) { 1260 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 1261 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE, 1262 true, 1263 userId, REASON_LISTENER_CANCEL, info); 1264 } 1265 1266 /** 1267 * Allow an INotificationListener to simulate clearing (dismissing) a single notification. 1268 * 1269 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear} 1270 * 1271 * @param token The binder for the listener, to check that the caller is allowed 1272 */ 1273 @Override 1274 public void cancelNotificationFromListener(INotificationListener token, String pkg, 1275 String tag, int id) { 1276 final int callingUid = Binder.getCallingUid(); 1277 final int callingPid = Binder.getCallingPid(); 1278 long identity = Binder.clearCallingIdentity(); 1279 try { 1280 synchronized (mNotificationList) { 1281 final NotificationListenerInfo info = 1282 mListeners.checkListenerTokenLocked(token); 1283 if (info.supportsProfiles()) { 1284 Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) " 1285 + "from " + info.component 1286 + " use cancelNotification(key) instead."); 1287 } else { 1288 cancelNotificationFromListenerLocked(info, callingUid, callingPid, 1289 pkg, tag, id, info.userid); 1290 } 1291 } 1292 } finally { 1293 Binder.restoreCallingIdentity(identity); 1294 } 1295 } 1296 1297 /** 1298 * Allow an INotificationListener to request the list of outstanding notifications seen by 1299 * the current user. Useful when starting up, after which point the listener callbacks 1300 * should be used. 1301 * 1302 * @param token The binder for the listener, to check that the caller is allowed 1303 */ 1304 @Override 1305 public StatusBarNotification[] getActiveNotificationsFromListener( 1306 INotificationListener token, String[] keys) { 1307 synchronized (mNotificationList) { 1308 final NotificationListenerInfo info = mListeners.checkListenerTokenLocked(token); 1309 final ArrayList<StatusBarNotification> list 1310 = new ArrayList<StatusBarNotification>(); 1311 if (keys == null) { 1312 final int N = mNotificationList.size(); 1313 for (int i=0; i<N; i++) { 1314 StatusBarNotification sbn = mNotificationList.get(i).sbn; 1315 if (info.enabledAndUserMatches(sbn)) { 1316 list.add(sbn); 1317 } 1318 } 1319 } else { 1320 final int N = keys.length; 1321 for (int i=0; i<N; i++) { 1322 NotificationRecord r = mNotificationsByKey.get(keys[i]); 1323 if (r != null && info.enabledAndUserMatches(r.sbn)) { 1324 list.add(r.sbn); 1325 } 1326 } 1327 } 1328 return list.toArray(new StatusBarNotification[list.size()]); 1329 } 1330 } 1331 1332 @Override 1333 public String[] getActiveNotificationKeysFromListener(INotificationListener token) { 1334 return NotificationManagerService.this.getActiveNotificationKeysFromListener(token); 1335 } 1336 1337 @Override 1338 public ZenModeConfig getZenModeConfig() { 1339 NotificationUtil.checkCallerIsSystem(); 1340 return mZenModeHelper.getConfig(); 1341 } 1342 1343 @Override 1344 public boolean setZenModeConfig(ZenModeConfig config) { 1345 NotificationUtil.checkCallerIsSystem(); 1346 return mZenModeHelper.setConfig(config); 1347 } 1348 1349 @Override 1350 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1351 if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 1352 != PackageManager.PERMISSION_GRANTED) { 1353 pw.println("Permission Denial: can't dump NotificationManager from from pid=" 1354 + Binder.getCallingPid() 1355 + ", uid=" + Binder.getCallingUid()); 1356 return; 1357 } 1358 1359 dumpImpl(pw); 1360 } 1361 }; 1362 1363 private String[] getActiveNotificationKeysFromListener(INotificationListener token) { 1364 synchronized (mNotificationList) { 1365 final NotificationListenerInfo info = mListeners.checkListenerTokenLocked(token); 1366 final ArrayList<String> keys = new ArrayList<String>(); 1367 final int N = mNotificationList.size(); 1368 for (int i=0; i<N; i++) { 1369 final StatusBarNotification sbn = mNotificationList.get(i).sbn; 1370 if (info.enabledAndUserMatches(sbn)) { 1371 keys.add(sbn.getKey()); 1372 } 1373 } 1374 return keys.toArray(new String[keys.size()]); 1375 } 1376 } 1377 1378 void dumpImpl(PrintWriter pw) { 1379 pw.println("Current Notification Manager state:"); 1380 1381 mListeners.dump(pw); 1382 1383 int N; 1384 1385 synchronized (mToastQueue) { 1386 N = mToastQueue.size(); 1387 if (N > 0) { 1388 pw.println(" Toast Queue:"); 1389 for (int i=0; i<N; i++) { 1390 mToastQueue.get(i).dump(pw, " "); 1391 } 1392 pw.println(" "); 1393 } 1394 1395 } 1396 1397 synchronized (mNotificationList) { 1398 N = mNotificationList.size(); 1399 if (N > 0) { 1400 pw.println(" Notification List:"); 1401 for (int i=0; i<N; i++) { 1402 mNotificationList.get(i).dump(pw, " ", getContext()); 1403 } 1404 pw.println(" "); 1405 } 1406 1407 N = mLights.size(); 1408 if (N > 0) { 1409 pw.println(" Lights List:"); 1410 for (int i=0; i<N; i++) { 1411 pw.println(" " + mLights.get(i)); 1412 } 1413 pw.println(" "); 1414 } 1415 1416 pw.println(" mSoundNotification=" + mSoundNotification); 1417 pw.println(" mVibrateNotification=" + mVibrateNotification); 1418 pw.println(" mDisableNotificationAlerts=" + mDisableNotificationAlerts); 1419 pw.println(" mSystemReady=" + mSystemReady); 1420 pw.println(" mArchive=" + mArchive.toString()); 1421 Iterator<StatusBarNotification> iter = mArchive.descendingIterator(); 1422 int i=0; 1423 while (iter.hasNext()) { 1424 pw.println(" " + iter.next()); 1425 if (++i >= 5) { 1426 if (iter.hasNext()) pw.println(" ..."); 1427 break; 1428 } 1429 } 1430 1431 pw.println("\n Usage Stats:"); 1432 mUsageStats.dump(pw, " "); 1433 1434 pw.println("\n Zen Mode:"); 1435 mZenModeHelper.dump(pw, " "); 1436 } 1437 } 1438 1439 /** 1440 * The private API only accessible to the system process. 1441 */ 1442 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() { 1443 @Override 1444 public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid, 1445 String tag, int id, Notification notification, int[] idReceived, int userId) { 1446 enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification, 1447 idReceived, userId); 1448 } 1449 }; 1450 1451 void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid, 1452 final int callingPid, final String tag, final int id, final Notification notification, 1453 int[] idOut, int incomingUserId) { 1454 if (DBG) { 1455 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id 1456 + " notification=" + notification); 1457 } 1458 NotificationUtil.checkCallerIsSystemOrSameApp(pkg); 1459 final boolean isSystemNotification = 1460 NotificationUtil.isUidSystem(callingUid) || ("android".equals(pkg)); 1461 1462 final int userId = ActivityManager.handleIncomingUser(callingPid, 1463 callingUid, incomingUserId, true, false, "enqueueNotification", pkg); 1464 final UserHandle user = new UserHandle(userId); 1465 1466 // Limit the number of notifications that any given package except the android 1467 // package can enqueue. Prevents DOS attacks and deals with leaks. 1468 if (!isSystemNotification) { 1469 synchronized (mNotificationList) { 1470 int count = 0; 1471 final int N = mNotificationList.size(); 1472 for (int i=0; i<N; i++) { 1473 final NotificationRecord r = mNotificationList.get(i); 1474 if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) { 1475 count++; 1476 if (count >= MAX_PACKAGE_NOTIFICATIONS) { 1477 Slog.e(TAG, "Package has already posted " + count 1478 + " notifications. Not showing more. package=" + pkg); 1479 return; 1480 } 1481 } 1482 } 1483 } 1484 } 1485 1486 // This conditional is a dirty hack to limit the logging done on 1487 // behalf of the download manager without affecting other apps. 1488 if (!pkg.equals("com.android.providers.downloads") 1489 || Log.isLoggable("DownloadManager", Log.VERBOSE)) { 1490 EventLogTags.writeNotificationEnqueue(callingUid, callingPid, 1491 pkg, id, tag, userId, notification.toString()); 1492 } 1493 1494 if (pkg == null || notification == null) { 1495 throw new IllegalArgumentException("null not allowed: pkg=" + pkg 1496 + " id=" + id + " notification=" + notification); 1497 } 1498 if (notification.icon != 0) { 1499 if (notification.contentView == null) { 1500 throw new IllegalArgumentException("contentView required: pkg=" + pkg 1501 + " id=" + id + " notification=" + notification); 1502 } 1503 } 1504 1505 mHandler.post(new Runnable() { 1506 @Override 1507 public void run() { 1508 1509 // === Scoring === 1510 1511 // 0. Sanitize inputs 1512 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN, 1513 Notification.PRIORITY_MAX); 1514 // Migrate notification flags to scores 1515 if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) { 1516 if (notification.priority < Notification.PRIORITY_MAX) { 1517 notification.priority = Notification.PRIORITY_MAX; 1518 } 1519 } else if (SCORE_ONGOING_HIGHER && 1520 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) { 1521 if (notification.priority < Notification.PRIORITY_HIGH) { 1522 notification.priority = Notification.PRIORITY_HIGH; 1523 } 1524 } 1525 1526 // 1. initial score: buckets of 10, around the app 1527 int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20] 1528 1529 // 2. Consult external heuristics (TBD) 1530 1531 // 3. Apply local rules 1532 1533 int initialScore = score; 1534 if (!mScorers.isEmpty()) { 1535 if (DBG) Slog.v(TAG, "Initial score is " + score + "."); 1536 for (NotificationScorer scorer : mScorers) { 1537 try { 1538 score = scorer.getScore(notification, score); 1539 } catch (Throwable t) { 1540 Slog.w(TAG, "Scorer threw on .getScore.", t); 1541 } 1542 } 1543 if (DBG) Slog.v(TAG, "Final score is " + score + "."); 1544 } 1545 1546 // add extra to indicate score modified by NotificationScorer 1547 notification.extras.putBoolean(Notification.EXTRA_SCORE_MODIFIED, 1548 score != initialScore); 1549 1550 // blocked apps 1551 if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) { 1552 if (!isSystemNotification) { 1553 score = JUNK_SCORE; 1554 Slog.e(TAG, "Suppressing notification from package " + pkg 1555 + " by user request."); 1556 } 1557 } 1558 1559 if (DBG) { 1560 Slog.v(TAG, "Assigned score=" + score + " to " + notification); 1561 } 1562 1563 if (score < SCORE_DISPLAY_THRESHOLD) { 1564 // Notification will be blocked because the score is too low. 1565 return; 1566 } 1567 1568 // Is this notification intercepted by zen mode? 1569 final boolean intercept = mZenModeHelper.shouldIntercept(pkg, notification); 1570 notification.extras.putBoolean(EXTRA_INTERCEPT, intercept); 1571 1572 // Should this notification make noise, vibe, or use the LED? 1573 final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD) && !intercept; 1574 if (DBG) Slog.v(TAG, "canInterrupt=" + canInterrupt + " intercept=" + intercept); 1575 synchronized (mNotificationList) { 1576 final StatusBarNotification n = new StatusBarNotification( 1577 pkg, opPkg, id, tag, callingUid, callingPid, score, notification, 1578 user); 1579 NotificationRecord r = new NotificationRecord(n); 1580 NotificationRecord old = null; 1581 1582 int index = indexOfNotificationLocked(pkg, tag, id, userId); 1583 if (index < 0) { 1584 mNotificationList.add(r); 1585 mUsageStats.registerPostedByApp(r); 1586 } else { 1587 old = mNotificationList.get(index); 1588 mNotificationList.set(index, r); 1589 mUsageStats.registerUpdatedByApp(r); 1590 // Make sure we don't lose the foreground service state. 1591 if (old != null) { 1592 notification.flags |= 1593 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE; 1594 } 1595 } 1596 if (old != null) { 1597 mNotificationsByKey.remove(old.sbn.getKey()); 1598 } 1599 mNotificationsByKey.put(n.getKey(), r); 1600 1601 // Ensure if this is a foreground service that the proper additional 1602 // flags are set. 1603 if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) { 1604 notification.flags |= Notification.FLAG_ONGOING_EVENT 1605 | Notification.FLAG_NO_CLEAR; 1606 } 1607 1608 final int currentUser; 1609 final long token = Binder.clearCallingIdentity(); 1610 try { 1611 currentUser = ActivityManager.getCurrentUser(); 1612 } finally { 1613 Binder.restoreCallingIdentity(token); 1614 } 1615 1616 if (notification.icon != 0) { 1617 if (old != null && old.statusBarKey != null) { 1618 r.statusBarKey = old.statusBarKey; 1619 final long identity = Binder.clearCallingIdentity(); 1620 try { 1621 mStatusBar.updateNotification(r.statusBarKey, n); 1622 } finally { 1623 Binder.restoreCallingIdentity(identity); 1624 } 1625 } else { 1626 final long identity = Binder.clearCallingIdentity(); 1627 try { 1628 r.statusBarKey = mStatusBar.addNotification(n); 1629 if ((n.getNotification().flags & Notification.FLAG_SHOW_LIGHTS) != 0 1630 && canInterrupt) { 1631 mAttentionLight.pulse(); 1632 } 1633 } finally { 1634 Binder.restoreCallingIdentity(identity); 1635 } 1636 } 1637 // Send accessibility events only for the current user. 1638 if (currentUser == userId) { 1639 sendAccessibilityEvent(notification, pkg); 1640 } 1641 1642 mListeners.notifyPostedLocked(r.sbn); 1643 } else { 1644 Slog.e(TAG, "Not posting notification with icon==0: " + notification); 1645 if (old != null && old.statusBarKey != null) { 1646 final long identity = Binder.clearCallingIdentity(); 1647 try { 1648 mStatusBar.removeNotification(old.statusBarKey); 1649 } finally { 1650 Binder.restoreCallingIdentity(identity); 1651 } 1652 1653 mListeners.notifyRemovedLocked(r.sbn); 1654 } 1655 // ATTENTION: in a future release we will bail out here 1656 // so that we do not play sounds, show lights, etc. for invalid 1657 // notifications 1658 Slog.e(TAG, "WARNING: In a future release this will crash the app: " 1659 + n.getPackageName()); 1660 } 1661 1662 // If we're not supposed to beep, vibrate, etc. then don't. 1663 if (!mDisableNotificationAlerts 1664 && (!(old != null 1665 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 )) 1666 && (r.getUserId() == UserHandle.USER_ALL || 1667 (r.getUserId() == userId && r.getUserId() == currentUser) || 1668 mUserProfiles.isCurrentProfile(r.getUserId())) 1669 && canInterrupt 1670 && mSystemReady 1671 && mAudioManager != null) { 1672 if (DBG) Slog.v(TAG, "Interrupting!"); 1673 // sound 1674 1675 // should we use the default notification sound? (indicated either by 1676 // DEFAULT_SOUND or because notification.sound is pointing at 1677 // Settings.System.NOTIFICATION_SOUND) 1678 final boolean useDefaultSound = 1679 (notification.defaults & Notification.DEFAULT_SOUND) != 0 || 1680 Settings.System.DEFAULT_NOTIFICATION_URI 1681 .equals(notification.sound); 1682 1683 Uri soundUri = null; 1684 boolean hasValidSound = false; 1685 1686 if (useDefaultSound) { 1687 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI; 1688 1689 // check to see if the default notification sound is silent 1690 ContentResolver resolver = getContext().getContentResolver(); 1691 hasValidSound = Settings.System.getString(resolver, 1692 Settings.System.NOTIFICATION_SOUND) != null; 1693 } else if (notification.sound != null) { 1694 soundUri = notification.sound; 1695 hasValidSound = (soundUri != null); 1696 } 1697 1698 if (hasValidSound) { 1699 boolean looping = 1700 (notification.flags & Notification.FLAG_INSISTENT) != 0; 1701 int audioStreamType; 1702 if (notification.audioStreamType >= 0) { 1703 audioStreamType = notification.audioStreamType; 1704 } else { 1705 audioStreamType = DEFAULT_STREAM_TYPE; 1706 } 1707 mSoundNotification = r; 1708 // do not play notifications if stream volume is 0 (typically because 1709 // ringer mode is silent) or if there is a user of exclusive audio focus 1710 if ((mAudioManager.getStreamVolume(audioStreamType) != 0) 1711 && !mAudioManager.isAudioFocusExclusive()) { 1712 final long identity = Binder.clearCallingIdentity(); 1713 try { 1714 final IRingtonePlayer player = 1715 mAudioManager.getRingtonePlayer(); 1716 if (player != null) { 1717 if (DBG) Slog.v(TAG, "Playing sound " + soundUri 1718 + " on stream " + audioStreamType); 1719 player.playAsync(soundUri, user, looping, audioStreamType); 1720 } 1721 } catch (RemoteException e) { 1722 } finally { 1723 Binder.restoreCallingIdentity(identity); 1724 } 1725 } 1726 } 1727 1728 // vibrate 1729 // Does the notification want to specify its own vibration? 1730 final boolean hasCustomVibrate = notification.vibrate != null; 1731 1732 // new in 4.2: if there was supposed to be a sound and we're in vibrate 1733 // mode, and no other vibration is specified, we fall back to vibration 1734 final boolean convertSoundToVibration = 1735 !hasCustomVibrate 1736 && hasValidSound 1737 && (mAudioManager.getRingerMode() 1738 == AudioManager.RINGER_MODE_VIBRATE); 1739 1740 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback. 1741 final boolean useDefaultVibrate = 1742 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; 1743 1744 if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate) 1745 && !(mAudioManager.getRingerMode() 1746 == AudioManager.RINGER_MODE_SILENT)) { 1747 mVibrateNotification = r; 1748 1749 if (useDefaultVibrate || convertSoundToVibration) { 1750 // Escalate privileges so we can use the vibrator even if the 1751 // notifying app does not have the VIBRATE permission. 1752 long identity = Binder.clearCallingIdentity(); 1753 try { 1754 mVibrator.vibrate(r.sbn.getUid(), r.sbn.getOpPkg(), 1755 useDefaultVibrate ? mDefaultVibrationPattern 1756 : mFallbackVibrationPattern, 1757 ((notification.flags & Notification.FLAG_INSISTENT) != 0) 1758 ? 0: -1, notification.audioStreamType); 1759 } finally { 1760 Binder.restoreCallingIdentity(identity); 1761 } 1762 } else if (notification.vibrate.length > 1) { 1763 // If you want your own vibration pattern, you need the VIBRATE 1764 // permission 1765 mVibrator.vibrate(r.sbn.getUid(), r.sbn.getOpPkg(), 1766 notification.vibrate, 1767 ((notification.flags & Notification.FLAG_INSISTENT) != 0) 1768 ? 0: -1, notification.audioStreamType); 1769 } 1770 } 1771 } 1772 1773 // light 1774 // the most recent thing gets the light 1775 mLights.remove(old); 1776 if (mLedNotification == old) { 1777 mLedNotification = null; 1778 } 1779 //Slog.i(TAG, "notification.lights=" 1780 // + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) 1781 // != 0)); 1782 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 1783 && canInterrupt) { 1784 mLights.add(r); 1785 updateLightsLocked(); 1786 } else { 1787 if (old != null 1788 && ((old.getFlags() & Notification.FLAG_SHOW_LIGHTS) != 0)) { 1789 updateLightsLocked(); 1790 } 1791 } 1792 } 1793 } 1794 }); 1795 1796 idOut[0] = id; 1797 } 1798 1799 void showNextToastLocked() { 1800 ToastRecord record = mToastQueue.get(0); 1801 while (record != null) { 1802 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback); 1803 try { 1804 record.callback.show(); 1805 scheduleTimeoutLocked(record); 1806 return; 1807 } catch (RemoteException e) { 1808 Slog.w(TAG, "Object died trying to show notification " + record.callback 1809 + " in package " + record.pkg); 1810 // remove it from the list and let the process die 1811 int index = mToastQueue.indexOf(record); 1812 if (index >= 0) { 1813 mToastQueue.remove(index); 1814 } 1815 keepProcessAliveLocked(record.pid); 1816 if (mToastQueue.size() > 0) { 1817 record = mToastQueue.get(0); 1818 } else { 1819 record = null; 1820 } 1821 } 1822 } 1823 } 1824 1825 void cancelToastLocked(int index) { 1826 ToastRecord record = mToastQueue.get(index); 1827 try { 1828 record.callback.hide(); 1829 } catch (RemoteException e) { 1830 Slog.w(TAG, "Object died trying to hide notification " + record.callback 1831 + " in package " + record.pkg); 1832 // don't worry about this, we're about to remove it from 1833 // the list anyway 1834 } 1835 mToastQueue.remove(index); 1836 keepProcessAliveLocked(record.pid); 1837 if (mToastQueue.size() > 0) { 1838 // Show the next one. If the callback fails, this will remove 1839 // it from the list, so don't assume that the list hasn't changed 1840 // after this point. 1841 showNextToastLocked(); 1842 } 1843 } 1844 1845 private void scheduleTimeoutLocked(ToastRecord r) 1846 { 1847 mHandler.removeCallbacksAndMessages(r); 1848 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r); 1849 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY; 1850 mHandler.sendMessageDelayed(m, delay); 1851 } 1852 1853 private void handleTimeout(ToastRecord record) 1854 { 1855 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback); 1856 synchronized (mToastQueue) { 1857 int index = indexOfToastLocked(record.pkg, record.callback); 1858 if (index >= 0) { 1859 cancelToastLocked(index); 1860 } 1861 } 1862 } 1863 1864 // lock on mToastQueue 1865 int indexOfToastLocked(String pkg, ITransientNotification callback) 1866 { 1867 IBinder cbak = callback.asBinder(); 1868 ArrayList<ToastRecord> list = mToastQueue; 1869 int len = list.size(); 1870 for (int i=0; i<len; i++) { 1871 ToastRecord r = list.get(i); 1872 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) { 1873 return i; 1874 } 1875 } 1876 return -1; 1877 } 1878 1879 // lock on mToastQueue 1880 void keepProcessAliveLocked(int pid) 1881 { 1882 int toastCount = 0; // toasts from this pid 1883 ArrayList<ToastRecord> list = mToastQueue; 1884 int N = list.size(); 1885 for (int i=0; i<N; i++) { 1886 ToastRecord r = list.get(i); 1887 if (r.pid == pid) { 1888 toastCount++; 1889 } 1890 } 1891 try { 1892 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0); 1893 } catch (RemoteException e) { 1894 // Shouldn't happen. 1895 } 1896 } 1897 1898 private final class WorkerHandler extends Handler 1899 { 1900 @Override 1901 public void handleMessage(Message msg) 1902 { 1903 switch (msg.what) 1904 { 1905 case MESSAGE_TIMEOUT: 1906 handleTimeout((ToastRecord)msg.obj); 1907 break; 1908 case MESSAGE_SAVE_POLICY_FILE: 1909 handleSavePolicyFile(); 1910 break; 1911 } 1912 } 1913 } 1914 1915 1916 // Notifications 1917 // ============================================================================ 1918 static int clamp(int x, int low, int high) { 1919 return (x < low) ? low : ((x > high) ? high : x); 1920 } 1921 1922 void sendAccessibilityEvent(Notification notification, CharSequence packageName) { 1923 AccessibilityManager manager = AccessibilityManager.getInstance(getContext()); 1924 if (!manager.isEnabled()) { 1925 return; 1926 } 1927 1928 AccessibilityEvent event = 1929 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); 1930 event.setPackageName(packageName); 1931 event.setClassName(Notification.class.getName()); 1932 event.setParcelableData(notification); 1933 CharSequence tickerText = notification.tickerText; 1934 if (!TextUtils.isEmpty(tickerText)) { 1935 event.getText().add(tickerText); 1936 } 1937 1938 manager.sendAccessibilityEvent(event); 1939 } 1940 1941 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) { 1942 // tell the app 1943 if (sendDelete) { 1944 if (r.getNotification().deleteIntent != null) { 1945 try { 1946 r.getNotification().deleteIntent.send(); 1947 } catch (PendingIntent.CanceledException ex) { 1948 // do nothing - there's no relevant way to recover, and 1949 // no reason to let this propagate 1950 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex); 1951 } 1952 } 1953 } 1954 1955 // status bar 1956 if (r.getNotification().icon != 0) { 1957 final long identity = Binder.clearCallingIdentity(); 1958 try { 1959 mStatusBar.removeNotification(r.statusBarKey); 1960 } finally { 1961 Binder.restoreCallingIdentity(identity); 1962 } 1963 r.statusBarKey = null; 1964 mListeners.notifyRemovedLocked(r.sbn); 1965 } 1966 1967 // sound 1968 if (mSoundNotification == r) { 1969 mSoundNotification = null; 1970 final long identity = Binder.clearCallingIdentity(); 1971 try { 1972 final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); 1973 if (player != null) { 1974 player.stopAsync(); 1975 } 1976 } catch (RemoteException e) { 1977 } finally { 1978 Binder.restoreCallingIdentity(identity); 1979 } 1980 } 1981 1982 // vibrate 1983 if (mVibrateNotification == r) { 1984 mVibrateNotification = null; 1985 long identity = Binder.clearCallingIdentity(); 1986 try { 1987 mVibrator.cancel(); 1988 } 1989 finally { 1990 Binder.restoreCallingIdentity(identity); 1991 } 1992 } 1993 1994 // light 1995 mLights.remove(r); 1996 if (mLedNotification == r) { 1997 mLedNotification = null; 1998 } 1999 2000 // Record usage stats 2001 switch (reason) { 2002 case REASON_DELEGATE_CANCEL: 2003 case REASON_DELEGATE_CANCEL_ALL: 2004 case REASON_LISTENER_CANCEL: 2005 case REASON_LISTENER_CANCEL_ALL: 2006 mUsageStats.registerDismissedByUser(r); 2007 break; 2008 case REASON_NOMAN_CANCEL: 2009 case REASON_NOMAN_CANCEL_ALL: 2010 mUsageStats.registerRemovedByApp(r); 2011 break; 2012 case REASON_DELEGATE_CLICK: 2013 mUsageStats.registerCancelDueToClick(r); 2014 break; 2015 default: 2016 mUsageStats.registerCancelUnknown(r); 2017 break; 2018 } 2019 2020 // Save it for users of getHistoricalNotifications() 2021 mArchive.record(r.sbn); 2022 } 2023 2024 /** 2025 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags} 2026 * and none of the {@code mustNotHaveFlags}. 2027 */ 2028 void cancelNotification(final int callingUid, final int callingPid, 2029 final String pkg, final String tag, final int id, 2030 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete, 2031 final int userId, final int reason, final NotificationListenerInfo listener) { 2032 // In enqueueNotificationInternal notifications are added by scheduling the 2033 // work on the worker handler. Hence, we also schedule the cancel on this 2034 // handler to avoid a scenario where an add notification call followed by a 2035 // remove notification call ends up in not removing the notification. 2036 mHandler.post(new Runnable() { 2037 @Override 2038 public void run() { 2039 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId, 2040 mustHaveFlags, mustNotHaveFlags, reason, 2041 listener == null ? null : listener.component.toShortString()); 2042 2043 synchronized (mNotificationList) { 2044 int index = indexOfNotificationLocked(pkg, tag, id, userId); 2045 if (index >= 0) { 2046 NotificationRecord r = mNotificationList.get(index); 2047 2048 // Ideally we'd do this in the caller of this method. However, that would 2049 // require the caller to also find the notification. 2050 if (reason == REASON_DELEGATE_CLICK) { 2051 mUsageStats.registerClickedByUser(r); 2052 } 2053 2054 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) { 2055 return; 2056 } 2057 if ((r.getNotification().flags & mustNotHaveFlags) != 0) { 2058 return; 2059 } 2060 2061 mNotificationList.remove(index); 2062 mNotificationsByKey.remove(r.sbn.getKey()); 2063 2064 cancelNotificationLocked(r, sendDelete, reason); 2065 updateLightsLocked(); 2066 } 2067 } 2068 } 2069 }); 2070 } 2071 2072 /** 2073 * Determine whether the userId applies to the notification in question, either because 2074 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard). 2075 */ 2076 private boolean notificationMatchesUserId(NotificationRecord r, int userId) { 2077 return 2078 // looking for USER_ALL notifications? match everything 2079 userId == UserHandle.USER_ALL 2080 // a notification sent to USER_ALL matches any query 2081 || r.getUserId() == UserHandle.USER_ALL 2082 // an exact user match 2083 || r.getUserId() == userId; 2084 } 2085 2086 /** 2087 * Determine whether the userId applies to the notification in question, either because 2088 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or 2089 * because it matches one of the users profiles. 2090 */ 2091 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) { 2092 return notificationMatchesUserId(r, userId) 2093 || mUserProfiles.isCurrentProfile(r.getUserId()); 2094 } 2095 2096 /** 2097 * Cancels all notifications from a given package that have all of the 2098 * {@code mustHaveFlags}. 2099 */ 2100 boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags, 2101 int mustNotHaveFlags, boolean doit, int userId, int reason, 2102 NotificationListenerInfo listener) { 2103 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2104 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason, 2105 listener == null ? null : listener.component.toShortString()); 2106 2107 synchronized (mNotificationList) { 2108 final int N = mNotificationList.size(); 2109 boolean canceledSomething = false; 2110 for (int i = N-1; i >= 0; --i) { 2111 NotificationRecord r = mNotificationList.get(i); 2112 if (!notificationMatchesUserId(r, userId)) { 2113 continue; 2114 } 2115 // Don't remove notifications to all, if there's no package name specified 2116 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) { 2117 continue; 2118 } 2119 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) { 2120 continue; 2121 } 2122 if ((r.getFlags() & mustNotHaveFlags) != 0) { 2123 continue; 2124 } 2125 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) { 2126 continue; 2127 } 2128 canceledSomething = true; 2129 if (!doit) { 2130 return true; 2131 } 2132 mNotificationList.remove(i); 2133 mNotificationsByKey.remove(r.sbn.getKey()); 2134 cancelNotificationLocked(r, false, reason); 2135 } 2136 if (canceledSomething) { 2137 updateLightsLocked(); 2138 } 2139 return canceledSomething; 2140 } 2141 } 2142 2143 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason, 2144 NotificationListenerInfo listener, boolean includeCurrentProfiles) { 2145 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2146 null, userId, 0, 0, reason, 2147 listener == null ? null : listener.component.toShortString()); 2148 2149 final int N = mNotificationList.size(); 2150 for (int i=N-1; i>=0; i--) { 2151 NotificationRecord r = mNotificationList.get(i); 2152 if (includeCurrentProfiles) { 2153 if (!notificationMatchesCurrentProfiles(r, userId)) { 2154 continue; 2155 } 2156 } else { 2157 if (!notificationMatchesUserId(r, userId)) { 2158 continue; 2159 } 2160 } 2161 2162 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT 2163 | Notification.FLAG_NO_CLEAR)) == 0) { 2164 mNotificationList.remove(i); 2165 mNotificationsByKey.remove(r.sbn.getKey()); 2166 cancelNotificationLocked(r, true, reason); 2167 } 2168 } 2169 updateLightsLocked(); 2170 } 2171 2172 // lock on mNotificationList 2173 void updateLightsLocked() 2174 { 2175 // handle notification lights 2176 if (mLedNotification == null) { 2177 // get next notification, if any 2178 int n = mLights.size(); 2179 if (n > 0) { 2180 mLedNotification = mLights.get(n-1); 2181 } 2182 } 2183 2184 // Don't flash while we are in a call or screen is on 2185 if (mLedNotification == null || mInCall || mScreenOn) { 2186 mNotificationLight.turnOff(); 2187 } else { 2188 final Notification ledno = mLedNotification.sbn.getNotification(); 2189 int ledARGB = ledno.ledARGB; 2190 int ledOnMS = ledno.ledOnMS; 2191 int ledOffMS = ledno.ledOffMS; 2192 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) { 2193 ledARGB = mDefaultNotificationColor; 2194 ledOnMS = mDefaultNotificationLedOn; 2195 ledOffMS = mDefaultNotificationLedOff; 2196 } 2197 if (mNotificationPulseEnabled) { 2198 // pulse repeatedly 2199 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED, 2200 ledOnMS, ledOffMS); 2201 } 2202 } 2203 } 2204 2205 // lock on mNotificationList 2206 int indexOfNotificationLocked(String pkg, String tag, int id, int userId) 2207 { 2208 ArrayList<NotificationRecord> list = mNotificationList; 2209 final int len = list.size(); 2210 for (int i=0; i<len; i++) { 2211 NotificationRecord r = list.get(i); 2212 if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) { 2213 continue; 2214 } 2215 if (tag == null) { 2216 if (r.sbn.getTag() != null) { 2217 continue; 2218 } 2219 } else { 2220 if (!tag.equals(r.sbn.getTag())) { 2221 continue; 2222 } 2223 } 2224 if (r.sbn.getPackageName().equals(pkg)) { 2225 return i; 2226 } 2227 } 2228 return -1; 2229 } 2230 2231 private void updateNotificationPulse() { 2232 synchronized (mNotificationList) { 2233 updateLightsLocked(); 2234 } 2235 } 2236 2237 public static class UserProfiles { 2238 // Profiles of the current user. 2239 private final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>(); 2240 2241 private void updateCache(Context context) { 2242 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE); 2243 if (userManager != null) { 2244 int currentUserId = ActivityManager.getCurrentUser(); 2245 List<UserInfo> profiles = userManager.getProfiles(currentUserId); 2246 synchronized (mCurrentProfiles) { 2247 mCurrentProfiles.clear(); 2248 for (UserInfo user : profiles) { 2249 mCurrentProfiles.put(user.id, user); 2250 } 2251 } 2252 } 2253 } 2254 2255 public int[] getCurrentProfileIds() { 2256 synchronized (mCurrentProfiles) { 2257 int[] users = new int[mCurrentProfiles.size()]; 2258 final int N = mCurrentProfiles.size(); 2259 for (int i = 0; i < N; ++i) { 2260 users[i] = mCurrentProfiles.keyAt(i); 2261 } 2262 return users; 2263 } 2264 } 2265 2266 public boolean isCurrentProfile(int userId) { 2267 synchronized (mCurrentProfiles) { 2268 return mCurrentProfiles.get(userId) != null; 2269 } 2270 } 2271 } 2272} 2273