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