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