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