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