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