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