NotificationManagerService.java revision 2e448f75c0392969792032461b2f90017aebba7c
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; 18 19import com.android.internal.statusbar.StatusBarNotification; 20 21import android.app.ActivityManagerNative; 22import android.app.IActivityManager; 23import android.app.INotificationManager; 24import android.app.ITransientNotification; 25import android.app.Notification; 26import android.app.NotificationManager; 27import android.app.PendingIntent; 28import android.app.StatusBarManager; 29import android.content.BroadcastReceiver; 30import android.content.ContentResolver; 31import android.content.Context; 32import android.content.Intent; 33import android.content.IntentFilter; 34import android.content.pm.ApplicationInfo; 35import android.content.pm.PackageManager; 36import android.content.pm.PackageManager.NameNotFoundException; 37import android.content.res.Resources; 38import android.database.ContentObserver; 39import android.media.AudioManager; 40import android.net.Uri; 41import android.os.Binder; 42import android.os.Bundle; 43import android.os.Handler; 44import android.os.IBinder; 45import android.os.Message; 46import android.os.Process; 47import android.os.RemoteException; 48import android.os.Vibrator; 49import android.provider.Settings; 50import android.telephony.TelephonyManager; 51import android.text.TextUtils; 52import android.util.EventLog; 53import android.util.Log; 54import android.util.Slog; 55import android.view.accessibility.AccessibilityEvent; 56import android.view.accessibility.AccessibilityManager; 57import android.widget.Toast; 58 59import java.io.FileDescriptor; 60import java.io.PrintWriter; 61import java.util.ArrayList; 62import java.util.Arrays; 63 64/** {@hide} */ 65public class NotificationManagerService extends INotificationManager.Stub 66{ 67 private static final String TAG = "NotificationService"; 68 private static final boolean DBG = false; 69 70 private static final int MAX_PACKAGE_NOTIFICATIONS = 50; 71 72 // message codes 73 private static final int MESSAGE_TIMEOUT = 2; 74 75 private static final int LONG_DELAY = 3500; // 3.5 seconds 76 private static final int SHORT_DELAY = 2000; // 2 seconds 77 78 private static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250}; 79 80 private static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION; 81 82 final Context mContext; 83 final IActivityManager mAm; 84 final IBinder mForegroundToken = new Binder(); 85 86 private WorkerHandler mHandler; 87 private StatusBarManagerService mStatusBar; 88 private LightsService.Light mNotificationLight; 89 private LightsService.Light mAttentionLight; 90 91 private int mDefaultNotificationColor; 92 private int mDefaultNotificationLedOn; 93 private int mDefaultNotificationLedOff; 94 95 private NotificationRecord mSoundNotification; 96 private NotificationPlayer mSound; 97 private boolean mSystemReady; 98 private int mDisabledNotifications; 99 100 private NotificationRecord mVibrateNotification; 101 private Vibrator mVibrator = new Vibrator(); 102 103 // for enabling and disabling notification pulse behavior 104 private boolean mScreenOn = true; 105 private boolean mInCall = false; 106 private boolean mNotificationPulseEnabled; 107 // This is true if we have received a new notification while the screen is off 108 // (that is, if mLedNotification was set while the screen was off) 109 // This is reset to false when the screen is turned on. 110 private boolean mPendingPulseNotification; 111 112 private final ArrayList<NotificationRecord> mNotificationList = 113 new ArrayList<NotificationRecord>(); 114 115 private ArrayList<ToastRecord> mToastQueue; 116 117 private ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>(); 118 private NotificationRecord mLedNotification; 119 120 private static String idDebugString(Context baseContext, String packageName, int id) { 121 Context c = null; 122 123 if (packageName != null) { 124 try { 125 c = baseContext.createPackageContext(packageName, 0); 126 } catch (NameNotFoundException e) { 127 c = baseContext; 128 } 129 } else { 130 c = baseContext; 131 } 132 133 String pkg; 134 String type; 135 String name; 136 137 Resources r = c.getResources(); 138 try { 139 return r.getResourceName(id); 140 } catch (Resources.NotFoundException e) { 141 return "<name unknown>"; 142 } 143 } 144 145 private static final class NotificationRecord 146 { 147 final String pkg; 148 final String tag; 149 final int id; 150 final int uid; 151 final int initialPid; 152 final int priority; 153 final Notification notification; 154 IBinder statusBarKey; 155 156 NotificationRecord(String pkg, String tag, int id, int uid, int initialPid, int priority, 157 Notification notification) 158 { 159 this.pkg = pkg; 160 this.tag = tag; 161 this.id = id; 162 this.uid = uid; 163 this.initialPid = initialPid; 164 this.priority = priority; 165 this.notification = notification; 166 } 167 168 void dump(PrintWriter pw, String prefix, Context baseContext) { 169 pw.println(prefix + this); 170 pw.println(prefix + " icon=0x" + Integer.toHexString(notification.icon) 171 + " / " + idDebugString(baseContext, this.pkg, notification.icon)); 172 pw.println(prefix + " contentIntent=" + notification.contentIntent); 173 pw.println(prefix + " deleteIntent=" + notification.deleteIntent); 174 pw.println(prefix + " tickerText=" + notification.tickerText); 175 pw.println(prefix + " contentView=" + notification.contentView); 176 pw.println(prefix + " defaults=0x" + Integer.toHexString(notification.defaults)); 177 pw.println(prefix + " flags=0x" + Integer.toHexString(notification.flags)); 178 pw.println(prefix + " sound=" + notification.sound); 179 pw.println(prefix + " vibrate=" + Arrays.toString(notification.vibrate)); 180 pw.println(prefix + " ledARGB=0x" + Integer.toHexString(notification.ledARGB) 181 + " ledOnMS=" + notification.ledOnMS 182 + " ledOffMS=" + notification.ledOffMS); 183 } 184 185 @Override 186 public final String toString() 187 { 188 return "NotificationRecord{" 189 + Integer.toHexString(System.identityHashCode(this)) 190 + " pkg=" + pkg 191 + " id=" + Integer.toHexString(id) 192 + " tag=" + tag 193 + " pri=" + priority 194 + "}"; 195 } 196 } 197 198 private static final class ToastRecord 199 { 200 final int pid; 201 final String pkg; 202 final ITransientNotification callback; 203 int duration; 204 205 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration) 206 { 207 this.pid = pid; 208 this.pkg = pkg; 209 this.callback = callback; 210 this.duration = duration; 211 } 212 213 void update(int duration) { 214 this.duration = duration; 215 } 216 217 void dump(PrintWriter pw, String prefix) { 218 pw.println(prefix + this); 219 } 220 221 @Override 222 public final String toString() 223 { 224 return "ToastRecord{" 225 + Integer.toHexString(System.identityHashCode(this)) 226 + " pkg=" + pkg 227 + " callback=" + callback 228 + " duration=" + duration; 229 } 230 } 231 232 private StatusBarManagerService.NotificationCallbacks mNotificationCallbacks 233 = new StatusBarManagerService.NotificationCallbacks() { 234 235 public void onSetDisabled(int status) { 236 synchronized (mNotificationList) { 237 mDisabledNotifications = status; 238 if ((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) { 239 // cancel whatever's going on 240 long identity = Binder.clearCallingIdentity(); 241 try { 242 mSound.stop(); 243 } 244 finally { 245 Binder.restoreCallingIdentity(identity); 246 } 247 248 identity = Binder.clearCallingIdentity(); 249 try { 250 mVibrator.cancel(); 251 } 252 finally { 253 Binder.restoreCallingIdentity(identity); 254 } 255 } 256 } 257 } 258 259 public void onClearAll() { 260 cancelAll(); 261 } 262 263 public void onNotificationClick(String pkg, String tag, int id) { 264 cancelNotification(pkg, tag, id, Notification.FLAG_AUTO_CANCEL, 265 Notification.FLAG_FOREGROUND_SERVICE, false); 266 } 267 268 public void onNotificationClear(String pkg, String tag, int id) { 269 cancelNotification(pkg, tag, id, 0, 270 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE, 271 true); 272 } 273 274 public void onPanelRevealed() { 275 synchronized (mNotificationList) { 276 // sound 277 mSoundNotification = null; 278 long identity = Binder.clearCallingIdentity(); 279 try { 280 mSound.stop(); 281 } 282 finally { 283 Binder.restoreCallingIdentity(identity); 284 } 285 286 // vibrate 287 mVibrateNotification = null; 288 identity = Binder.clearCallingIdentity(); 289 try { 290 mVibrator.cancel(); 291 } 292 finally { 293 Binder.restoreCallingIdentity(identity); 294 } 295 296 // light 297 mLights.clear(); 298 mLedNotification = null; 299 updateLightsLocked(); 300 } 301 } 302 303 public void onNotificationError(String pkg, String tag, int id, 304 int uid, int initialPid, String message) { 305 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id 306 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")"); 307 cancelNotification(pkg, tag, id, 0, 0, false); 308 long ident = Binder.clearCallingIdentity(); 309 try { 310 ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg, 311 "Bad notification posted from package " + pkg 312 + ": " + message); 313 } catch (RemoteException e) { 314 } 315 Binder.restoreCallingIdentity(ident); 316 } 317 }; 318 319 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 320 @Override 321 public void onReceive(Context context, Intent intent) { 322 String action = intent.getAction(); 323 324 boolean queryRestart = false; 325 326 if (action.equals(Intent.ACTION_PACKAGE_REMOVED) 327 || action.equals(Intent.ACTION_PACKAGE_RESTARTED) 328 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART)) 329 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { 330 String pkgList[] = null; 331 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { 332 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); 333 } else if (queryRestart) { 334 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES); 335 } else { 336 Uri uri = intent.getData(); 337 if (uri == null) { 338 return; 339 } 340 String pkgName = uri.getSchemeSpecificPart(); 341 if (pkgName == null) { 342 return; 343 } 344 pkgList = new String[]{pkgName}; 345 } 346 if (pkgList != null && (pkgList.length > 0)) { 347 for (String pkgName : pkgList) { 348 cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart); 349 } 350 } 351 } else if (action.equals(Intent.ACTION_SCREEN_ON)) { 352 mScreenOn = true; 353 updateNotificationPulse(); 354 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { 355 mScreenOn = false; 356 updateNotificationPulse(); 357 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) { 358 mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_OFFHOOK)); 359 updateNotificationPulse(); 360 } 361 } 362 }; 363 364 class SettingsObserver extends ContentObserver { 365 SettingsObserver(Handler handler) { 366 super(handler); 367 } 368 369 void observe() { 370 ContentResolver resolver = mContext.getContentResolver(); 371 resolver.registerContentObserver(Settings.System.getUriFor( 372 Settings.System.NOTIFICATION_LIGHT_PULSE), false, this); 373 update(); 374 } 375 376 @Override public void onChange(boolean selfChange) { 377 update(); 378 } 379 380 public void update() { 381 ContentResolver resolver = mContext.getContentResolver(); 382 boolean pulseEnabled = Settings.System.getInt(resolver, 383 Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0; 384 if (mNotificationPulseEnabled != pulseEnabled) { 385 mNotificationPulseEnabled = pulseEnabled; 386 updateNotificationPulse(); 387 } 388 } 389 } 390 391 NotificationManagerService(Context context, StatusBarManagerService statusBar, 392 LightsService lights) 393 { 394 super(); 395 mContext = context; 396 mAm = ActivityManagerNative.getDefault(); 397 mSound = new NotificationPlayer(TAG); 398 mSound.setUsesWakeLock(context); 399 mToastQueue = new ArrayList<ToastRecord>(); 400 mHandler = new WorkerHandler(); 401 402 mStatusBar = statusBar; 403 statusBar.setNotificationCallbacks(mNotificationCallbacks); 404 405 mNotificationLight = lights.getLight(LightsService.LIGHT_ID_NOTIFICATIONS); 406 mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION); 407 408 Resources resources = mContext.getResources(); 409 mDefaultNotificationColor = resources.getColor( 410 com.android.internal.R.color.config_defaultNotificationColor); 411 mDefaultNotificationLedOn = resources.getInteger( 412 com.android.internal.R.integer.config_defaultNotificationLedOn); 413 mDefaultNotificationLedOff = resources.getInteger( 414 com.android.internal.R.integer.config_defaultNotificationLedOff); 415 416 // Don't start allowing notifications until the setup wizard has run once. 417 // After that, including subsequent boots, init with notifications turned on. 418 // This works on the first boot because the setup wizard will toggle this 419 // flag at least once and we'll go back to 0 after that. 420 if (0 == Settings.Secure.getInt(mContext.getContentResolver(), 421 Settings.Secure.DEVICE_PROVISIONED, 0)) { 422 mDisabledNotifications = StatusBarManager.DISABLE_NOTIFICATION_ALERTS; 423 } 424 425 // register for various Intents 426 IntentFilter filter = new IntentFilter(); 427 filter.addAction(Intent.ACTION_SCREEN_ON); 428 filter.addAction(Intent.ACTION_SCREEN_OFF); 429 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); 430 mContext.registerReceiver(mIntentReceiver, filter); 431 IntentFilter pkgFilter = new IntentFilter(); 432 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 433 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); 434 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); 435 pkgFilter.addDataScheme("package"); 436 mContext.registerReceiver(mIntentReceiver, pkgFilter); 437 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); 438 mContext.registerReceiver(mIntentReceiver, sdFilter); 439 440 SettingsObserver observer = new SettingsObserver(mHandler); 441 observer.observe(); 442 } 443 444 void systemReady() { 445 // no beeping until we're basically done booting 446 mSystemReady = true; 447 } 448 449 // Toasts 450 // ============================================================================ 451 public void enqueueToast(String pkg, ITransientNotification callback, int duration) 452 { 453 if (DBG) Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback + " duration=" + duration); 454 455 if (pkg == null || callback == null) { 456 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback); 457 return ; 458 } 459 460 synchronized (mToastQueue) { 461 int callingPid = Binder.getCallingPid(); 462 long callingId = Binder.clearCallingIdentity(); 463 try { 464 ToastRecord record; 465 int index = indexOfToastLocked(pkg, callback); 466 // If it's already in the queue, we update it in place, we don't 467 // move it to the end of the queue. 468 if (index >= 0) { 469 record = mToastQueue.get(index); 470 record.update(duration); 471 } else { 472 record = new ToastRecord(callingPid, pkg, callback, duration); 473 mToastQueue.add(record); 474 index = mToastQueue.size() - 1; 475 keepProcessAliveLocked(callingPid); 476 } 477 // If it's at index 0, it's the current toast. It doesn't matter if it's 478 // new or just been updated. Call back and tell it to show itself. 479 // If the callback fails, this will remove it from the list, so don't 480 // assume that it's valid after this. 481 if (index == 0) { 482 showNextToastLocked(); 483 } 484 } finally { 485 Binder.restoreCallingIdentity(callingId); 486 } 487 } 488 } 489 490 public void cancelToast(String pkg, ITransientNotification callback) { 491 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback); 492 493 if (pkg == null || callback == null) { 494 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback); 495 return ; 496 } 497 498 synchronized (mToastQueue) { 499 long callingId = Binder.clearCallingIdentity(); 500 try { 501 int index = indexOfToastLocked(pkg, callback); 502 if (index >= 0) { 503 cancelToastLocked(index); 504 } else { 505 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg + " callback=" + callback); 506 } 507 } finally { 508 Binder.restoreCallingIdentity(callingId); 509 } 510 } 511 } 512 513 private void showNextToastLocked() { 514 ToastRecord record = mToastQueue.get(0); 515 while (record != null) { 516 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback); 517 try { 518 record.callback.show(); 519 scheduleTimeoutLocked(record, false); 520 return; 521 } catch (RemoteException e) { 522 Slog.w(TAG, "Object died trying to show notification " + record.callback 523 + " in package " + record.pkg); 524 // remove it from the list and let the process die 525 int index = mToastQueue.indexOf(record); 526 if (index >= 0) { 527 mToastQueue.remove(index); 528 } 529 keepProcessAliveLocked(record.pid); 530 if (mToastQueue.size() > 0) { 531 record = mToastQueue.get(0); 532 } else { 533 record = null; 534 } 535 } 536 } 537 } 538 539 private void cancelToastLocked(int index) { 540 ToastRecord record = mToastQueue.get(index); 541 try { 542 record.callback.hide(); 543 } catch (RemoteException e) { 544 Slog.w(TAG, "Object died trying to hide notification " + record.callback 545 + " in package " + record.pkg); 546 // don't worry about this, we're about to remove it from 547 // the list anyway 548 } 549 mToastQueue.remove(index); 550 keepProcessAliveLocked(record.pid); 551 if (mToastQueue.size() > 0) { 552 // Show the next one. If the callback fails, this will remove 553 // it from the list, so don't assume that the list hasn't changed 554 // after this point. 555 showNextToastLocked(); 556 } 557 } 558 559 private void scheduleTimeoutLocked(ToastRecord r, boolean immediate) 560 { 561 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r); 562 long delay = immediate ? 0 : (r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY); 563 mHandler.removeCallbacksAndMessages(r); 564 mHandler.sendMessageDelayed(m, delay); 565 } 566 567 private void handleTimeout(ToastRecord record) 568 { 569 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback); 570 synchronized (mToastQueue) { 571 int index = indexOfToastLocked(record.pkg, record.callback); 572 if (index >= 0) { 573 cancelToastLocked(index); 574 } 575 } 576 } 577 578 // lock on mToastQueue 579 private int indexOfToastLocked(String pkg, ITransientNotification callback) 580 { 581 IBinder cbak = callback.asBinder(); 582 ArrayList<ToastRecord> list = mToastQueue; 583 int len = list.size(); 584 for (int i=0; i<len; i++) { 585 ToastRecord r = list.get(i); 586 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) { 587 return i; 588 } 589 } 590 return -1; 591 } 592 593 // lock on mToastQueue 594 private void keepProcessAliveLocked(int pid) 595 { 596 int toastCount = 0; // toasts from this pid 597 ArrayList<ToastRecord> list = mToastQueue; 598 int N = list.size(); 599 for (int i=0; i<N; i++) { 600 ToastRecord r = list.get(i); 601 if (r.pid == pid) { 602 toastCount++; 603 } 604 } 605 try { 606 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0); 607 } catch (RemoteException e) { 608 // Shouldn't happen. 609 } 610 } 611 612 private final class WorkerHandler extends Handler 613 { 614 @Override 615 public void handleMessage(Message msg) 616 { 617 switch (msg.what) 618 { 619 case MESSAGE_TIMEOUT: 620 handleTimeout((ToastRecord)msg.obj); 621 break; 622 } 623 } 624 } 625 626 627 // Notifications 628 // ============================================================================ 629 @Deprecated 630 public void enqueueNotification(String pkg, int id, Notification notification, int[] idOut) 631 { 632 enqueueNotificationWithTag(pkg, null /* tag */, id, notification, idOut); 633 } 634 635 public void enqueueNotificationWithTag(String pkg, String tag, int id, Notification notification, 636 int[] idOut) 637 { 638 enqueueNotificationInternal(pkg, Binder.getCallingUid(), Binder.getCallingPid(), 639 tag, id, notification, idOut); 640 } 641 642 public void enqueueNotificationWithTagPriority(String pkg, String tag, int id, int priority, 643 Notification notification, int[] idOut) 644 { 645 enqueueNotificationInternal(pkg, Binder.getCallingUid(), Binder.getCallingPid(), 646 tag, id, priority, notification, idOut); 647 } 648 649 // Not exposed via Binder; for system use only (otherwise malicious apps could spoof the 650 // uid/pid of another application) 651 public void enqueueNotificationInternal(String pkg, int callingUid, int callingPid, 652 String tag, int id, Notification notification, int[] idOut) 653 { 654 enqueueNotificationInternal(pkg, callingUid, callingPid, tag, id, 655 ((notification.flags & Notification.FLAG_ONGOING_EVENT) != 0) 656 ? StatusBarNotification.PRIORITY_ONGOING 657 : StatusBarNotification.PRIORITY_NORMAL, 658 notification, idOut); 659 } 660 public void enqueueNotificationInternal(String pkg, int callingUid, int callingPid, 661 String tag, int id, int priority, Notification notification, int[] idOut) 662 { 663 checkIncomingCall(pkg); 664 665 // Limit the number of notifications that any given package except the android 666 // package can enqueue. Prevents DOS attacks and deals with leaks. 667 if (!"android".equals(pkg)) { 668 synchronized (mNotificationList) { 669 int count = 0; 670 final int N = mNotificationList.size(); 671 for (int i=0; i<N; i++) { 672 final NotificationRecord r = mNotificationList.get(i); 673 if (r.pkg.equals(pkg)) { 674 count++; 675 if (count >= MAX_PACKAGE_NOTIFICATIONS) { 676 Slog.e(TAG, "Package has already posted " + count 677 + " notifications. Not showing more. package=" + pkg); 678 return; 679 } 680 } 681 } 682 } 683 } 684 685 // This conditional is a dirty hack to limit the logging done on 686 // behalf of the download manager without affecting other apps. 687 if (!pkg.equals("com.android.providers.downloads") 688 || Log.isLoggable("DownloadManager", Log.VERBOSE)) { 689 EventLog.writeEvent(EventLogTags.NOTIFICATION_ENQUEUE, pkg, id, notification.toString()); 690 } 691 692 if (pkg == null || notification == null) { 693 throw new IllegalArgumentException("null not allowed: pkg=" + pkg 694 + " id=" + id + " notification=" + notification); 695 } 696 if (notification.icon != 0) { 697 if (notification.contentView == null) { 698 throw new IllegalArgumentException("contentView required: pkg=" + pkg 699 + " id=" + id + " notification=" + notification); 700 } 701 } 702 703 synchronized (mNotificationList) { 704 NotificationRecord r = new NotificationRecord(pkg, tag, id, 705 callingUid, callingPid, 706 priority, 707 notification); 708 NotificationRecord old = null; 709 710 int index = indexOfNotificationLocked(pkg, tag, id); 711 if (index < 0) { 712 mNotificationList.add(r); 713 } else { 714 old = mNotificationList.remove(index); 715 mNotificationList.add(index, r); 716 // Make sure we don't lose the foreground service state. 717 if (old != null) { 718 notification.flags |= 719 old.notification.flags&Notification.FLAG_FOREGROUND_SERVICE; 720 } 721 } 722 723 // Ensure if this is a foreground service that the proper additional 724 // flags are set. 725 if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) { 726 notification.flags |= Notification.FLAG_ONGOING_EVENT 727 | Notification.FLAG_NO_CLEAR; 728 } 729 730 if (notification.icon != 0) { 731 StatusBarNotification n = new StatusBarNotification(pkg, id, tag, 732 r.uid, r.initialPid, notification); 733 n.priority = r.priority; 734 735 if (old != null && old.statusBarKey != null) { 736 r.statusBarKey = old.statusBarKey; 737 long identity = Binder.clearCallingIdentity(); 738 try { 739 mStatusBar.updateNotification(r.statusBarKey, n); 740 } 741 finally { 742 Binder.restoreCallingIdentity(identity); 743 } 744 } else { 745 long identity = Binder.clearCallingIdentity(); 746 try { 747 r.statusBarKey = mStatusBar.addNotification(n); 748 mAttentionLight.pulse(); 749 } 750 finally { 751 Binder.restoreCallingIdentity(identity); 752 } 753 } 754 sendAccessibilityEvent(notification, pkg); 755 } else { 756 Slog.e(TAG, "Ignoring notification with icon==0: " + notification); 757 if (old != null && old.statusBarKey != null) { 758 long identity = Binder.clearCallingIdentity(); 759 try { 760 mStatusBar.removeNotification(old.statusBarKey); 761 } 762 finally { 763 Binder.restoreCallingIdentity(identity); 764 } 765 } 766 } 767 768 // If we're not supposed to beep, vibrate, etc. then don't. 769 if (((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0) 770 && (!(old != null 771 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 )) 772 && mSystemReady) { 773 774 final AudioManager audioManager = (AudioManager) mContext 775 .getSystemService(Context.AUDIO_SERVICE); 776 // sound 777 final boolean useDefaultSound = 778 (notification.defaults & Notification.DEFAULT_SOUND) != 0; 779 if (useDefaultSound || notification.sound != null) { 780 Uri uri; 781 if (useDefaultSound) { 782 uri = Settings.System.DEFAULT_NOTIFICATION_URI; 783 } else { 784 uri = notification.sound; 785 } 786 boolean looping = (notification.flags & Notification.FLAG_INSISTENT) != 0; 787 int audioStreamType; 788 if (notification.audioStreamType >= 0) { 789 audioStreamType = notification.audioStreamType; 790 } else { 791 audioStreamType = DEFAULT_STREAM_TYPE; 792 } 793 mSoundNotification = r; 794 // do not play notifications if stream volume is 0 795 // (typically because ringer mode is silent). 796 if (audioManager.getStreamVolume(audioStreamType) != 0) { 797 long identity = Binder.clearCallingIdentity(); 798 try { 799 mSound.play(mContext, uri, looping, audioStreamType); 800 } 801 finally { 802 Binder.restoreCallingIdentity(identity); 803 } 804 } 805 } 806 807 // vibrate 808 final boolean useDefaultVibrate = 809 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; 810 if ((useDefaultVibrate || notification.vibrate != null) 811 && audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_NOTIFICATION)) { 812 mVibrateNotification = r; 813 814 mVibrator.vibrate(useDefaultVibrate ? DEFAULT_VIBRATE_PATTERN 815 : notification.vibrate, 816 ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1); 817 } 818 } 819 820 // this option doesn't shut off the lights 821 822 // light 823 // the most recent thing gets the light 824 mLights.remove(old); 825 if (mLedNotification == old) { 826 mLedNotification = null; 827 } 828 //Slog.i(TAG, "notification.lights=" 829 // + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) != 0)); 830 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) { 831 mLights.add(r); 832 updateLightsLocked(); 833 } else { 834 if (old != null 835 && ((old.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0)) { 836 updateLightsLocked(); 837 } 838 } 839 } 840 841 idOut[0] = id; 842 } 843 844 private void sendAccessibilityEvent(Notification notification, CharSequence packageName) { 845 AccessibilityManager manager = AccessibilityManager.getInstance(mContext); 846 if (!manager.isEnabled()) { 847 return; 848 } 849 850 AccessibilityEvent event = 851 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); 852 event.setPackageName(packageName); 853 event.setClassName(Notification.class.getName()); 854 event.setParcelableData(notification); 855 CharSequence tickerText = notification.tickerText; 856 if (!TextUtils.isEmpty(tickerText)) { 857 event.getText().add(tickerText); 858 } 859 860 manager.sendAccessibilityEvent(event); 861 } 862 863 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete) { 864 // tell the app 865 if (sendDelete) { 866 if (r.notification.deleteIntent != null) { 867 try { 868 r.notification.deleteIntent.send(); 869 } catch (PendingIntent.CanceledException ex) { 870 // do nothing - there's no relevant way to recover, and 871 // no reason to let this propagate 872 Slog.w(TAG, "canceled PendingIntent for " + r.pkg, ex); 873 } 874 } 875 } 876 877 // status bar 878 if (r.notification.icon != 0) { 879 long identity = Binder.clearCallingIdentity(); 880 try { 881 mStatusBar.removeNotification(r.statusBarKey); 882 } 883 finally { 884 Binder.restoreCallingIdentity(identity); 885 } 886 r.statusBarKey = null; 887 } 888 889 // sound 890 if (mSoundNotification == r) { 891 mSoundNotification = null; 892 long identity = Binder.clearCallingIdentity(); 893 try { 894 mSound.stop(); 895 } 896 finally { 897 Binder.restoreCallingIdentity(identity); 898 } 899 } 900 901 // vibrate 902 if (mVibrateNotification == r) { 903 mVibrateNotification = null; 904 long identity = Binder.clearCallingIdentity(); 905 try { 906 mVibrator.cancel(); 907 } 908 finally { 909 Binder.restoreCallingIdentity(identity); 910 } 911 } 912 913 // light 914 mLights.remove(r); 915 if (mLedNotification == r) { 916 mLedNotification = null; 917 } 918 } 919 920 /** 921 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags} 922 * and none of the {@code mustNotHaveFlags}. 923 */ 924 private void cancelNotification(String pkg, String tag, int id, int mustHaveFlags, 925 int mustNotHaveFlags, boolean sendDelete) { 926 EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, mustHaveFlags); 927 928 synchronized (mNotificationList) { 929 int index = indexOfNotificationLocked(pkg, tag, id); 930 if (index >= 0) { 931 NotificationRecord r = mNotificationList.get(index); 932 933 if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) { 934 return; 935 } 936 if ((r.notification.flags & mustNotHaveFlags) != 0) { 937 return; 938 } 939 940 mNotificationList.remove(index); 941 942 cancelNotificationLocked(r, sendDelete); 943 updateLightsLocked(); 944 } 945 } 946 } 947 948 /** 949 * Cancels all notifications from a given package that have all of the 950 * {@code mustHaveFlags}. 951 */ 952 boolean cancelAllNotificationsInt(String pkg, int mustHaveFlags, 953 int mustNotHaveFlags, boolean doit) { 954 EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL_ALL, pkg, mustHaveFlags); 955 956 synchronized (mNotificationList) { 957 final int N = mNotificationList.size(); 958 boolean canceledSomething = false; 959 for (int i = N-1; i >= 0; --i) { 960 NotificationRecord r = mNotificationList.get(i); 961 if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) { 962 continue; 963 } 964 if ((r.notification.flags & mustNotHaveFlags) != 0) { 965 continue; 966 } 967 if (!r.pkg.equals(pkg)) { 968 continue; 969 } 970 canceledSomething = true; 971 if (!doit) { 972 return true; 973 } 974 mNotificationList.remove(i); 975 cancelNotificationLocked(r, false); 976 } 977 if (canceledSomething) { 978 updateLightsLocked(); 979 } 980 return canceledSomething; 981 } 982 } 983 984 @Deprecated 985 public void cancelNotification(String pkg, int id) { 986 cancelNotificationWithTag(pkg, null /* tag */, id); 987 } 988 989 public void cancelNotificationWithTag(String pkg, String tag, int id) { 990 checkIncomingCall(pkg); 991 // Don't allow client applications to cancel foreground service notis. 992 cancelNotification(pkg, tag, id, 0, 993 Binder.getCallingUid() == Process.SYSTEM_UID 994 ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false); 995 } 996 997 public void cancelAllNotifications(String pkg) { 998 checkIncomingCall(pkg); 999 1000 // Calling from user space, don't allow the canceling of actively 1001 // running foreground services. 1002 cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true); 1003 } 1004 1005 void checkIncomingCall(String pkg) { 1006 int uid = Binder.getCallingUid(); 1007 if (uid == Process.SYSTEM_UID || uid == 0) { 1008 return; 1009 } 1010 try { 1011 ApplicationInfo ai = mContext.getPackageManager().getApplicationInfo( 1012 pkg, 0); 1013 if (ai.uid != uid) { 1014 throw new SecurityException("Calling uid " + uid + " gave package" 1015 + pkg + " which is owned by uid " + ai.uid); 1016 } 1017 } catch (PackageManager.NameNotFoundException e) { 1018 throw new SecurityException("Unknown package " + pkg); 1019 } 1020 } 1021 1022 void cancelAll() { 1023 synchronized (mNotificationList) { 1024 final int N = mNotificationList.size(); 1025 for (int i=N-1; i>=0; i--) { 1026 NotificationRecord r = mNotificationList.get(i); 1027 1028 if ((r.notification.flags & (Notification.FLAG_ONGOING_EVENT 1029 | Notification.FLAG_NO_CLEAR)) == 0) { 1030 mNotificationList.remove(i); 1031 cancelNotificationLocked(r, true); 1032 } 1033 } 1034 1035 updateLightsLocked(); 1036 } 1037 } 1038 1039 // lock on mNotificationList 1040 private void updateLightsLocked() 1041 { 1042 // clear pending pulse notification if screen is on 1043 if (mScreenOn || mLedNotification == null) { 1044 mPendingPulseNotification = false; 1045 } 1046 1047 // handle notification lights 1048 if (mLedNotification == null) { 1049 // get next notification, if any 1050 int n = mLights.size(); 1051 if (n > 0) { 1052 mLedNotification = mLights.get(n-1); 1053 } 1054 if (mLedNotification != null && !mScreenOn) { 1055 mPendingPulseNotification = true; 1056 } 1057 } 1058 1059 // we only flash if screen is off and persistent pulsing is enabled 1060 // and we are not currently in a call 1061 if (!mPendingPulseNotification || mScreenOn || mInCall) { 1062 mNotificationLight.turnOff(); 1063 } else { 1064 int ledARGB = mLedNotification.notification.ledARGB; 1065 int ledOnMS = mLedNotification.notification.ledOnMS; 1066 int ledOffMS = mLedNotification.notification.ledOffMS; 1067 if ((mLedNotification.notification.defaults & Notification.DEFAULT_LIGHTS) != 0) { 1068 ledARGB = mDefaultNotificationColor; 1069 ledOnMS = mDefaultNotificationLedOn; 1070 ledOffMS = mDefaultNotificationLedOff; 1071 } 1072 if (mNotificationPulseEnabled) { 1073 // pulse repeatedly 1074 mNotificationLight.setFlashing(ledARGB, LightsService.LIGHT_FLASH_TIMED, 1075 ledOnMS, ledOffMS); 1076 } else { 1077 // pulse only once 1078 mNotificationLight.pulse(ledARGB, ledOnMS); 1079 } 1080 } 1081 } 1082 1083 // lock on mNotificationList 1084 private int indexOfNotificationLocked(String pkg, String tag, int id) 1085 { 1086 ArrayList<NotificationRecord> list = mNotificationList; 1087 final int len = list.size(); 1088 for (int i=0; i<len; i++) { 1089 NotificationRecord r = list.get(i); 1090 if (tag == null) { 1091 if (r.tag != null) { 1092 continue; 1093 } 1094 } else { 1095 if (!tag.equals(r.tag)) { 1096 continue; 1097 } 1098 } 1099 if (r.id == id && r.pkg.equals(pkg)) { 1100 return i; 1101 } 1102 } 1103 return -1; 1104 } 1105 1106 private void updateNotificationPulse() { 1107 synchronized (mNotificationList) { 1108 updateLightsLocked(); 1109 } 1110 } 1111 1112 // ====================================================================== 1113 @Override 1114 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1115 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 1116 != PackageManager.PERMISSION_GRANTED) { 1117 pw.println("Permission Denial: can't dump NotificationManager from from pid=" 1118 + Binder.getCallingPid() 1119 + ", uid=" + Binder.getCallingUid()); 1120 return; 1121 } 1122 1123 pw.println("Current Notification Manager state:"); 1124 1125 int N; 1126 1127 synchronized (mToastQueue) { 1128 N = mToastQueue.size(); 1129 if (N > 0) { 1130 pw.println(" Toast Queue:"); 1131 for (int i=0; i<N; i++) { 1132 mToastQueue.get(i).dump(pw, " "); 1133 } 1134 pw.println(" "); 1135 } 1136 1137 } 1138 1139 synchronized (mNotificationList) { 1140 N = mNotificationList.size(); 1141 if (N > 0) { 1142 pw.println(" Notification List:"); 1143 for (int i=0; i<N; i++) { 1144 mNotificationList.get(i).dump(pw, " ", mContext); 1145 } 1146 pw.println(" "); 1147 } 1148 1149 N = mLights.size(); 1150 if (N > 0) { 1151 pw.println(" Lights List:"); 1152 for (int i=0; i<N; i++) { 1153 mLights.get(i).dump(pw, " ", mContext); 1154 } 1155 pw.println(" "); 1156 } 1157 1158 pw.println(" mSoundNotification=" + mSoundNotification); 1159 pw.println(" mSound=" + mSound); 1160 pw.println(" mVibrateNotification=" + mVibrateNotification); 1161 pw.println(" mDisabledNotifications=0x" + Integer.toHexString(mDisabledNotifications)); 1162 pw.println(" mSystemReady=" + mSystemReady); 1163 } 1164 } 1165} 1166