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