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