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 17 package com.android.server; 18 19 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; 20 import static org.xmlpull.v1.XmlPullParser.END_TAG; 21 import static org.xmlpull.v1.XmlPullParser.START_TAG; 22 23 import android.app.ActivityManager; 24 import android.app.ActivityManagerNative; 25 import android.app.AppGlobals; 26 import android.app.IActivityManager; 27 import android.app.INotificationManager; 28 import android.app.ITransientNotification; 29 import android.app.Notification; 30 import android.app.PendingIntent; 31 import android.app.StatusBarManager; 32 import android.content.BroadcastReceiver; 33 import android.content.ContentResolver; 34 import android.content.Context; 35 import android.content.Intent; 36 import android.content.IntentFilter; 37 import android.content.pm.ApplicationInfo; 38 import android.content.pm.PackageManager; 39 import android.content.pm.PackageManager.NameNotFoundException; 40 import android.content.res.Resources; 41 import android.database.ContentObserver; 42 import android.media.AudioManager; 43 import android.media.IAudioService; 44 import android.media.IRingtonePlayer; 45 import android.net.Uri; 46 import android.os.Binder; 47 import android.os.Handler; 48 import android.os.IBinder; 49 import android.os.Message; 50 import android.os.Process; 51 import android.os.RemoteException; 52 import android.os.ServiceManager; 53 import android.os.UserHandle; 54 import android.os.Vibrator; 55 import android.provider.Settings; 56 import android.telephony.TelephonyManager; 57 import android.text.TextUtils; 58 import android.util.AtomicFile; 59 import android.util.EventLog; 60 import android.util.Log; 61 import android.util.Slog; 62 import android.util.Xml; 63 import android.view.accessibility.AccessibilityEvent; 64 import android.view.accessibility.AccessibilityManager; 65 import android.widget.RemoteViews; 66 import android.widget.Toast; 67 68 import com.android.internal.statusbar.StatusBarNotification; 69 import com.android.internal.util.FastXmlSerializer; 70 71 import org.xmlpull.v1.XmlPullParser; 72 import org.xmlpull.v1.XmlPullParserException; 73 import org.xmlpull.v1.XmlSerializer; 74 75 import java.io.File; 76 import java.io.FileDescriptor; 77 import java.io.FileInputStream; 78 import java.io.FileNotFoundException; 79 import java.io.FileOutputStream; 80 import java.io.IOException; 81 import java.io.PrintWriter; 82 import java.util.ArrayList; 83 import java.util.Arrays; 84 import java.util.HashSet; 85 86 import libcore.io.IoUtils; 87 88 89 /** {@hide} */ 90 public 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 loadBlockDb()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 writeBlockDb()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 areNotificationsEnabledForPackage(String pkg)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*(). areNotificationsEnabledForPackageInt(String pkg)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 setNotificationsEnabledForPackage(String pkg, boolean enabled)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 idDebugString(Context baseContext, String packageName, int id)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 NotificationRecord(String pkg, String tag, int id, int uid, int initialPid, int userId, int score, Notification notification)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 dump(PrintWriter pw, String prefix, Context baseContext)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 toString()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 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration)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 update(int duration)395 void update(int duration) { 396 this.duration = duration; 397 } 398 dump(PrintWriter pw, String prefix)399 void dump(PrintWriter pw, String prefix) { 400 pw.println(prefix + this); 401 } 402 403 @Override toString()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 { SettingsObserver(Handler handler)582 SettingsObserver(Handler handler) { 583 super(handler); 584 } 585 observe()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 onChange(boolean selfChange)593 @Override public void onChange(boolean selfChange) { 594 update(); 595 } 596 update()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 getLongArray(Resources r, int resid, int maxlen, long[] def)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 NotificationManagerService(Context context, StatusBarManagerService statusBar, LightsService lights)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 systemReady()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 // ============================================================================ enqueueToast(String pkg, ITransientNotification callback, int duration)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 cancelToast(String pkg, ITransientNotification callback)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 showNextToastLocked()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 cancelToastLocked(int index)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 scheduleTimeoutLocked(ToastRecord r, boolean immediate)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 handleTimeout(ToastRecord record)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 indexOfToastLocked(String pkg, ITransientNotification callback)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 keepProcessAliveLocked(int pid)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 handleMessage(Message msg)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 // ============================================================================ enqueueNotificationWithTag(String pkg, String tag, int id, Notification notification, int[] idOut, int userId)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 clamp(int x, int low, int high)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) enqueueNotificationInternal(String pkg, int callingUid, int callingPid, String tag, int id, Notification notification, int[] idOut, int userId)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 sendAccessibilityEvent(Notification notification, CharSequence packageName)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 cancelNotificationLocked(NotificationRecord r, boolean sendDelete)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 */ cancelNotification(String pkg, String tag, int id, int mustHaveFlags, int mustNotHaveFlags, boolean sendDelete, int userId)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 */ notificationMatchesUserId(NotificationRecord r, int userId)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 */ cancelAllNotificationsInt(String pkg, int mustHaveFlags, int mustNotHaveFlags, boolean doit, int userId)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 cancelNotificationWithTag(String pkg, String tag, int id, int userId)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 cancelAllNotifications(String pkg, int userId)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 checkCallerIsSystem()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 checkCallerIsSystemOrSameApp(String pkg)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 cancelAll(int userId)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 updateLightsLocked()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 indexOfNotificationLocked(String pkg, String tag, int id, int userId)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 updateNotificationPulse()1490 private void updateNotificationPulse() { 1491 synchronized (mNotificationList) { 1492 updateLightsLocked(); 1493 } 1494 } 1495 1496 // ====================================================================== 1497 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)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