NotificationManagerService.java revision 524dc046b1225087f481878a5c3ae7f733c9f0e2
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 HardwareService mHardware; 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 HardwareService hardware) 367 { 368 super(); 369 mContext = context; 370 mHardware = hardware; 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 mHardware.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 708 final AudioManager audioManager = (AudioManager) mContext 709 .getSystemService(Context.AUDIO_SERVICE); 710 // sound 711 final boolean useDefaultSound = 712 (notification.defaults & Notification.DEFAULT_SOUND) != 0; 713 if (useDefaultSound || notification.sound != null) { 714 Uri uri; 715 if (useDefaultSound) { 716 uri = Settings.System.DEFAULT_NOTIFICATION_URI; 717 } else { 718 uri = notification.sound; 719 } 720 boolean looping = (notification.flags & Notification.FLAG_INSISTENT) != 0; 721 int audioStreamType; 722 if (notification.audioStreamType >= 0) { 723 audioStreamType = notification.audioStreamType; 724 } else { 725 audioStreamType = DEFAULT_STREAM_TYPE; 726 } 727 mSoundNotification = r; 728 // do not play notifications if stream volume is 0 729 // (typically because ringer mode is silent). 730 if (audioManager.getStreamVolume(audioStreamType) != 0) { 731 long identity = Binder.clearCallingIdentity(); 732 try { 733 mSound.play(mContext, uri, looping, audioStreamType); 734 } 735 finally { 736 Binder.restoreCallingIdentity(identity); 737 } 738 } 739 } 740 741 // vibrate 742 final boolean useDefaultVibrate = 743 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; 744 if ((useDefaultVibrate || notification.vibrate != null) 745 && audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_NOTIFICATION)) { 746 mVibrateNotification = r; 747 748 mVibrator.vibrate(useDefaultVibrate ? DEFAULT_VIBRATE_PATTERN 749 : notification.vibrate, 750 ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1); 751 } 752 } 753 754 // this option doesn't shut off the lights 755 756 // light 757 // the most recent thing gets the light 758 mLights.remove(old); 759 if (mLedNotification == old) { 760 mLedNotification = null; 761 } 762 //Log.i(TAG, "notification.lights=" 763 // + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) != 0)); 764 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) { 765 mLights.add(r); 766 updateLightsLocked(); 767 } else { 768 if (old != null 769 && ((old.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0)) { 770 updateLightsLocked(); 771 } 772 } 773 } 774 775 idOut[0] = id; 776 } 777 778 private void sendAccessibilityEvent(Notification notification, CharSequence packageName) { 779 AccessibilityManager manager = AccessibilityManager.getInstance(mContext); 780 if (!manager.isEnabled()) { 781 return; 782 } 783 784 AccessibilityEvent event = 785 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); 786 event.setPackageName(packageName); 787 event.setClassName(Notification.class.getName()); 788 event.setParcelableData(notification); 789 CharSequence tickerText = notification.tickerText; 790 if (!TextUtils.isEmpty(tickerText)) { 791 event.getText().add(tickerText); 792 } 793 794 manager.sendAccessibilityEvent(event); 795 } 796 797 private void cancelNotificationLocked(NotificationRecord r) { 798 // status bar 799 if (r.notification.icon != 0) { 800 long identity = Binder.clearCallingIdentity(); 801 try { 802 mStatusBarService.removeIcon(r.statusBarKey); 803 } 804 finally { 805 Binder.restoreCallingIdentity(identity); 806 } 807 r.statusBarKey = null; 808 } 809 810 // sound 811 if (mSoundNotification == r) { 812 mSoundNotification = null; 813 long identity = Binder.clearCallingIdentity(); 814 try { 815 mSound.stop(); 816 } 817 finally { 818 Binder.restoreCallingIdentity(identity); 819 } 820 } 821 822 // vibrate 823 if (mVibrateNotification == r) { 824 mVibrateNotification = null; 825 long identity = Binder.clearCallingIdentity(); 826 try { 827 mVibrator.cancel(); 828 } 829 finally { 830 Binder.restoreCallingIdentity(identity); 831 } 832 } 833 834 // light 835 mLights.remove(r); 836 if (mLedNotification == r) { 837 mLedNotification = null; 838 } 839 } 840 841 /** 842 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags} 843 * and none of the {@code mustNotHaveFlags}. 844 */ 845 private void cancelNotification(String pkg, String tag, int id, int mustHaveFlags, 846 int mustNotHaveFlags) { 847 EventLog.writeEvent(EVENT_LOG_CANCEL, pkg, id, mustHaveFlags); 848 849 synchronized (mNotificationList) { 850 int index = indexOfNotificationLocked(pkg, tag, id); 851 if (index >= 0) { 852 NotificationRecord r = mNotificationList.get(index); 853 854 if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) { 855 return; 856 } 857 if ((r.notification.flags & mustNotHaveFlags) != 0) { 858 return; 859 } 860 861 mNotificationList.remove(index); 862 863 cancelNotificationLocked(r); 864 updateLightsLocked(); 865 } 866 } 867 } 868 869 /** 870 * Cancels all notifications from a given package that have all of the 871 * {@code mustHaveFlags}. 872 */ 873 void cancelAllNotificationsInt(String pkg, int mustHaveFlags, 874 int mustNotHaveFlags) { 875 EventLog.writeEvent(EVENT_LOG_CANCEL_ALL, pkg, mustHaveFlags); 876 877 synchronized (mNotificationList) { 878 final int N = mNotificationList.size(); 879 boolean canceledSomething = false; 880 for (int i = N-1; i >= 0; --i) { 881 NotificationRecord r = mNotificationList.get(i); 882 if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) { 883 continue; 884 } 885 if ((r.notification.flags & mustNotHaveFlags) != 0) { 886 continue; 887 } 888 if (!r.pkg.equals(pkg)) { 889 continue; 890 } 891 mNotificationList.remove(i); 892 cancelNotificationLocked(r); 893 canceledSomething = true; 894 } 895 if (canceledSomething) { 896 updateLightsLocked(); 897 } 898 } 899 } 900 901 902 public void cancelNotification(String pkg, int id) { 903 cancelNotificationWithTag(pkg, null /* tag */, id); 904 } 905 906 public void cancelNotificationWithTag(String pkg, String tag, int id) { 907 checkIncomingCall(pkg); 908 // Don't allow client applications to cancel foreground service notis. 909 cancelNotification(pkg, tag, id, 0, 910 Binder.getCallingUid() == Process.SYSTEM_UID 911 ? 0 : Notification.FLAG_FOREGROUND_SERVICE); 912 } 913 914 public void cancelAllNotifications(String pkg) { 915 checkIncomingCall(pkg); 916 917 // Calling from user space, don't allow the canceling of actively 918 // running foreground services. 919 cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE); 920 } 921 922 void checkIncomingCall(String pkg) { 923 int uid = Binder.getCallingUid(); 924 if (uid == Process.SYSTEM_UID || uid == 0) { 925 return; 926 } 927 try { 928 ApplicationInfo ai = mContext.getPackageManager().getApplicationInfo( 929 pkg, 0); 930 if (ai.uid != uid) { 931 throw new SecurityException("Calling uid " + uid + " gave package" 932 + pkg + " which is owned by uid " + ai.uid); 933 } 934 } catch (PackageManager.NameNotFoundException e) { 935 throw new SecurityException("Unknown package " + pkg); 936 } 937 } 938 939 void cancelAll() { 940 synchronized (mNotificationList) { 941 final int N = mNotificationList.size(); 942 for (int i=N-1; i>=0; i--) { 943 NotificationRecord r = mNotificationList.get(i); 944 945 if ((r.notification.flags & (Notification.FLAG_ONGOING_EVENT 946 | Notification.FLAG_NO_CLEAR)) == 0) { 947 if (r.notification.deleteIntent != null) { 948 try { 949 r.notification.deleteIntent.send(); 950 } catch (PendingIntent.CanceledException ex) { 951 // do nothing - there's no relevant way to recover, and 952 // no reason to let this propagate 953 Log.w(TAG, "canceled PendingIntent for " + r.pkg, ex); 954 } 955 } 956 mNotificationList.remove(i); 957 cancelNotificationLocked(r); 958 } 959 } 960 961 updateLightsLocked(); 962 } 963 } 964 965 private void updateLights() { 966 synchronized (mNotificationList) { 967 updateLightsLocked(); 968 } 969 } 970 971 // lock on mNotificationList 972 private void updateLightsLocked() 973 { 974 // Battery low always shows, other states only show if charging. 975 if (mBatteryLow) { 976 if (mBatteryCharging) { 977 mHardware.setLightColor_UNCHECKED(HardwareService.LIGHT_ID_BATTERY, 978 BATTERY_LOW_ARGB); 979 } else { 980 // Flash when battery is low and not charging 981 mHardware.setLightFlashing_UNCHECKED(HardwareService.LIGHT_ID_BATTERY, 982 BATTERY_LOW_ARGB, HardwareService.LIGHT_FLASH_TIMED, 983 BATTERY_BLINK_ON, BATTERY_BLINK_OFF); 984 } 985 } else if (mBatteryCharging) { 986 if (mBatteryFull) { 987 mHardware.setLightColor_UNCHECKED(HardwareService.LIGHT_ID_BATTERY, 988 BATTERY_FULL_ARGB); 989 } else { 990 mHardware.setLightColor_UNCHECKED(HardwareService.LIGHT_ID_BATTERY, 991 BATTERY_MEDIUM_ARGB); 992 } 993 } else { 994 mHardware.setLightOff_UNCHECKED(HardwareService.LIGHT_ID_BATTERY); 995 } 996 997 // handle notification lights 998 if (mLedNotification == null) { 999 // get next notification, if any 1000 int n = mLights.size(); 1001 if (n > 0) { 1002 mLedNotification = mLights.get(n-1); 1003 } 1004 } 1005 if (mLedNotification == null) { 1006 mHardware.setLightOff_UNCHECKED(HardwareService.LIGHT_ID_NOTIFICATIONS); 1007 } else { 1008 mHardware.setLightFlashing_UNCHECKED( 1009 HardwareService.LIGHT_ID_NOTIFICATIONS, 1010 mLedNotification.notification.ledARGB, 1011 HardwareService.LIGHT_FLASH_TIMED, 1012 mLedNotification.notification.ledOnMS, 1013 mLedNotification.notification.ledOffMS); 1014 } 1015 } 1016 1017 // lock on mNotificationList 1018 private int indexOfNotificationLocked(String pkg, String tag, int id) 1019 { 1020 ArrayList<NotificationRecord> list = mNotificationList; 1021 final int len = list.size(); 1022 for (int i=0; i<len; i++) { 1023 NotificationRecord r = list.get(i); 1024 if (tag == null) { 1025 if (r.tag != null) { 1026 continue; 1027 } 1028 } else { 1029 if (!tag.equals(r.tag)) { 1030 continue; 1031 } 1032 } 1033 if (r.id == id && r.pkg.equals(pkg)) { 1034 return i; 1035 } 1036 } 1037 return -1; 1038 } 1039 1040 // This is here instead of StatusBarPolicy because it is an important 1041 // security feature that we don't want people customizing the platform 1042 // to accidentally lose. 1043 private void updateAdbNotification() { 1044 if (mAdbEnabled && mUsbConnected) { 1045 if ("0".equals(SystemProperties.get("persist.adb.notify"))) { 1046 return; 1047 } 1048 if (!mAdbNotificationShown) { 1049 NotificationManager notificationManager = (NotificationManager) mContext 1050 .getSystemService(Context.NOTIFICATION_SERVICE); 1051 if (notificationManager != null) { 1052 Resources r = mContext.getResources(); 1053 CharSequence title = r.getText( 1054 com.android.internal.R.string.adb_active_notification_title); 1055 CharSequence message = r.getText( 1056 com.android.internal.R.string.adb_active_notification_message); 1057 1058 if (mAdbNotification == null) { 1059 mAdbNotification = new Notification(); 1060 mAdbNotification.icon = com.android.internal.R.drawable.stat_sys_warning; 1061 mAdbNotification.when = 0; 1062 mAdbNotification.flags = Notification.FLAG_ONGOING_EVENT; 1063 mAdbNotification.tickerText = title; 1064 mAdbNotification.defaults |= Notification.DEFAULT_SOUND; 1065 } 1066 1067 Intent intent = new Intent( 1068 Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS); 1069 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | 1070 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 1071 // Note: we are hard-coding the component because this is 1072 // an important security UI that we don't want anyone 1073 // intercepting. 1074 intent.setComponent(new ComponentName("com.android.settings", 1075 "com.android.settings.DevelopmentSettings")); 1076 PendingIntent pi = PendingIntent.getActivity(mContext, 0, 1077 intent, 0); 1078 1079 mAdbNotification.setLatestEventInfo(mContext, title, message, pi); 1080 1081 mAdbNotificationShown = true; 1082 notificationManager.notify( 1083 com.android.internal.R.string.adb_active_notification_title, 1084 mAdbNotification); 1085 } 1086 } 1087 1088 } else if (mAdbNotificationShown) { 1089 NotificationManager notificationManager = (NotificationManager) mContext 1090 .getSystemService(Context.NOTIFICATION_SERVICE); 1091 if (notificationManager != null) { 1092 mAdbNotificationShown = false; 1093 notificationManager.cancel( 1094 com.android.internal.R.string.adb_active_notification_title); 1095 } 1096 } 1097 } 1098 1099 // ====================================================================== 1100 @Override 1101 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1102 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 1103 != PackageManager.PERMISSION_GRANTED) { 1104 pw.println("Permission Denial: can't dump NotificationManager from from pid=" 1105 + Binder.getCallingPid() 1106 + ", uid=" + Binder.getCallingUid()); 1107 return; 1108 } 1109 1110 pw.println("Current Notification Manager state:"); 1111 1112 int N; 1113 1114 synchronized (mToastQueue) { 1115 N = mToastQueue.size(); 1116 if (N > 0) { 1117 pw.println(" Toast Queue:"); 1118 for (int i=0; i<N; i++) { 1119 mToastQueue.get(i).dump(pw, " "); 1120 } 1121 pw.println(" "); 1122 } 1123 1124 } 1125 1126 synchronized (mNotificationList) { 1127 N = mNotificationList.size(); 1128 if (N > 0) { 1129 pw.println(" Notification List:"); 1130 for (int i=0; i<N; i++) { 1131 mNotificationList.get(i).dump(pw, " ", mContext); 1132 } 1133 pw.println(" "); 1134 } 1135 1136 N = mLights.size(); 1137 if (N > 0) { 1138 pw.println(" Lights List:"); 1139 for (int i=0; i<N; i++) { 1140 mLights.get(i).dump(pw, " ", mContext); 1141 } 1142 pw.println(" "); 1143 } 1144 1145 pw.println(" mSoundNotification=" + mSoundNotification); 1146 pw.println(" mSound=" + mSound); 1147 pw.println(" mVibrateNotification=" + mVibrateNotification); 1148 pw.println(" mDisabledNotifications=0x" + Integer.toHexString(mDisabledNotifications)); 1149 pw.println(" mSystemReady=" + mSystemReady); 1150 } 1151 } 1152} 1153