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