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