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