NotificationManagerService.java revision ef9d55a6b004685daefee43b49e833c1030de017
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 if ((n.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) { 772 mAttentionLight.pulse(); 773 } 774 } 775 finally { 776 Binder.restoreCallingIdentity(identity); 777 } 778 } 779 sendAccessibilityEvent(notification, pkg); 780 } else { 781 Slog.e(TAG, "Ignoring notification with icon==0: " + notification); 782 if (old != null && old.statusBarKey != null) { 783 long identity = Binder.clearCallingIdentity(); 784 try { 785 mStatusBar.removeNotification(old.statusBarKey); 786 } 787 finally { 788 Binder.restoreCallingIdentity(identity); 789 } 790 } 791 } 792 793 // If we're not supposed to beep, vibrate, etc. then don't. 794 if (((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0) 795 && (!(old != null 796 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 )) 797 && mSystemReady) { 798 799 final AudioManager audioManager = (AudioManager) mContext 800 .getSystemService(Context.AUDIO_SERVICE); 801 // sound 802 final boolean useDefaultSound = 803 (notification.defaults & Notification.DEFAULT_SOUND) != 0; 804 if (useDefaultSound || notification.sound != null) { 805 Uri uri; 806 if (useDefaultSound) { 807 uri = Settings.System.DEFAULT_NOTIFICATION_URI; 808 } else { 809 uri = notification.sound; 810 } 811 boolean looping = (notification.flags & Notification.FLAG_INSISTENT) != 0; 812 int audioStreamType; 813 if (notification.audioStreamType >= 0) { 814 audioStreamType = notification.audioStreamType; 815 } else { 816 audioStreamType = DEFAULT_STREAM_TYPE; 817 } 818 mSoundNotification = r; 819 // do not play notifications if stream volume is 0 820 // (typically because ringer mode is silent). 821 if (audioManager.getStreamVolume(audioStreamType) != 0) { 822 long identity = Binder.clearCallingIdentity(); 823 try { 824 mSound.play(mContext, uri, looping, audioStreamType); 825 } 826 finally { 827 Binder.restoreCallingIdentity(identity); 828 } 829 } 830 } 831 832 // vibrate 833 final boolean useDefaultVibrate = 834 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; 835 if ((useDefaultVibrate || notification.vibrate != null) 836 && audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_NOTIFICATION)) { 837 mVibrateNotification = r; 838 839 mVibrator.vibrate(useDefaultVibrate ? DEFAULT_VIBRATE_PATTERN 840 : notification.vibrate, 841 ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1); 842 } 843 } 844 845 // this option doesn't shut off the lights 846 847 // light 848 // the most recent thing gets the light 849 mLights.remove(old); 850 if (mLedNotification == old) { 851 mLedNotification = null; 852 } 853 //Slog.i(TAG, "notification.lights=" 854 // + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) != 0)); 855 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) { 856 mLights.add(r); 857 updateLightsLocked(); 858 } else { 859 if (old != null 860 && ((old.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0)) { 861 updateLightsLocked(); 862 } 863 } 864 } 865 866 idOut[0] = id; 867 } 868 869 private void sendAccessibilityEvent(Notification notification, CharSequence packageName) { 870 AccessibilityManager manager = AccessibilityManager.getInstance(mContext); 871 if (!manager.isEnabled()) { 872 return; 873 } 874 875 AccessibilityEvent event = 876 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); 877 event.setPackageName(packageName); 878 event.setClassName(Notification.class.getName()); 879 event.setParcelableData(notification); 880 CharSequence tickerText = notification.tickerText; 881 if (!TextUtils.isEmpty(tickerText)) { 882 event.getText().add(tickerText); 883 } 884 885 manager.sendAccessibilityEvent(event); 886 } 887 888 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete) { 889 // tell the app 890 if (sendDelete) { 891 if (r.notification.deleteIntent != null) { 892 try { 893 r.notification.deleteIntent.send(); 894 } catch (PendingIntent.CanceledException ex) { 895 // do nothing - there's no relevant way to recover, and 896 // no reason to let this propagate 897 Slog.w(TAG, "canceled PendingIntent for " + r.pkg, ex); 898 } 899 } 900 } 901 902 // status bar 903 if (r.notification.icon != 0) { 904 long identity = Binder.clearCallingIdentity(); 905 try { 906 mStatusBar.removeNotification(r.statusBarKey); 907 } 908 finally { 909 Binder.restoreCallingIdentity(identity); 910 } 911 r.statusBarKey = null; 912 } 913 914 // sound 915 if (mSoundNotification == r) { 916 mSoundNotification = null; 917 long identity = Binder.clearCallingIdentity(); 918 try { 919 mSound.stop(); 920 } 921 finally { 922 Binder.restoreCallingIdentity(identity); 923 } 924 } 925 926 // vibrate 927 if (mVibrateNotification == r) { 928 mVibrateNotification = null; 929 long identity = Binder.clearCallingIdentity(); 930 try { 931 mVibrator.cancel(); 932 } 933 finally { 934 Binder.restoreCallingIdentity(identity); 935 } 936 } 937 938 // light 939 mLights.remove(r); 940 if (mLedNotification == r) { 941 mLedNotification = null; 942 } 943 } 944 945 /** 946 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags} 947 * and none of the {@code mustNotHaveFlags}. 948 */ 949 private void cancelNotification(String pkg, String tag, int id, int mustHaveFlags, 950 int mustNotHaveFlags, boolean sendDelete) { 951 EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, tag, 952 mustHaveFlags, mustNotHaveFlags); 953 954 synchronized (mNotificationList) { 955 int index = indexOfNotificationLocked(pkg, tag, id); 956 if (index >= 0) { 957 NotificationRecord r = mNotificationList.get(index); 958 959 if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) { 960 return; 961 } 962 if ((r.notification.flags & mustNotHaveFlags) != 0) { 963 return; 964 } 965 966 mNotificationList.remove(index); 967 968 cancelNotificationLocked(r, sendDelete); 969 updateLightsLocked(); 970 } 971 } 972 } 973 974 /** 975 * Cancels all notifications from a given package that have all of the 976 * {@code mustHaveFlags}. 977 */ 978 boolean cancelAllNotificationsInt(String pkg, int mustHaveFlags, 979 int mustNotHaveFlags, boolean doit) { 980 EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL_ALL, pkg, mustHaveFlags, 981 mustNotHaveFlags); 982 983 synchronized (mNotificationList) { 984 final int N = mNotificationList.size(); 985 boolean canceledSomething = false; 986 for (int i = N-1; i >= 0; --i) { 987 NotificationRecord r = mNotificationList.get(i); 988 if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) { 989 continue; 990 } 991 if ((r.notification.flags & mustNotHaveFlags) != 0) { 992 continue; 993 } 994 if (!r.pkg.equals(pkg)) { 995 continue; 996 } 997 canceledSomething = true; 998 if (!doit) { 999 return true; 1000 } 1001 mNotificationList.remove(i); 1002 cancelNotificationLocked(r, false); 1003 } 1004 if (canceledSomething) { 1005 updateLightsLocked(); 1006 } 1007 return canceledSomething; 1008 } 1009 } 1010 1011 @Deprecated 1012 public void cancelNotification(String pkg, int id) { 1013 cancelNotificationWithTag(pkg, null /* tag */, id); 1014 } 1015 1016 public void cancelNotificationWithTag(String pkg, String tag, int id) { 1017 checkIncomingCall(pkg); 1018 // Don't allow client applications to cancel foreground service notis. 1019 cancelNotification(pkg, tag, id, 0, 1020 Binder.getCallingUid() == Process.SYSTEM_UID 1021 ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false); 1022 } 1023 1024 public void cancelAllNotifications(String pkg) { 1025 checkIncomingCall(pkg); 1026 1027 // Calling from user space, don't allow the canceling of actively 1028 // running foreground services. 1029 cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true); 1030 } 1031 1032 void checkIncomingCall(String pkg) { 1033 int uid = Binder.getCallingUid(); 1034 if (uid == Process.SYSTEM_UID || uid == 0) { 1035 return; 1036 } 1037 try { 1038 ApplicationInfo ai = mContext.getPackageManager().getApplicationInfo( 1039 pkg, 0); 1040 if (!UserId.isSameApp(ai.uid, uid)) { 1041 throw new SecurityException("Calling uid " + uid + " gave package" 1042 + pkg + " which is owned by uid " + ai.uid); 1043 } 1044 } catch (PackageManager.NameNotFoundException e) { 1045 throw new SecurityException("Unknown package " + pkg); 1046 } 1047 } 1048 1049 void cancelAll() { 1050 synchronized (mNotificationList) { 1051 final int N = mNotificationList.size(); 1052 for (int i=N-1; i>=0; i--) { 1053 NotificationRecord r = mNotificationList.get(i); 1054 1055 if ((r.notification.flags & (Notification.FLAG_ONGOING_EVENT 1056 | Notification.FLAG_NO_CLEAR)) == 0) { 1057 mNotificationList.remove(i); 1058 cancelNotificationLocked(r, true); 1059 } 1060 } 1061 1062 updateLightsLocked(); 1063 } 1064 } 1065 1066 // lock on mNotificationList 1067 private void updateLightsLocked() 1068 { 1069 // handle notification lights 1070 if (mLedNotification == null) { 1071 // get next notification, if any 1072 int n = mLights.size(); 1073 if (n > 0) { 1074 mLedNotification = mLights.get(n-1); 1075 } 1076 } 1077 1078 // Don't flash while we are in a call or screen is on 1079 if (mLedNotification == null || mInCall || mScreenOn) { 1080 mNotificationLight.turnOff(); 1081 } else { 1082 int ledARGB = mLedNotification.notification.ledARGB; 1083 int ledOnMS = mLedNotification.notification.ledOnMS; 1084 int ledOffMS = mLedNotification.notification.ledOffMS; 1085 if ((mLedNotification.notification.defaults & Notification.DEFAULT_LIGHTS) != 0) { 1086 ledARGB = mDefaultNotificationColor; 1087 ledOnMS = mDefaultNotificationLedOn; 1088 ledOffMS = mDefaultNotificationLedOff; 1089 } 1090 if (mNotificationPulseEnabled) { 1091 // pulse repeatedly 1092 mNotificationLight.setFlashing(ledARGB, LightsService.LIGHT_FLASH_TIMED, 1093 ledOnMS, ledOffMS); 1094 } 1095 } 1096 } 1097 1098 // lock on mNotificationList 1099 private int indexOfNotificationLocked(String pkg, String tag, int id) 1100 { 1101 ArrayList<NotificationRecord> list = mNotificationList; 1102 final int len = list.size(); 1103 for (int i=0; i<len; i++) { 1104 NotificationRecord r = list.get(i); 1105 if (tag == null) { 1106 if (r.tag != null) { 1107 continue; 1108 } 1109 } else { 1110 if (!tag.equals(r.tag)) { 1111 continue; 1112 } 1113 } 1114 if (r.id == id && r.pkg.equals(pkg)) { 1115 return i; 1116 } 1117 } 1118 return -1; 1119 } 1120 1121 private void updateNotificationPulse() { 1122 synchronized (mNotificationList) { 1123 updateLightsLocked(); 1124 } 1125 } 1126 1127 // ====================================================================== 1128 @Override 1129 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1130 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 1131 != PackageManager.PERMISSION_GRANTED) { 1132 pw.println("Permission Denial: can't dump NotificationManager from from pid=" 1133 + Binder.getCallingPid() 1134 + ", uid=" + Binder.getCallingUid()); 1135 return; 1136 } 1137 1138 pw.println("Current Notification Manager state:"); 1139 1140 int N; 1141 1142 synchronized (mToastQueue) { 1143 N = mToastQueue.size(); 1144 if (N > 0) { 1145 pw.println(" Toast Queue:"); 1146 for (int i=0; i<N; i++) { 1147 mToastQueue.get(i).dump(pw, " "); 1148 } 1149 pw.println(" "); 1150 } 1151 1152 } 1153 1154 synchronized (mNotificationList) { 1155 N = mNotificationList.size(); 1156 if (N > 0) { 1157 pw.println(" Notification List:"); 1158 for (int i=0; i<N; i++) { 1159 mNotificationList.get(i).dump(pw, " ", mContext); 1160 } 1161 pw.println(" "); 1162 } 1163 1164 N = mLights.size(); 1165 if (N > 0) { 1166 pw.println(" Lights List:"); 1167 for (int i=0; i<N; i++) { 1168 mLights.get(i).dump(pw, " ", mContext); 1169 } 1170 pw.println(" "); 1171 } 1172 1173 pw.println(" mSoundNotification=" + mSoundNotification); 1174 pw.println(" mSound=" + mSound); 1175 pw.println(" mVibrateNotification=" + mVibrateNotification); 1176 pw.println(" mDisabledNotifications=0x" + Integer.toHexString(mDisabledNotifications)); 1177 pw.println(" mSystemReady=" + mSystemReady); 1178 } 1179 } 1180} 1181