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