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