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