NotificationManagerService.java revision c8d7ab9ff9f1ca0790d6fd9b9a2076c3eb63a795
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.notification; 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.ComponentName; 35import android.content.ContentResolver; 36import android.content.Context; 37import android.content.Intent; 38import android.content.IntentFilter; 39import android.content.pm.ApplicationInfo; 40import android.content.pm.PackageInfo; 41import android.content.pm.PackageManager; 42import android.content.pm.PackageManager.NameNotFoundException; 43import android.content.res.Resources; 44import android.database.ContentObserver; 45import android.graphics.Bitmap; 46import android.media.AudioManager; 47import android.media.IRingtonePlayer; 48import android.net.Uri; 49import android.os.Binder; 50import android.os.Environment; 51import android.os.Handler; 52import android.os.IBinder; 53import android.os.IInterface; 54import android.os.Message; 55import android.os.Process; 56import android.os.RemoteException; 57import android.os.UserHandle; 58import android.os.Vibrator; 59import android.provider.Settings; 60import android.service.notification.INotificationListener; 61import android.service.notification.IConditionListener; 62import android.service.notification.IConditionProvider; 63import android.service.notification.NotificationListenerService; 64import android.service.notification.StatusBarNotification; 65import android.service.notification.Condition; 66import android.service.notification.ZenModeConfig; 67import android.telephony.TelephonyManager; 68import android.text.TextUtils; 69import android.util.ArrayMap; 70import android.util.AtomicFile; 71import android.util.Log; 72import android.util.Slog; 73import android.util.Xml; 74import android.view.accessibility.AccessibilityEvent; 75import android.view.accessibility.AccessibilityManager; 76import android.widget.Toast; 77 78import com.android.internal.R; 79import com.android.internal.notification.NotificationScorer; 80import com.android.internal.util.FastXmlSerializer; 81import com.android.server.EventLogTags; 82import com.android.server.SystemService; 83import com.android.server.lights.Light; 84import com.android.server.lights.LightsManager; 85import com.android.server.notification.ManagedServices.ManagedServiceInfo; 86import com.android.server.notification.ManagedServices.UserProfiles; 87import com.android.server.notification.NotificationUsageStats.SingleNotificationStats; 88import com.android.server.statusbar.StatusBarManagerInternal; 89 90import libcore.io.IoUtils; 91 92import org.xmlpull.v1.XmlPullParser; 93import org.xmlpull.v1.XmlPullParserException; 94import org.xmlpull.v1.XmlSerializer; 95 96import java.io.File; 97import java.io.FileDescriptor; 98import java.io.FileInputStream; 99import java.io.FileNotFoundException; 100import java.io.FileOutputStream; 101import java.io.IOException; 102import java.io.PrintWriter; 103import java.lang.reflect.Array; 104import java.util.ArrayDeque; 105import java.util.ArrayList; 106import java.util.Arrays; 107import java.util.HashSet; 108import java.util.Iterator; 109import java.util.NoSuchElementException; 110 111/** {@hide} */ 112public class NotificationManagerService extends SystemService { 113 static final String TAG = "NotificationService"; 114 static final boolean DBG = false; 115 116 static final int MAX_PACKAGE_NOTIFICATIONS = 50; 117 118 // message codes 119 static final int MESSAGE_TIMEOUT = 2; 120 static final int MESSAGE_SAVE_POLICY_FILE = 3; 121 122 static final int LONG_DELAY = 3500; // 3.5 seconds 123 static final int SHORT_DELAY = 2000; // 2 seconds 124 125 static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250}; 126 static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps 127 128 static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION; 129 static final boolean SCORE_ONGOING_HIGHER = false; 130 131 static final int JUNK_SCORE = -1000; 132 static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; 133 static final int SCORE_DISPLAY_THRESHOLD = Notification.PRIORITY_MIN * NOTIFICATION_PRIORITY_MULTIPLIER; 134 135 // Notifications with scores below this will not interrupt the user, either via LED or 136 // sound or vibration 137 static final int SCORE_INTERRUPTION_THRESHOLD = 138 Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER; 139 140 static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true; 141 static final boolean ENABLE_BLOCKED_TOASTS = true; 142 143 private IActivityManager mAm; 144 AudioManager mAudioManager; 145 StatusBarManagerInternal mStatusBar; 146 Vibrator mVibrator; 147 148 final IBinder mForegroundToken = new Binder(); 149 private WorkerHandler mHandler; 150 151 private Light mNotificationLight; 152 Light mAttentionLight; 153 private int mDefaultNotificationColor; 154 private int mDefaultNotificationLedOn; 155 156 private int mDefaultNotificationLedOff; 157 private long[] mDefaultVibrationPattern; 158 159 private long[] mFallbackVibrationPattern; 160 boolean mSystemReady; 161 162 private boolean mDisableNotificationAlerts; 163 NotificationRecord mSoundNotification; 164 NotificationRecord mVibrateNotification; 165 166 // for enabling and disabling notification pulse behavior 167 private boolean mScreenOn = true; 168 private boolean mInCall = false; 169 private boolean mNotificationPulseEnabled; 170 171 // used as a mutex for access to all active notifications & listeners 172 final ArrayList<NotificationRecord> mNotificationList = 173 new ArrayList<NotificationRecord>(); 174 final ArrayMap<String, NotificationRecord> mNotificationsByKey = 175 new ArrayMap<String, NotificationRecord>(); 176 final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>(); 177 178 ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>(); 179 NotificationRecord mLedNotification; 180 181 private AppOpsManager mAppOps; 182 183 // Notification control database. For now just contains disabled packages. 184 private AtomicFile mPolicyFile; 185 private HashSet<String> mBlockedPackages = new HashSet<String>(); 186 187 private static final int DB_VERSION = 1; 188 189 private static final String TAG_BODY = "notification-policy"; 190 private static final String ATTR_VERSION = "version"; 191 192 private static final String TAG_BLOCKED_PKGS = "blocked-packages"; 193 private static final String TAG_PACKAGE = "package"; 194 private static final String ATTR_NAME = "name"; 195 196 final ArrayList<NotificationScorer> mScorers = new ArrayList<NotificationScorer>(); 197 198 private final UserProfiles mUserProfiles = new UserProfiles(); 199 private NotificationListeners mListeners; 200 private ConditionProviders mConditionProviders; 201 202 private final NotificationUsageStats mUsageStats = new NotificationUsageStats(); 203 204 private static final String EXTRA_INTERCEPT = "android.intercept"; 205 206 private static final int MY_UID = Process.myUid(); 207 private static final int MY_PID = Process.myPid(); 208 private static final int REASON_DELEGATE_CLICK = 1; 209 private static final int REASON_DELEGATE_CANCEL = 2; 210 private static final int REASON_DELEGATE_CANCEL_ALL = 3; 211 private static final int REASON_DELEGATE_ERROR = 4; 212 private static final int REASON_PACKAGE_CHANGED = 5; 213 private static final int REASON_USER_STOPPED = 6; 214 private static final int REASON_PACKAGE_BANNED = 7; 215 private static final int REASON_NOMAN_CANCEL = 8; 216 private static final int REASON_NOMAN_CANCEL_ALL = 9; 217 private static final int REASON_LISTENER_CANCEL = 10; 218 private static final int REASON_LISTENER_CANCEL_ALL = 11; 219 220 private static class Archive { 221 static final int BUFFER_SIZE = 250; 222 ArrayDeque<StatusBarNotification> mBuffer = new ArrayDeque<StatusBarNotification>(BUFFER_SIZE); 223 224 public Archive() { 225 } 226 227 public String toString() { 228 final StringBuilder sb = new StringBuilder(); 229 final int N = mBuffer.size(); 230 sb.append("Archive ("); 231 sb.append(N); 232 sb.append(" notification"); 233 sb.append((N==1)?")":"s)"); 234 return sb.toString(); 235 } 236 237 public void record(StatusBarNotification nr) { 238 if (mBuffer.size() == BUFFER_SIZE) { 239 mBuffer.removeFirst(); 240 } 241 242 // We don't want to store the heavy bits of the notification in the archive, 243 // but other clients in the system process might be using the object, so we 244 // store a (lightened) copy. 245 mBuffer.addLast(nr.cloneLight()); 246 } 247 248 249 public void clear() { 250 mBuffer.clear(); 251 } 252 253 public Iterator<StatusBarNotification> descendingIterator() { 254 return mBuffer.descendingIterator(); 255 } 256 public Iterator<StatusBarNotification> ascendingIterator() { 257 return mBuffer.iterator(); 258 } 259 public Iterator<StatusBarNotification> filter( 260 final Iterator<StatusBarNotification> iter, final String pkg, final int userId) { 261 return new Iterator<StatusBarNotification>() { 262 StatusBarNotification mNext = findNext(); 263 264 private StatusBarNotification findNext() { 265 while (iter.hasNext()) { 266 StatusBarNotification nr = iter.next(); 267 if ((pkg == null || nr.getPackageName() == pkg) 268 && (userId == UserHandle.USER_ALL || nr.getUserId() == userId)) { 269 return nr; 270 } 271 } 272 return null; 273 } 274 275 @Override 276 public boolean hasNext() { 277 return mNext == null; 278 } 279 280 @Override 281 public StatusBarNotification next() { 282 StatusBarNotification next = mNext; 283 if (next == null) { 284 throw new NoSuchElementException(); 285 } 286 mNext = findNext(); 287 return next; 288 } 289 290 @Override 291 public void remove() { 292 iter.remove(); 293 } 294 }; 295 } 296 297 public StatusBarNotification[] getArray(int count) { 298 if (count == 0) count = Archive.BUFFER_SIZE; 299 final StatusBarNotification[] a 300 = new StatusBarNotification[Math.min(count, mBuffer.size())]; 301 Iterator<StatusBarNotification> iter = descendingIterator(); 302 int i=0; 303 while (iter.hasNext() && i < count) { 304 a[i++] = iter.next(); 305 } 306 return a; 307 } 308 309 public StatusBarNotification[] getArray(int count, String pkg, int userId) { 310 if (count == 0) count = Archive.BUFFER_SIZE; 311 final StatusBarNotification[] a 312 = new StatusBarNotification[Math.min(count, mBuffer.size())]; 313 Iterator<StatusBarNotification> iter = filter(descendingIterator(), pkg, userId); 314 int i=0; 315 while (iter.hasNext() && i < count) { 316 a[i++] = iter.next(); 317 } 318 return a; 319 } 320 321 } 322 323 Archive mArchive = new Archive(); 324 325 private void loadPolicyFile() { 326 synchronized(mPolicyFile) { 327 mBlockedPackages.clear(); 328 329 FileInputStream infile = null; 330 try { 331 infile = mPolicyFile.openRead(); 332 final XmlPullParser parser = Xml.newPullParser(); 333 parser.setInput(infile, null); 334 335 int type; 336 String tag; 337 int version = DB_VERSION; 338 while ((type = parser.next()) != END_DOCUMENT) { 339 tag = parser.getName(); 340 if (type == START_TAG) { 341 if (TAG_BODY.equals(tag)) { 342 version = Integer.parseInt( 343 parser.getAttributeValue(null, ATTR_VERSION)); 344 } else if (TAG_BLOCKED_PKGS.equals(tag)) { 345 while ((type = parser.next()) != END_DOCUMENT) { 346 tag = parser.getName(); 347 if (TAG_PACKAGE.equals(tag)) { 348 mBlockedPackages.add( 349 parser.getAttributeValue(null, ATTR_NAME)); 350 } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) { 351 break; 352 } 353 } 354 } 355 } 356 mZenModeHelper.readXml(parser); 357 } 358 } catch (FileNotFoundException e) { 359 // No data yet 360 } catch (IOException e) { 361 Log.wtf(TAG, "Unable to read notification policy", e); 362 } catch (NumberFormatException e) { 363 Log.wtf(TAG, "Unable to parse notification policy", e); 364 } catch (XmlPullParserException e) { 365 Log.wtf(TAG, "Unable to parse notification policy", e); 366 } finally { 367 IoUtils.closeQuietly(infile); 368 } 369 } 370 } 371 372 public void savePolicyFile() { 373 mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE); 374 mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE); 375 } 376 377 private void handleSavePolicyFile() { 378 Slog.d(TAG, "handleSavePolicyFile"); 379 synchronized (mPolicyFile) { 380 final FileOutputStream stream; 381 try { 382 stream = mPolicyFile.startWrite(); 383 } catch (IOException e) { 384 Slog.w(TAG, "Failed to save policy file", e); 385 return; 386 } 387 388 try { 389 final XmlSerializer out = new FastXmlSerializer(); 390 out.setOutput(stream, "utf-8"); 391 out.startDocument(null, true); 392 out.startTag(null, TAG_BODY); 393 out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION)); 394 mZenModeHelper.writeXml(out); 395 out.endTag(null, TAG_BODY); 396 out.endDocument(); 397 mPolicyFile.finishWrite(stream); 398 } catch (IOException e) { 399 Slog.w(TAG, "Failed to save policy file, restoring backup", e); 400 mPolicyFile.failWrite(stream); 401 } 402 } 403 } 404 405 /** Use this when you actually want to post a notification or toast. 406 * 407 * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*(). 408 */ 409 private boolean noteNotificationOp(String pkg, int uid) { 410 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg) 411 != AppOpsManager.MODE_ALLOWED) { 412 Slog.v(TAG, "notifications are disabled by AppOps for " + pkg); 413 return false; 414 } 415 return true; 416 } 417 418 private static String idDebugString(Context baseContext, String packageName, int id) { 419 Context c = null; 420 421 if (packageName != null) { 422 try { 423 c = baseContext.createPackageContext(packageName, 0); 424 } catch (NameNotFoundException e) { 425 c = baseContext; 426 } 427 } else { 428 c = baseContext; 429 } 430 431 String pkg; 432 String type; 433 String name; 434 435 Resources r = c.getResources(); 436 try { 437 return r.getResourceName(id); 438 } catch (Resources.NotFoundException e) { 439 return "<name unknown>"; 440 } 441 } 442 443 444 445 public static final class NotificationRecord 446 { 447 final StatusBarNotification sbn; 448 final SingleNotificationStats stats = new SingleNotificationStats(); 449 IBinder statusBarKey; 450 451 NotificationRecord(StatusBarNotification sbn) 452 { 453 this.sbn = sbn; 454 } 455 456 public Notification getNotification() { return sbn.getNotification(); } 457 public int getFlags() { return sbn.getNotification().flags; } 458 public int getUserId() { return sbn.getUserId(); } 459 460 void dump(PrintWriter pw, String prefix, Context baseContext) { 461 final Notification notification = sbn.getNotification(); 462 pw.println(prefix + this); 463 pw.println(prefix + " uid=" + sbn.getUid() + " userId=" + sbn.getUserId()); 464 pw.println(prefix + " icon=0x" + Integer.toHexString(notification.icon) 465 + " / " + idDebugString(baseContext, sbn.getPackageName(), notification.icon)); 466 pw.println(prefix + " pri=" + notification.priority + " score=" + sbn.getScore()); 467 pw.println(prefix + " key=" + sbn.getKey()); 468 pw.println(prefix + " contentIntent=" + notification.contentIntent); 469 pw.println(prefix + " deleteIntent=" + notification.deleteIntent); 470 pw.println(prefix + " tickerText=" + notification.tickerText); 471 pw.println(prefix + " contentView=" + notification.contentView); 472 pw.println(prefix + String.format(" defaults=0x%08x flags=0x%08x", 473 notification.defaults, notification.flags)); 474 pw.println(prefix + " sound=" + notification.sound); 475 pw.println(prefix + " vibrate=" + Arrays.toString(notification.vibrate)); 476 pw.println(prefix + String.format(" led=0x%08x onMs=%d offMs=%d", 477 notification.ledARGB, notification.ledOnMS, notification.ledOffMS)); 478 if (notification.actions != null && notification.actions.length > 0) { 479 pw.println(prefix + " actions={"); 480 final int N = notification.actions.length; 481 for (int i=0; i<N; i++) { 482 final Notification.Action action = notification.actions[i]; 483 pw.println(String.format("%s [%d] \"%s\" -> %s", 484 prefix, 485 i, 486 action.title, 487 action.actionIntent.toString() 488 )); 489 } 490 pw.println(prefix + " }"); 491 } 492 if (notification.extras != null && notification.extras.size() > 0) { 493 pw.println(prefix + " extras={"); 494 for (String key : notification.extras.keySet()) { 495 pw.print(prefix + " " + key + "="); 496 Object val = notification.extras.get(key); 497 if (val == null) { 498 pw.println("null"); 499 } else { 500 pw.print(val.toString()); 501 if (val instanceof Bitmap) { 502 pw.print(String.format(" (%dx%d)", 503 ((Bitmap) val).getWidth(), 504 ((Bitmap) val).getHeight())); 505 } else if (val.getClass().isArray()) { 506 pw.println(" {"); 507 final int N = Array.getLength(val); 508 for (int i=0; i<N; i++) { 509 if (i > 0) pw.println(","); 510 pw.print(prefix + " " + Array.get(val, i)); 511 } 512 pw.print("\n" + prefix + " }"); 513 } 514 pw.println(); 515 } 516 } 517 pw.println(prefix + " }"); 518 } 519 pw.println(prefix + " stats=" + stats.toString()); 520 } 521 522 @Override 523 public final String toString() { 524 return String.format( 525 "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s score=%d key=%s: %s)", 526 System.identityHashCode(this), 527 this.sbn.getPackageName(), this.sbn.getUser(), this.sbn.getId(), 528 this.sbn.getTag(), this.sbn.getScore(), this.sbn.getKey(), 529 this.sbn.getNotification()); 530 } 531 } 532 533 private static final class ToastRecord 534 { 535 final int pid; 536 final String pkg; 537 final ITransientNotification callback; 538 int duration; 539 540 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration) 541 { 542 this.pid = pid; 543 this.pkg = pkg; 544 this.callback = callback; 545 this.duration = duration; 546 } 547 548 void update(int duration) { 549 this.duration = duration; 550 } 551 552 void dump(PrintWriter pw, String prefix) { 553 pw.println(prefix + this); 554 } 555 556 @Override 557 public final String toString() 558 { 559 return "ToastRecord{" 560 + Integer.toHexString(System.identityHashCode(this)) 561 + " pkg=" + pkg 562 + " callback=" + callback 563 + " duration=" + duration; 564 } 565 } 566 567 private final NotificationDelegate mNotificationDelegate = new NotificationDelegate() { 568 569 @Override 570 public void onSetDisabled(int status) { 571 synchronized (mNotificationList) { 572 mDisableNotificationAlerts = (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0; 573 if (mDisableNotificationAlerts) { 574 // cancel whatever's going on 575 long identity = Binder.clearCallingIdentity(); 576 try { 577 final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); 578 if (player != null) { 579 player.stopAsync(); 580 } 581 } catch (RemoteException e) { 582 } finally { 583 Binder.restoreCallingIdentity(identity); 584 } 585 586 identity = Binder.clearCallingIdentity(); 587 try { 588 mVibrator.cancel(); 589 } finally { 590 Binder.restoreCallingIdentity(identity); 591 } 592 } 593 } 594 } 595 596 @Override 597 public void onClearAll(int callingUid, int callingPid, int userId) { 598 synchronized (mNotificationList) { 599 cancelAllLocked(callingUid, callingPid, userId, REASON_DELEGATE_CANCEL_ALL, null, 600 /*includeCurrentProfiles*/ true); 601 } 602 } 603 604 @Override 605 public void onNotificationClick(int callingUid, int callingPid, 606 String pkg, String tag, int id, int userId) { 607 cancelNotification(callingUid, callingPid, pkg, tag, id, Notification.FLAG_AUTO_CANCEL, 608 Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_DELEGATE_CLICK, null); 609 } 610 611 @Override 612 public void onNotificationClear(int callingUid, int callingPid, 613 String pkg, String tag, int id, int userId) { 614 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 615 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE, 616 true, userId, REASON_DELEGATE_CANCEL, null); 617 } 618 619 @Override 620 public void onPanelRevealed() { 621 EventLogTags.writeNotificationPanelRevealed(); 622 synchronized (mNotificationList) { 623 // sound 624 mSoundNotification = null; 625 626 long identity = Binder.clearCallingIdentity(); 627 try { 628 final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); 629 if (player != null) { 630 player.stopAsync(); 631 } 632 } catch (RemoteException e) { 633 } finally { 634 Binder.restoreCallingIdentity(identity); 635 } 636 637 // vibrate 638 mVibrateNotification = null; 639 identity = Binder.clearCallingIdentity(); 640 try { 641 mVibrator.cancel(); 642 } finally { 643 Binder.restoreCallingIdentity(identity); 644 } 645 646 // light 647 mLights.clear(); 648 mLedNotification = null; 649 updateLightsLocked(); 650 } 651 } 652 653 @Override 654 public void onPanelHidden() { 655 EventLogTags.writeNotificationPanelHidden(); 656 } 657 658 @Override 659 public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id, 660 int uid, int initialPid, String message, int userId) { 661 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id 662 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")"); 663 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId, 664 REASON_DELEGATE_ERROR, null); 665 long ident = Binder.clearCallingIdentity(); 666 try { 667 ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg, 668 "Bad notification posted from package " + pkg 669 + ": " + message); 670 } catch (RemoteException e) { 671 } 672 Binder.restoreCallingIdentity(ident); 673 } 674 675 @Override 676 public boolean allowDisable(int what, IBinder token, String pkg) { 677 return mZenModeHelper.allowDisable(what, token, pkg); 678 } 679 680 @Override 681 public void onNotificationVisibilityChanged( 682 String[] newlyVisibleKeys, String[] noLongerVisibleKeys) { 683 // Using ';' as separator since eventlogs uses ',' to separate 684 // args. 685 EventLogTags.writeNotificationVisibilityChanged( 686 TextUtils.join(";", newlyVisibleKeys), 687 TextUtils.join(";", noLongerVisibleKeys)); 688 } 689 }; 690 691 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 692 @Override 693 public void onReceive(Context context, Intent intent) { 694 String action = intent.getAction(); 695 696 boolean queryRestart = false; 697 boolean queryRemove = false; 698 boolean packageChanged = false; 699 boolean cancelNotifications = true; 700 701 if (action.equals(Intent.ACTION_PACKAGE_ADDED) 702 || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED)) 703 || action.equals(Intent.ACTION_PACKAGE_RESTARTED) 704 || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED)) 705 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART)) 706 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { 707 String pkgList[] = null; 708 boolean queryReplace = queryRemove && 709 intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); 710 if (DBG) Slog.i(TAG, "action=" + action + " queryReplace=" + queryReplace); 711 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { 712 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); 713 } else if (queryRestart) { 714 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES); 715 } else { 716 Uri uri = intent.getData(); 717 if (uri == null) { 718 return; 719 } 720 String pkgName = uri.getSchemeSpecificPart(); 721 if (pkgName == null) { 722 return; 723 } 724 if (packageChanged) { 725 // We cancel notifications for packages which have just been disabled 726 try { 727 final int enabled = getContext().getPackageManager() 728 .getApplicationEnabledSetting(pkgName); 729 if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED 730 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) { 731 cancelNotifications = false; 732 } 733 } catch (IllegalArgumentException e) { 734 // Package doesn't exist; probably racing with uninstall. 735 // cancelNotifications is already true, so nothing to do here. 736 if (DBG) { 737 Slog.i(TAG, "Exception trying to look up app enabled setting", e); 738 } 739 } 740 } 741 pkgList = new String[]{pkgName}; 742 } 743 744 if (pkgList != null && (pkgList.length > 0)) { 745 for (String pkgName : pkgList) { 746 if (cancelNotifications) { 747 cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart, 748 UserHandle.USER_ALL, REASON_PACKAGE_CHANGED, null); 749 } 750 } 751 } 752 mListeners.onPackagesChanged(queryReplace, pkgList); 753 mConditionProviders.onPackagesChanged(queryReplace, pkgList); 754 } else if (action.equals(Intent.ACTION_SCREEN_ON)) { 755 // Keep track of screen on/off state, but do not turn off the notification light 756 // until user passes through the lock screen or views the notification. 757 mScreenOn = true; 758 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { 759 mScreenOn = false; 760 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) { 761 mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK 762 .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE)); 763 updateNotificationPulse(); 764 } else if (action.equals(Intent.ACTION_USER_STOPPED)) { 765 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); 766 if (userHandle >= 0) { 767 cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle, 768 REASON_USER_STOPPED, null); 769 } 770 } else if (action.equals(Intent.ACTION_USER_PRESENT)) { 771 // turn off LED when user passes through lock screen 772 mNotificationLight.turnOff(); 773 } else if (action.equals(Intent.ACTION_USER_SWITCHED)) { 774 // reload per-user settings 775 mSettingsObserver.update(null); 776 mUserProfiles.updateCache(context); 777 } else if (action.equals(Intent.ACTION_USER_ADDED)) { 778 mUserProfiles.updateCache(context); 779 } 780 } 781 }; 782 783 class SettingsObserver extends ContentObserver { 784 private final Uri NOTIFICATION_LIGHT_PULSE_URI 785 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE); 786 787 SettingsObserver(Handler handler) { 788 super(handler); 789 } 790 791 void observe() { 792 ContentResolver resolver = getContext().getContentResolver(); 793 resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI, 794 false, this, UserHandle.USER_ALL); 795 update(null); 796 } 797 798 @Override public void onChange(boolean selfChange, Uri uri) { 799 update(uri); 800 } 801 802 public void update(Uri uri) { 803 ContentResolver resolver = getContext().getContentResolver(); 804 if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) { 805 boolean pulseEnabled = Settings.System.getInt(resolver, 806 Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0; 807 if (mNotificationPulseEnabled != pulseEnabled) { 808 mNotificationPulseEnabled = pulseEnabled; 809 updateNotificationPulse(); 810 } 811 } 812 } 813 } 814 815 private SettingsObserver mSettingsObserver; 816 private ZenModeHelper mZenModeHelper; 817 818 static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) { 819 int[] ar = r.getIntArray(resid); 820 if (ar == null) { 821 return def; 822 } 823 final int len = ar.length > maxlen ? maxlen : ar.length; 824 long[] out = new long[len]; 825 for (int i=0; i<len; i++) { 826 out[i] = ar[i]; 827 } 828 return out; 829 } 830 831 public NotificationManagerService(Context context) { 832 super(context); 833 } 834 835 @Override 836 public void onStart() { 837 mAm = ActivityManagerNative.getDefault(); 838 mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE); 839 mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE); 840 841 mHandler = new WorkerHandler(); 842 mZenModeHelper = new ZenModeHelper(getContext(), mHandler); 843 mZenModeHelper.addCallback(new ZenModeHelper.Callback() { 844 @Override 845 public void onConfigChanged() { 846 savePolicyFile(); 847 } 848 }); 849 final File systemDir = new File(Environment.getDataDirectory(), "system"); 850 mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml")); 851 852 importOldBlockDb(); 853 854 mListeners = new NotificationListeners(); 855 mConditionProviders = new ConditionProviders(getContext(), 856 mHandler, mUserProfiles, mZenModeHelper); 857 mStatusBar = getLocalService(StatusBarManagerInternal.class); 858 mStatusBar.setNotificationDelegate(mNotificationDelegate); 859 860 final LightsManager lights = getLocalService(LightsManager.class); 861 mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS); 862 mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION); 863 864 Resources resources = getContext().getResources(); 865 mDefaultNotificationColor = resources.getColor( 866 R.color.config_defaultNotificationColor); 867 mDefaultNotificationLedOn = resources.getInteger( 868 R.integer.config_defaultNotificationLedOn); 869 mDefaultNotificationLedOff = resources.getInteger( 870 R.integer.config_defaultNotificationLedOff); 871 872 mDefaultVibrationPattern = getLongArray(resources, 873 R.array.config_defaultNotificationVibePattern, 874 VIBRATE_PATTERN_MAXLEN, 875 DEFAULT_VIBRATE_PATTERN); 876 877 mFallbackVibrationPattern = getLongArray(resources, 878 R.array.config_notificationFallbackVibePattern, 879 VIBRATE_PATTERN_MAXLEN, 880 DEFAULT_VIBRATE_PATTERN); 881 882 // Don't start allowing notifications until the setup wizard has run once. 883 // After that, including subsequent boots, init with notifications turned on. 884 // This works on the first boot because the setup wizard will toggle this 885 // flag at least once and we'll go back to 0 after that. 886 if (0 == Settings.Global.getInt(getContext().getContentResolver(), 887 Settings.Global.DEVICE_PROVISIONED, 0)) { 888 mDisableNotificationAlerts = true; 889 } 890 mZenModeHelper.updateZenMode(); 891 892 mUserProfiles.updateCache(getContext()); 893 894 // register for various Intents 895 IntentFilter filter = new IntentFilter(); 896 filter.addAction(Intent.ACTION_SCREEN_ON); 897 filter.addAction(Intent.ACTION_SCREEN_OFF); 898 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); 899 filter.addAction(Intent.ACTION_USER_PRESENT); 900 filter.addAction(Intent.ACTION_USER_STOPPED); 901 filter.addAction(Intent.ACTION_USER_SWITCHED); 902 filter.addAction(Intent.ACTION_USER_ADDED); 903 getContext().registerReceiver(mIntentReceiver, filter); 904 IntentFilter pkgFilter = new IntentFilter(); 905 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED); 906 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 907 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); 908 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); 909 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); 910 pkgFilter.addDataScheme("package"); 911 getContext().registerReceiver(mIntentReceiver, pkgFilter); 912 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); 913 getContext().registerReceiver(mIntentReceiver, sdFilter); 914 915 mSettingsObserver = new SettingsObserver(mHandler); 916 917 // spin up NotificationScorers 918 String[] notificationScorerNames = resources.getStringArray( 919 R.array.config_notificationScorers); 920 for (String scorerName : notificationScorerNames) { 921 try { 922 Class<?> scorerClass = getContext().getClassLoader().loadClass(scorerName); 923 NotificationScorer scorer = (NotificationScorer) scorerClass.newInstance(); 924 scorer.initialize(getContext()); 925 mScorers.add(scorer); 926 } catch (ClassNotFoundException e) { 927 Slog.w(TAG, "Couldn't find scorer " + scorerName + ".", e); 928 } catch (InstantiationException e) { 929 Slog.w(TAG, "Couldn't instantiate scorer " + scorerName + ".", e); 930 } catch (IllegalAccessException e) { 931 Slog.w(TAG, "Problem accessing scorer " + scorerName + ".", e); 932 } 933 } 934 935 publishBinderService(Context.NOTIFICATION_SERVICE, mService); 936 publishLocalService(NotificationManagerInternal.class, mInternalService); 937 } 938 939 /** 940 * Read the old XML-based app block database and import those blockages into the AppOps system. 941 */ 942 private void importOldBlockDb() { 943 loadPolicyFile(); 944 945 PackageManager pm = getContext().getPackageManager(); 946 for (String pkg : mBlockedPackages) { 947 PackageInfo info = null; 948 try { 949 info = pm.getPackageInfo(pkg, 0); 950 setNotificationsEnabledForPackageImpl(pkg, info.applicationInfo.uid, false); 951 } catch (NameNotFoundException e) { 952 // forget you 953 } 954 } 955 mBlockedPackages.clear(); 956 } 957 958 @Override 959 public void onBootPhase(int phase) { 960 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { 961 // no beeping until we're basically done booting 962 mSystemReady = true; 963 964 // Grab our optional AudioService 965 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); 966 967 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { 968 // This observer will force an update when observe is called, causing us to 969 // bind to listener services. 970 mSettingsObserver.observe(); 971 mListeners.onBootPhaseAppsCanStart(); 972 mConditionProviders.onBootPhaseAppsCanStart(); 973 } 974 } 975 976 void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) { 977 Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg); 978 979 mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg, 980 enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED); 981 982 // Now, cancel any outstanding notifications that are part of a just-disabled app 983 if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) { 984 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid), 985 REASON_PACKAGE_BANNED, null); 986 } 987 } 988 989 private final IBinder mService = new INotificationManager.Stub() { 990 // Toasts 991 // ============================================================================ 992 993 @Override 994 public void enqueueToast(String pkg, ITransientNotification callback, int duration) 995 { 996 if (DBG) { 997 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback 998 + " duration=" + duration); 999 } 1000 1001 if (pkg == null || callback == null) { 1002 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback); 1003 return ; 1004 } 1005 1006 final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg)); 1007 1008 if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) { 1009 if (!isSystemToast) { 1010 Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request."); 1011 return; 1012 } 1013 } 1014 1015 synchronized (mToastQueue) { 1016 int callingPid = Binder.getCallingPid(); 1017 long callingId = Binder.clearCallingIdentity(); 1018 try { 1019 ToastRecord record; 1020 int index = indexOfToastLocked(pkg, callback); 1021 // If it's already in the queue, we update it in place, we don't 1022 // move it to the end of the queue. 1023 if (index >= 0) { 1024 record = mToastQueue.get(index); 1025 record.update(duration); 1026 } else { 1027 // Limit the number of toasts that any given package except the android 1028 // package can enqueue. Prevents DOS attacks and deals with leaks. 1029 if (!isSystemToast) { 1030 int count = 0; 1031 final int N = mToastQueue.size(); 1032 for (int i=0; i<N; i++) { 1033 final ToastRecord r = mToastQueue.get(i); 1034 if (r.pkg.equals(pkg)) { 1035 count++; 1036 if (count >= MAX_PACKAGE_NOTIFICATIONS) { 1037 Slog.e(TAG, "Package has already posted " + count 1038 + " toasts. Not showing more. Package=" + pkg); 1039 return; 1040 } 1041 } 1042 } 1043 } 1044 1045 record = new ToastRecord(callingPid, pkg, callback, duration); 1046 mToastQueue.add(record); 1047 index = mToastQueue.size() - 1; 1048 keepProcessAliveLocked(callingPid); 1049 } 1050 // If it's at index 0, it's the current toast. It doesn't matter if it's 1051 // new or just been updated. Call back and tell it to show itself. 1052 // If the callback fails, this will remove it from the list, so don't 1053 // assume that it's valid after this. 1054 if (index == 0) { 1055 showNextToastLocked(); 1056 } 1057 } finally { 1058 Binder.restoreCallingIdentity(callingId); 1059 } 1060 } 1061 } 1062 1063 @Override 1064 public void cancelToast(String pkg, ITransientNotification callback) { 1065 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback); 1066 1067 if (pkg == null || callback == null) { 1068 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback); 1069 return ; 1070 } 1071 1072 synchronized (mToastQueue) { 1073 long callingId = Binder.clearCallingIdentity(); 1074 try { 1075 int index = indexOfToastLocked(pkg, callback); 1076 if (index >= 0) { 1077 cancelToastLocked(index); 1078 } else { 1079 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg 1080 + " callback=" + callback); 1081 } 1082 } finally { 1083 Binder.restoreCallingIdentity(callingId); 1084 } 1085 } 1086 } 1087 1088 @Override 1089 public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id, 1090 Notification notification, int[] idOut, int userId) throws RemoteException { 1091 enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(), 1092 Binder.getCallingPid(), tag, id, notification, idOut, userId); 1093 } 1094 1095 @Override 1096 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) { 1097 checkCallerIsSystemOrSameApp(pkg); 1098 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 1099 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg); 1100 // Don't allow client applications to cancel foreground service notis. 1101 cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0, 1102 Binder.getCallingUid() == Process.SYSTEM_UID 1103 ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_NOMAN_CANCEL, 1104 null); 1105 } 1106 1107 @Override 1108 public void cancelAllNotifications(String pkg, int userId) { 1109 checkCallerIsSystemOrSameApp(pkg); 1110 1111 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 1112 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg); 1113 1114 // Calling from user space, don't allow the canceling of actively 1115 // running foreground services. 1116 cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(), 1117 pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId, 1118 REASON_NOMAN_CANCEL_ALL, null); 1119 } 1120 1121 @Override 1122 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) { 1123 checkCallerIsSystem(); 1124 1125 setNotificationsEnabledForPackageImpl(pkg, uid, enabled); 1126 } 1127 1128 /** 1129 * Use this when you just want to know if notifications are OK for this package. 1130 */ 1131 @Override 1132 public boolean areNotificationsEnabledForPackage(String pkg, int uid) { 1133 checkCallerIsSystem(); 1134 return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg) 1135 == AppOpsManager.MODE_ALLOWED); 1136 } 1137 1138 /** 1139 * System-only API for getting a list of current (i.e. not cleared) notifications. 1140 * 1141 * Requires ACCESS_NOTIFICATIONS which is signature|system. 1142 */ 1143 @Override 1144 public StatusBarNotification[] getActiveNotifications(String callingPkg) { 1145 // enforce() will ensure the calling uid has the correct permission 1146 getContext().enforceCallingOrSelfPermission( 1147 android.Manifest.permission.ACCESS_NOTIFICATIONS, 1148 "NotificationManagerService.getActiveNotifications"); 1149 1150 StatusBarNotification[] tmp = null; 1151 int uid = Binder.getCallingUid(); 1152 1153 // noteOp will check to make sure the callingPkg matches the uid 1154 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg) 1155 == AppOpsManager.MODE_ALLOWED) { 1156 synchronized (mNotificationList) { 1157 tmp = new StatusBarNotification[mNotificationList.size()]; 1158 final int N = mNotificationList.size(); 1159 for (int i=0; i<N; i++) { 1160 tmp[i] = mNotificationList.get(i).sbn; 1161 } 1162 } 1163 } 1164 return tmp; 1165 } 1166 1167 /** 1168 * System-only API for getting a list of recent (cleared, no longer shown) notifications. 1169 * 1170 * Requires ACCESS_NOTIFICATIONS which is signature|system. 1171 */ 1172 @Override 1173 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) { 1174 // enforce() will ensure the calling uid has the correct permission 1175 getContext().enforceCallingOrSelfPermission( 1176 android.Manifest.permission.ACCESS_NOTIFICATIONS, 1177 "NotificationManagerService.getHistoricalNotifications"); 1178 1179 StatusBarNotification[] tmp = null; 1180 int uid = Binder.getCallingUid(); 1181 1182 // noteOp will check to make sure the callingPkg matches the uid 1183 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg) 1184 == AppOpsManager.MODE_ALLOWED) { 1185 synchronized (mArchive) { 1186 tmp = mArchive.getArray(count); 1187 } 1188 } 1189 return tmp; 1190 } 1191 1192 /** 1193 * Register a listener binder directly with the notification manager. 1194 * 1195 * Only works with system callers. Apps should extend 1196 * {@link android.service.notification.NotificationListenerService}. 1197 */ 1198 @Override 1199 public void registerListener(final INotificationListener listener, 1200 final ComponentName component, final int userid) { 1201 checkCallerIsSystem(); 1202 mListeners.registerService(listener, component, userid); 1203 } 1204 1205 /** 1206 * Remove a listener binder directly 1207 */ 1208 @Override 1209 public void unregisterListener(INotificationListener listener, int userid) { 1210 mListeners.unregisterService(listener, userid); 1211 } 1212 1213 /** 1214 * Allow an INotificationListener to simulate a "clear all" operation. 1215 * 1216 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications} 1217 * 1218 * @param token The binder for the listener, to check that the caller is allowed 1219 */ 1220 @Override 1221 public void cancelNotificationsFromListener(INotificationListener token, String[] keys) { 1222 final int callingUid = Binder.getCallingUid(); 1223 final int callingPid = Binder.getCallingPid(); 1224 long identity = Binder.clearCallingIdentity(); 1225 try { 1226 synchronized (mNotificationList) { 1227 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1228 if (keys != null) { 1229 final int N = keys.length; 1230 for (int i = 0; i < N; i++) { 1231 NotificationRecord r = mNotificationsByKey.get(keys[i]); 1232 final int userId = r.sbn.getUserId(); 1233 if (userId != info.userid && userId != UserHandle.USER_ALL && 1234 !mUserProfiles.isCurrentProfile(userId)) { 1235 throw new SecurityException("Disallowed call from listener: " 1236 + info.service); 1237 } 1238 if (r != null) { 1239 cancelNotificationFromListenerLocked(info, callingUid, callingPid, 1240 r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(), 1241 userId); 1242 } 1243 } 1244 } else { 1245 cancelAllLocked(callingUid, callingPid, info.userid, 1246 REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles()); 1247 } 1248 } 1249 } finally { 1250 Binder.restoreCallingIdentity(identity); 1251 } 1252 } 1253 1254 private void cancelNotificationFromListenerLocked(ManagedServiceInfo info, 1255 int callingUid, int callingPid, String pkg, String tag, int id, int userId) { 1256 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 1257 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE, 1258 true, 1259 userId, REASON_LISTENER_CANCEL, info); 1260 } 1261 1262 /** 1263 * Allow an INotificationListener to simulate clearing (dismissing) a single notification. 1264 * 1265 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear} 1266 * 1267 * @param token The binder for the listener, to check that the caller is allowed 1268 */ 1269 @Override 1270 public void cancelNotificationFromListener(INotificationListener token, String pkg, 1271 String tag, int id) { 1272 final int callingUid = Binder.getCallingUid(); 1273 final int callingPid = Binder.getCallingPid(); 1274 long identity = Binder.clearCallingIdentity(); 1275 try { 1276 synchronized (mNotificationList) { 1277 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1278 if (info.supportsProfiles()) { 1279 Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) " 1280 + "from " + info.component 1281 + " use cancelNotification(key) instead."); 1282 } else { 1283 cancelNotificationFromListenerLocked(info, callingUid, callingPid, 1284 pkg, tag, id, info.userid); 1285 } 1286 } 1287 } finally { 1288 Binder.restoreCallingIdentity(identity); 1289 } 1290 } 1291 1292 /** 1293 * Allow an INotificationListener to request the list of outstanding notifications seen by 1294 * the current user. Useful when starting up, after which point the listener callbacks 1295 * should be used. 1296 * 1297 * @param token The binder for the listener, to check that the caller is allowed 1298 */ 1299 @Override 1300 public StatusBarNotification[] getActiveNotificationsFromListener( 1301 INotificationListener token, String[] keys) { 1302 synchronized (mNotificationList) { 1303 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1304 final ArrayList<StatusBarNotification> list 1305 = new ArrayList<StatusBarNotification>(); 1306 if (keys == null) { 1307 final int N = mNotificationList.size(); 1308 for (int i=0; i<N; i++) { 1309 StatusBarNotification sbn = mNotificationList.get(i).sbn; 1310 if (info.enabledAndUserMatches(sbn.getUserId())) { 1311 list.add(sbn); 1312 } 1313 } 1314 } else { 1315 final int N = keys.length; 1316 for (int i=0; i<N; i++) { 1317 NotificationRecord r = mNotificationsByKey.get(keys[i]); 1318 if (r != null && info.enabledAndUserMatches(r.sbn.getUserId())) { 1319 list.add(r.sbn); 1320 } 1321 } 1322 } 1323 return list.toArray(new StatusBarNotification[list.size()]); 1324 } 1325 } 1326 1327 @Override 1328 public String[] getActiveNotificationKeysFromListener(INotificationListener token) { 1329 return NotificationManagerService.this.getActiveNotificationKeysFromListener(token); 1330 } 1331 1332 @Override 1333 public ZenModeConfig getZenModeConfig() { 1334 checkCallerIsSystem(); 1335 return mZenModeHelper.getConfig(); 1336 } 1337 1338 @Override 1339 public boolean setZenModeConfig(ZenModeConfig config) { 1340 checkCallerIsSystem(); 1341 return mZenModeHelper.setConfig(config); 1342 } 1343 1344 @Override 1345 public void notifyConditions(String pkg, IConditionProvider provider, 1346 Condition[] conditions) { 1347 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider); 1348 checkCallerIsSystemOrSameApp(pkg); 1349 final long identity = Binder.clearCallingIdentity(); 1350 try { 1351 mConditionProviders.notifyConditions(pkg, info, conditions); 1352 } finally { 1353 Binder.restoreCallingIdentity(identity); 1354 } 1355 } 1356 1357 @Override 1358 public void requestZenModeConditions(IConditionListener callback, int relevance) { 1359 enforceSystemOrSystemUI("INotificationManager.requestZenModeConditions"); 1360 mConditionProviders.requestZenModeConditions(callback, relevance); 1361 } 1362 1363 @Override 1364 public void setZenModeCondition(Uri conditionId) { 1365 enforceSystemOrSystemUI("INotificationManager.setZenModeCondition"); 1366 mConditionProviders.setZenModeCondition(conditionId); 1367 } 1368 1369 @Override 1370 public void setAutomaticZenModeConditions(Uri[] conditionIds) { 1371 enforceSystemOrSystemUI("INotificationManager.setAutomaticZenModeConditions"); 1372 mConditionProviders.setAutomaticZenModeConditions(conditionIds); 1373 } 1374 1375 @Override 1376 public Condition[] getAutomaticZenModeConditions() { 1377 enforceSystemOrSystemUI("INotificationManager.getAutomaticZenModeConditions"); 1378 return mConditionProviders.getAutomaticZenModeConditions(); 1379 } 1380 1381 private void enforceSystemOrSystemUI(String message) { 1382 if (isCallerSystem()) return; 1383 getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE, 1384 message); 1385 } 1386 1387 @Override 1388 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1389 if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 1390 != PackageManager.PERMISSION_GRANTED) { 1391 pw.println("Permission Denial: can't dump NotificationManager from from pid=" 1392 + Binder.getCallingPid() 1393 + ", uid=" + Binder.getCallingUid()); 1394 return; 1395 } 1396 1397 dumpImpl(pw); 1398 } 1399 }; 1400 1401 private String[] getActiveNotificationKeysFromListener(INotificationListener token) { 1402 synchronized (mNotificationList) { 1403 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1404 final ArrayList<String> keys = new ArrayList<String>(); 1405 final int N = mNotificationList.size(); 1406 for (int i=0; i<N; i++) { 1407 final StatusBarNotification sbn = mNotificationList.get(i).sbn; 1408 if (info.enabledAndUserMatches(sbn.getUserId())) { 1409 keys.add(sbn.getKey()); 1410 } 1411 } 1412 return keys.toArray(new String[keys.size()]); 1413 } 1414 } 1415 1416 void dumpImpl(PrintWriter pw) { 1417 pw.println("Current Notification Manager state:"); 1418 1419 int N; 1420 1421 synchronized (mToastQueue) { 1422 N = mToastQueue.size(); 1423 if (N > 0) { 1424 pw.println(" Toast Queue:"); 1425 for (int i=0; i<N; i++) { 1426 mToastQueue.get(i).dump(pw, " "); 1427 } 1428 pw.println(" "); 1429 } 1430 1431 } 1432 1433 synchronized (mNotificationList) { 1434 N = mNotificationList.size(); 1435 if (N > 0) { 1436 pw.println(" Notification List:"); 1437 for (int i=0; i<N; i++) { 1438 mNotificationList.get(i).dump(pw, " ", getContext()); 1439 } 1440 pw.println(" "); 1441 } 1442 1443 N = mLights.size(); 1444 if (N > 0) { 1445 pw.println(" Lights List:"); 1446 for (int i=0; i<N; i++) { 1447 pw.println(" " + mLights.get(i)); 1448 } 1449 pw.println(" "); 1450 } 1451 1452 pw.println(" mSoundNotification=" + mSoundNotification); 1453 pw.println(" mVibrateNotification=" + mVibrateNotification); 1454 pw.println(" mDisableNotificationAlerts=" + mDisableNotificationAlerts); 1455 pw.println(" mSystemReady=" + mSystemReady); 1456 pw.println(" mArchive=" + mArchive.toString()); 1457 Iterator<StatusBarNotification> iter = mArchive.descendingIterator(); 1458 int i=0; 1459 while (iter.hasNext()) { 1460 pw.println(" " + iter.next()); 1461 if (++i >= 5) { 1462 if (iter.hasNext()) pw.println(" ..."); 1463 break; 1464 } 1465 } 1466 1467 pw.println("\n Usage Stats:"); 1468 mUsageStats.dump(pw, " "); 1469 1470 pw.println("\n Zen Mode:"); 1471 mZenModeHelper.dump(pw, " "); 1472 1473 pw.println("\n Notification listeners:"); 1474 mListeners.dump(pw); 1475 1476 pw.println("\n Condition providers:"); 1477 mConditionProviders.dump(pw); 1478 } 1479 } 1480 1481 /** 1482 * The private API only accessible to the system process. 1483 */ 1484 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() { 1485 @Override 1486 public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid, 1487 String tag, int id, Notification notification, int[] idReceived, int userId) { 1488 enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification, 1489 idReceived, userId); 1490 } 1491 }; 1492 1493 void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid, 1494 final int callingPid, final String tag, final int id, final Notification notification, 1495 int[] idOut, int incomingUserId) { 1496 if (DBG) { 1497 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id 1498 + " notification=" + notification); 1499 } 1500 checkCallerIsSystemOrSameApp(pkg); 1501 final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg)); 1502 1503 final int userId = ActivityManager.handleIncomingUser(callingPid, 1504 callingUid, incomingUserId, true, false, "enqueueNotification", pkg); 1505 final UserHandle user = new UserHandle(userId); 1506 1507 // Limit the number of notifications that any given package except the android 1508 // package can enqueue. Prevents DOS attacks and deals with leaks. 1509 if (!isSystemNotification) { 1510 synchronized (mNotificationList) { 1511 int count = 0; 1512 final int N = mNotificationList.size(); 1513 for (int i=0; i<N; i++) { 1514 final NotificationRecord r = mNotificationList.get(i); 1515 if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) { 1516 count++; 1517 if (count >= MAX_PACKAGE_NOTIFICATIONS) { 1518 Slog.e(TAG, "Package has already posted " + count 1519 + " notifications. Not showing more. package=" + pkg); 1520 return; 1521 } 1522 } 1523 } 1524 } 1525 } 1526 1527 // This conditional is a dirty hack to limit the logging done on 1528 // behalf of the download manager without affecting other apps. 1529 if (!pkg.equals("com.android.providers.downloads") 1530 || Log.isLoggable("DownloadManager", Log.VERBOSE)) { 1531 EventLogTags.writeNotificationEnqueue(callingUid, callingPid, 1532 pkg, id, tag, userId, notification.toString()); 1533 } 1534 1535 if (pkg == null || notification == null) { 1536 throw new IllegalArgumentException("null not allowed: pkg=" + pkg 1537 + " id=" + id + " notification=" + notification); 1538 } 1539 if (notification.icon != 0) { 1540 if (notification.contentView == null) { 1541 throw new IllegalArgumentException("contentView required: pkg=" + pkg 1542 + " id=" + id + " notification=" + notification); 1543 } 1544 } 1545 1546 mHandler.post(new Runnable() { 1547 @Override 1548 public void run() { 1549 1550 // === Scoring === 1551 1552 // 0. Sanitize inputs 1553 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN, 1554 Notification.PRIORITY_MAX); 1555 // Migrate notification flags to scores 1556 if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) { 1557 if (notification.priority < Notification.PRIORITY_MAX) { 1558 notification.priority = Notification.PRIORITY_MAX; 1559 } 1560 } else if (SCORE_ONGOING_HIGHER && 1561 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) { 1562 if (notification.priority < Notification.PRIORITY_HIGH) { 1563 notification.priority = Notification.PRIORITY_HIGH; 1564 } 1565 } 1566 1567 // 1. initial score: buckets of 10, around the app 1568 int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20] 1569 1570 // 2. Consult external heuristics (TBD) 1571 1572 // 3. Apply local rules 1573 1574 int initialScore = score; 1575 if (!mScorers.isEmpty()) { 1576 if (DBG) Slog.v(TAG, "Initial score is " + score + "."); 1577 for (NotificationScorer scorer : mScorers) { 1578 try { 1579 score = scorer.getScore(notification, score); 1580 } catch (Throwable t) { 1581 Slog.w(TAG, "Scorer threw on .getScore.", t); 1582 } 1583 } 1584 if (DBG) Slog.v(TAG, "Final score is " + score + "."); 1585 } 1586 1587 // add extra to indicate score modified by NotificationScorer 1588 notification.extras.putBoolean(Notification.EXTRA_SCORE_MODIFIED, 1589 score != initialScore); 1590 1591 // blocked apps 1592 if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) { 1593 if (!isSystemNotification) { 1594 score = JUNK_SCORE; 1595 Slog.e(TAG, "Suppressing notification from package " + pkg 1596 + " by user request."); 1597 } 1598 } 1599 1600 if (DBG) { 1601 Slog.v(TAG, "Assigned score=" + score + " to " + notification); 1602 } 1603 1604 if (score < SCORE_DISPLAY_THRESHOLD) { 1605 // Notification will be blocked because the score is too low. 1606 return; 1607 } 1608 1609 // Is this notification intercepted by zen mode? 1610 final boolean intercept = mZenModeHelper.shouldIntercept(pkg, notification); 1611 notification.extras.putBoolean(EXTRA_INTERCEPT, intercept); 1612 1613 // Should this notification make noise, vibe, or use the LED? 1614 final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD) && !intercept; 1615 if (DBG || intercept) Slog.v(TAG, 1616 "pkg=" + pkg + " canInterrupt=" + canInterrupt + " intercept=" + intercept); 1617 synchronized (mNotificationList) { 1618 final StatusBarNotification n = new StatusBarNotification( 1619 pkg, opPkg, id, tag, callingUid, callingPid, score, notification, 1620 user); 1621 NotificationRecord r = new NotificationRecord(n); 1622 NotificationRecord old = null; 1623 1624 int index = indexOfNotificationLocked(pkg, tag, id, userId); 1625 if (index < 0) { 1626 mNotificationList.add(r); 1627 mUsageStats.registerPostedByApp(r); 1628 } else { 1629 old = mNotificationList.get(index); 1630 mNotificationList.set(index, r); 1631 mUsageStats.registerUpdatedByApp(r); 1632 // Make sure we don't lose the foreground service state. 1633 if (old != null) { 1634 notification.flags |= 1635 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE; 1636 } 1637 } 1638 if (old != null) { 1639 mNotificationsByKey.remove(old.sbn.getKey()); 1640 } 1641 mNotificationsByKey.put(n.getKey(), r); 1642 1643 // Ensure if this is a foreground service that the proper additional 1644 // flags are set. 1645 if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) { 1646 notification.flags |= Notification.FLAG_ONGOING_EVENT 1647 | Notification.FLAG_NO_CLEAR; 1648 } 1649 1650 final int currentUser; 1651 final long token = Binder.clearCallingIdentity(); 1652 try { 1653 currentUser = ActivityManager.getCurrentUser(); 1654 } finally { 1655 Binder.restoreCallingIdentity(token); 1656 } 1657 1658 if (notification.icon != 0) { 1659 if (old != null && old.statusBarKey != null) { 1660 r.statusBarKey = old.statusBarKey; 1661 final long identity = Binder.clearCallingIdentity(); 1662 try { 1663 mStatusBar.updateNotification(r.statusBarKey, n); 1664 } finally { 1665 Binder.restoreCallingIdentity(identity); 1666 } 1667 } else { 1668 final long identity = Binder.clearCallingIdentity(); 1669 try { 1670 r.statusBarKey = mStatusBar.addNotification(n); 1671 if ((n.getNotification().flags & Notification.FLAG_SHOW_LIGHTS) != 0 1672 && canInterrupt) { 1673 mAttentionLight.pulse(); 1674 } 1675 } finally { 1676 Binder.restoreCallingIdentity(identity); 1677 } 1678 } 1679 // Send accessibility events only for the current user. 1680 if (currentUser == userId) { 1681 sendAccessibilityEvent(notification, pkg); 1682 } 1683 1684 mListeners.notifyPostedLocked(r.sbn); 1685 } else { 1686 Slog.e(TAG, "Not posting notification with icon==0: " + notification); 1687 if (old != null && old.statusBarKey != null) { 1688 final long identity = Binder.clearCallingIdentity(); 1689 try { 1690 mStatusBar.removeNotification(old.statusBarKey); 1691 } finally { 1692 Binder.restoreCallingIdentity(identity); 1693 } 1694 1695 mListeners.notifyRemovedLocked(r.sbn); 1696 } 1697 // ATTENTION: in a future release we will bail out here 1698 // so that we do not play sounds, show lights, etc. for invalid 1699 // notifications 1700 Slog.e(TAG, "WARNING: In a future release this will crash the app: " 1701 + n.getPackageName()); 1702 } 1703 1704 // If we're not supposed to beep, vibrate, etc. then don't. 1705 if (!mDisableNotificationAlerts 1706 && (!(old != null 1707 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 )) 1708 && (r.getUserId() == UserHandle.USER_ALL || 1709 (r.getUserId() == userId && r.getUserId() == currentUser) || 1710 mUserProfiles.isCurrentProfile(r.getUserId())) 1711 && canInterrupt 1712 && mSystemReady 1713 && mAudioManager != null) { 1714 if (DBG) Slog.v(TAG, "Interrupting!"); 1715 // sound 1716 1717 // should we use the default notification sound? (indicated either by 1718 // DEFAULT_SOUND or because notification.sound is pointing at 1719 // Settings.System.NOTIFICATION_SOUND) 1720 final boolean useDefaultSound = 1721 (notification.defaults & Notification.DEFAULT_SOUND) != 0 || 1722 Settings.System.DEFAULT_NOTIFICATION_URI 1723 .equals(notification.sound); 1724 1725 Uri soundUri = null; 1726 boolean hasValidSound = false; 1727 1728 if (useDefaultSound) { 1729 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI; 1730 1731 // check to see if the default notification sound is silent 1732 ContentResolver resolver = getContext().getContentResolver(); 1733 hasValidSound = Settings.System.getString(resolver, 1734 Settings.System.NOTIFICATION_SOUND) != null; 1735 } else if (notification.sound != null) { 1736 soundUri = notification.sound; 1737 hasValidSound = (soundUri != null); 1738 } 1739 1740 if (hasValidSound) { 1741 boolean looping = 1742 (notification.flags & Notification.FLAG_INSISTENT) != 0; 1743 int audioStreamType; 1744 if (notification.audioStreamType >= 0) { 1745 audioStreamType = notification.audioStreamType; 1746 } else { 1747 audioStreamType = DEFAULT_STREAM_TYPE; 1748 } 1749 mSoundNotification = r; 1750 // do not play notifications if stream volume is 0 (typically because 1751 // ringer mode is silent) or if there is a user of exclusive audio focus 1752 if ((mAudioManager.getStreamVolume(audioStreamType) != 0) 1753 && !mAudioManager.isAudioFocusExclusive()) { 1754 final long identity = Binder.clearCallingIdentity(); 1755 try { 1756 final IRingtonePlayer player = 1757 mAudioManager.getRingtonePlayer(); 1758 if (player != null) { 1759 if (DBG) Slog.v(TAG, "Playing sound " + soundUri 1760 + " on stream " + audioStreamType); 1761 player.playAsync(soundUri, user, looping, audioStreamType); 1762 } 1763 } catch (RemoteException e) { 1764 } finally { 1765 Binder.restoreCallingIdentity(identity); 1766 } 1767 } 1768 } 1769 1770 // vibrate 1771 // Does the notification want to specify its own vibration? 1772 final boolean hasCustomVibrate = notification.vibrate != null; 1773 1774 // new in 4.2: if there was supposed to be a sound and we're in vibrate 1775 // mode, and no other vibration is specified, we fall back to vibration 1776 final boolean convertSoundToVibration = 1777 !hasCustomVibrate 1778 && hasValidSound 1779 && (mAudioManager.getRingerMode() 1780 == AudioManager.RINGER_MODE_VIBRATE); 1781 1782 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback. 1783 final boolean useDefaultVibrate = 1784 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; 1785 1786 if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate) 1787 && !(mAudioManager.getRingerMode() 1788 == AudioManager.RINGER_MODE_SILENT)) { 1789 mVibrateNotification = r; 1790 1791 if (useDefaultVibrate || convertSoundToVibration) { 1792 // Escalate privileges so we can use the vibrator even if the 1793 // notifying app does not have the VIBRATE permission. 1794 long identity = Binder.clearCallingIdentity(); 1795 try { 1796 mVibrator.vibrate(r.sbn.getUid(), r.sbn.getOpPkg(), 1797 useDefaultVibrate ? mDefaultVibrationPattern 1798 : mFallbackVibrationPattern, 1799 ((notification.flags & Notification.FLAG_INSISTENT) != 0) 1800 ? 0: -1, notification.audioStreamType); 1801 } finally { 1802 Binder.restoreCallingIdentity(identity); 1803 } 1804 } else if (notification.vibrate.length > 1) { 1805 // If you want your own vibration pattern, you need the VIBRATE 1806 // permission 1807 mVibrator.vibrate(r.sbn.getUid(), r.sbn.getOpPkg(), 1808 notification.vibrate, 1809 ((notification.flags & Notification.FLAG_INSISTENT) != 0) 1810 ? 0: -1, notification.audioStreamType); 1811 } 1812 } 1813 } 1814 1815 // light 1816 // the most recent thing gets the light 1817 mLights.remove(old); 1818 if (mLedNotification == old) { 1819 mLedNotification = null; 1820 } 1821 //Slog.i(TAG, "notification.lights=" 1822 // + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) 1823 // != 0)); 1824 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 1825 && canInterrupt) { 1826 mLights.add(r); 1827 updateLightsLocked(); 1828 } else { 1829 if (old != null 1830 && ((old.getFlags() & Notification.FLAG_SHOW_LIGHTS) != 0)) { 1831 updateLightsLocked(); 1832 } 1833 } 1834 } 1835 } 1836 }); 1837 1838 idOut[0] = id; 1839 } 1840 1841 void showNextToastLocked() { 1842 ToastRecord record = mToastQueue.get(0); 1843 while (record != null) { 1844 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback); 1845 try { 1846 record.callback.show(); 1847 scheduleTimeoutLocked(record); 1848 return; 1849 } catch (RemoteException e) { 1850 Slog.w(TAG, "Object died trying to show notification " + record.callback 1851 + " in package " + record.pkg); 1852 // remove it from the list and let the process die 1853 int index = mToastQueue.indexOf(record); 1854 if (index >= 0) { 1855 mToastQueue.remove(index); 1856 } 1857 keepProcessAliveLocked(record.pid); 1858 if (mToastQueue.size() > 0) { 1859 record = mToastQueue.get(0); 1860 } else { 1861 record = null; 1862 } 1863 } 1864 } 1865 } 1866 1867 void cancelToastLocked(int index) { 1868 ToastRecord record = mToastQueue.get(index); 1869 try { 1870 record.callback.hide(); 1871 } catch (RemoteException e) { 1872 Slog.w(TAG, "Object died trying to hide notification " + record.callback 1873 + " in package " + record.pkg); 1874 // don't worry about this, we're about to remove it from 1875 // the list anyway 1876 } 1877 mToastQueue.remove(index); 1878 keepProcessAliveLocked(record.pid); 1879 if (mToastQueue.size() > 0) { 1880 // Show the next one. If the callback fails, this will remove 1881 // it from the list, so don't assume that the list hasn't changed 1882 // after this point. 1883 showNextToastLocked(); 1884 } 1885 } 1886 1887 private void scheduleTimeoutLocked(ToastRecord r) 1888 { 1889 mHandler.removeCallbacksAndMessages(r); 1890 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r); 1891 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY; 1892 mHandler.sendMessageDelayed(m, delay); 1893 } 1894 1895 private void handleTimeout(ToastRecord record) 1896 { 1897 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback); 1898 synchronized (mToastQueue) { 1899 int index = indexOfToastLocked(record.pkg, record.callback); 1900 if (index >= 0) { 1901 cancelToastLocked(index); 1902 } 1903 } 1904 } 1905 1906 // lock on mToastQueue 1907 int indexOfToastLocked(String pkg, ITransientNotification callback) 1908 { 1909 IBinder cbak = callback.asBinder(); 1910 ArrayList<ToastRecord> list = mToastQueue; 1911 int len = list.size(); 1912 for (int i=0; i<len; i++) { 1913 ToastRecord r = list.get(i); 1914 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) { 1915 return i; 1916 } 1917 } 1918 return -1; 1919 } 1920 1921 // lock on mToastQueue 1922 void keepProcessAliveLocked(int pid) 1923 { 1924 int toastCount = 0; // toasts from this pid 1925 ArrayList<ToastRecord> list = mToastQueue; 1926 int N = list.size(); 1927 for (int i=0; i<N; i++) { 1928 ToastRecord r = list.get(i); 1929 if (r.pid == pid) { 1930 toastCount++; 1931 } 1932 } 1933 try { 1934 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0); 1935 } catch (RemoteException e) { 1936 // Shouldn't happen. 1937 } 1938 } 1939 1940 private final class WorkerHandler extends Handler 1941 { 1942 @Override 1943 public void handleMessage(Message msg) 1944 { 1945 switch (msg.what) 1946 { 1947 case MESSAGE_TIMEOUT: 1948 handleTimeout((ToastRecord)msg.obj); 1949 break; 1950 case MESSAGE_SAVE_POLICY_FILE: 1951 handleSavePolicyFile(); 1952 break; 1953 } 1954 } 1955 } 1956 1957 1958 // Notifications 1959 // ============================================================================ 1960 static int clamp(int x, int low, int high) { 1961 return (x < low) ? low : ((x > high) ? high : x); 1962 } 1963 1964 void sendAccessibilityEvent(Notification notification, CharSequence packageName) { 1965 AccessibilityManager manager = AccessibilityManager.getInstance(getContext()); 1966 if (!manager.isEnabled()) { 1967 return; 1968 } 1969 1970 AccessibilityEvent event = 1971 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); 1972 event.setPackageName(packageName); 1973 event.setClassName(Notification.class.getName()); 1974 event.setParcelableData(notification); 1975 CharSequence tickerText = notification.tickerText; 1976 if (!TextUtils.isEmpty(tickerText)) { 1977 event.getText().add(tickerText); 1978 } 1979 1980 manager.sendAccessibilityEvent(event); 1981 } 1982 1983 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) { 1984 // tell the app 1985 if (sendDelete) { 1986 if (r.getNotification().deleteIntent != null) { 1987 try { 1988 r.getNotification().deleteIntent.send(); 1989 } catch (PendingIntent.CanceledException ex) { 1990 // do nothing - there's no relevant way to recover, and 1991 // no reason to let this propagate 1992 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex); 1993 } 1994 } 1995 } 1996 1997 // status bar 1998 if (r.getNotification().icon != 0) { 1999 final long identity = Binder.clearCallingIdentity(); 2000 try { 2001 mStatusBar.removeNotification(r.statusBarKey); 2002 } finally { 2003 Binder.restoreCallingIdentity(identity); 2004 } 2005 r.statusBarKey = null; 2006 mListeners.notifyRemovedLocked(r.sbn); 2007 } 2008 2009 // sound 2010 if (mSoundNotification == r) { 2011 mSoundNotification = null; 2012 final long identity = Binder.clearCallingIdentity(); 2013 try { 2014 final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); 2015 if (player != null) { 2016 player.stopAsync(); 2017 } 2018 } catch (RemoteException e) { 2019 } finally { 2020 Binder.restoreCallingIdentity(identity); 2021 } 2022 } 2023 2024 // vibrate 2025 if (mVibrateNotification == r) { 2026 mVibrateNotification = null; 2027 long identity = Binder.clearCallingIdentity(); 2028 try { 2029 mVibrator.cancel(); 2030 } 2031 finally { 2032 Binder.restoreCallingIdentity(identity); 2033 } 2034 } 2035 2036 // light 2037 mLights.remove(r); 2038 if (mLedNotification == r) { 2039 mLedNotification = null; 2040 } 2041 2042 // Record usage stats 2043 switch (reason) { 2044 case REASON_DELEGATE_CANCEL: 2045 case REASON_DELEGATE_CANCEL_ALL: 2046 case REASON_LISTENER_CANCEL: 2047 case REASON_LISTENER_CANCEL_ALL: 2048 mUsageStats.registerDismissedByUser(r); 2049 break; 2050 case REASON_NOMAN_CANCEL: 2051 case REASON_NOMAN_CANCEL_ALL: 2052 mUsageStats.registerRemovedByApp(r); 2053 break; 2054 case REASON_DELEGATE_CLICK: 2055 mUsageStats.registerCancelDueToClick(r); 2056 break; 2057 default: 2058 mUsageStats.registerCancelUnknown(r); 2059 break; 2060 } 2061 2062 // Save it for users of getHistoricalNotifications() 2063 mArchive.record(r.sbn); 2064 } 2065 2066 /** 2067 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags} 2068 * and none of the {@code mustNotHaveFlags}. 2069 */ 2070 void cancelNotification(final int callingUid, final int callingPid, 2071 final String pkg, final String tag, final int id, 2072 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete, 2073 final int userId, final int reason, final ManagedServiceInfo listener) { 2074 // In enqueueNotificationInternal notifications are added by scheduling the 2075 // work on the worker handler. Hence, we also schedule the cancel on this 2076 // handler to avoid a scenario where an add notification call followed by a 2077 // remove notification call ends up in not removing the notification. 2078 mHandler.post(new Runnable() { 2079 @Override 2080 public void run() { 2081 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId, 2082 mustHaveFlags, mustNotHaveFlags, reason, 2083 listener == null ? null : listener.component.toShortString()); 2084 2085 synchronized (mNotificationList) { 2086 int index = indexOfNotificationLocked(pkg, tag, id, userId); 2087 if (index >= 0) { 2088 NotificationRecord r = mNotificationList.get(index); 2089 2090 // Ideally we'd do this in the caller of this method. However, that would 2091 // require the caller to also find the notification. 2092 if (reason == REASON_DELEGATE_CLICK) { 2093 mUsageStats.registerClickedByUser(r); 2094 } 2095 2096 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) { 2097 return; 2098 } 2099 if ((r.getNotification().flags & mustNotHaveFlags) != 0) { 2100 return; 2101 } 2102 2103 mNotificationList.remove(index); 2104 mNotificationsByKey.remove(r.sbn.getKey()); 2105 2106 cancelNotificationLocked(r, sendDelete, reason); 2107 updateLightsLocked(); 2108 } 2109 } 2110 } 2111 }); 2112 } 2113 2114 /** 2115 * Determine whether the userId applies to the notification in question, either because 2116 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard). 2117 */ 2118 private boolean notificationMatchesUserId(NotificationRecord r, int userId) { 2119 return 2120 // looking for USER_ALL notifications? match everything 2121 userId == UserHandle.USER_ALL 2122 // a notification sent to USER_ALL matches any query 2123 || r.getUserId() == UserHandle.USER_ALL 2124 // an exact user match 2125 || r.getUserId() == userId; 2126 } 2127 2128 /** 2129 * Determine whether the userId applies to the notification in question, either because 2130 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or 2131 * because it matches one of the users profiles. 2132 */ 2133 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) { 2134 return notificationMatchesUserId(r, userId) 2135 || mUserProfiles.isCurrentProfile(r.getUserId()); 2136 } 2137 2138 /** 2139 * Cancels all notifications from a given package that have all of the 2140 * {@code mustHaveFlags}. 2141 */ 2142 boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags, 2143 int mustNotHaveFlags, boolean doit, int userId, int reason, 2144 ManagedServiceInfo listener) { 2145 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2146 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason, 2147 listener == null ? null : listener.component.toShortString()); 2148 2149 synchronized (mNotificationList) { 2150 final int N = mNotificationList.size(); 2151 boolean canceledSomething = false; 2152 for (int i = N-1; i >= 0; --i) { 2153 NotificationRecord r = mNotificationList.get(i); 2154 if (!notificationMatchesUserId(r, userId)) { 2155 continue; 2156 } 2157 // Don't remove notifications to all, if there's no package name specified 2158 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) { 2159 continue; 2160 } 2161 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) { 2162 continue; 2163 } 2164 if ((r.getFlags() & mustNotHaveFlags) != 0) { 2165 continue; 2166 } 2167 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) { 2168 continue; 2169 } 2170 canceledSomething = true; 2171 if (!doit) { 2172 return true; 2173 } 2174 mNotificationList.remove(i); 2175 mNotificationsByKey.remove(r.sbn.getKey()); 2176 cancelNotificationLocked(r, false, reason); 2177 } 2178 if (canceledSomething) { 2179 updateLightsLocked(); 2180 } 2181 return canceledSomething; 2182 } 2183 } 2184 2185 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason, 2186 ManagedServiceInfo listener, boolean includeCurrentProfiles) { 2187 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2188 null, userId, 0, 0, reason, 2189 listener == null ? null : listener.component.toShortString()); 2190 2191 final int N = mNotificationList.size(); 2192 for (int i=N-1; i>=0; i--) { 2193 NotificationRecord r = mNotificationList.get(i); 2194 if (includeCurrentProfiles) { 2195 if (!notificationMatchesCurrentProfiles(r, userId)) { 2196 continue; 2197 } 2198 } else { 2199 if (!notificationMatchesUserId(r, userId)) { 2200 continue; 2201 } 2202 } 2203 2204 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT 2205 | Notification.FLAG_NO_CLEAR)) == 0) { 2206 mNotificationList.remove(i); 2207 mNotificationsByKey.remove(r.sbn.getKey()); 2208 cancelNotificationLocked(r, true, reason); 2209 } 2210 } 2211 updateLightsLocked(); 2212 } 2213 2214 // lock on mNotificationList 2215 void updateLightsLocked() 2216 { 2217 // handle notification lights 2218 if (mLedNotification == null) { 2219 // get next notification, if any 2220 int n = mLights.size(); 2221 if (n > 0) { 2222 mLedNotification = mLights.get(n-1); 2223 } 2224 } 2225 2226 // Don't flash while we are in a call or screen is on 2227 if (mLedNotification == null || mInCall || mScreenOn) { 2228 mNotificationLight.turnOff(); 2229 } else { 2230 final Notification ledno = mLedNotification.sbn.getNotification(); 2231 int ledARGB = ledno.ledARGB; 2232 int ledOnMS = ledno.ledOnMS; 2233 int ledOffMS = ledno.ledOffMS; 2234 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) { 2235 ledARGB = mDefaultNotificationColor; 2236 ledOnMS = mDefaultNotificationLedOn; 2237 ledOffMS = mDefaultNotificationLedOff; 2238 } 2239 if (mNotificationPulseEnabled) { 2240 // pulse repeatedly 2241 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED, 2242 ledOnMS, ledOffMS); 2243 } 2244 } 2245 } 2246 2247 // lock on mNotificationList 2248 int indexOfNotificationLocked(String pkg, String tag, int id, int userId) 2249 { 2250 ArrayList<NotificationRecord> list = mNotificationList; 2251 final int len = list.size(); 2252 for (int i=0; i<len; i++) { 2253 NotificationRecord r = list.get(i); 2254 if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) { 2255 continue; 2256 } 2257 if (tag == null) { 2258 if (r.sbn.getTag() != null) { 2259 continue; 2260 } 2261 } else { 2262 if (!tag.equals(r.sbn.getTag())) { 2263 continue; 2264 } 2265 } 2266 if (r.sbn.getPackageName().equals(pkg)) { 2267 return i; 2268 } 2269 } 2270 return -1; 2271 } 2272 2273 private void updateNotificationPulse() { 2274 synchronized (mNotificationList) { 2275 updateLightsLocked(); 2276 } 2277 } 2278 2279 private static boolean isUidSystem(int uid) { 2280 final int appid = UserHandle.getAppId(uid); 2281 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0); 2282 } 2283 2284 private static boolean isCallerSystem() { 2285 return isUidSystem(Binder.getCallingUid()); 2286 } 2287 2288 private static void checkCallerIsSystem() { 2289 if (isCallerSystem()) { 2290 return; 2291 } 2292 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid()); 2293 } 2294 2295 private static void checkCallerIsSystemOrSameApp(String pkg) { 2296 if (isCallerSystem()) { 2297 return; 2298 } 2299 final int uid = Binder.getCallingUid(); 2300 try { 2301 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo( 2302 pkg, 0, UserHandle.getCallingUserId()); 2303 if (!UserHandle.isSameApp(ai.uid, uid)) { 2304 throw new SecurityException("Calling uid " + uid + " gave package" 2305 + pkg + " which is owned by uid " + ai.uid); 2306 } 2307 } catch (RemoteException re) { 2308 throw new SecurityException("Unknown package " + pkg + "\n" + re); 2309 } 2310 } 2311 2312 public class NotificationListeners extends ManagedServices { 2313 2314 public NotificationListeners() { 2315 super(getContext(), mHandler, mNotificationList, mUserProfiles); 2316 } 2317 2318 @Override 2319 protected Config getConfig() { 2320 Config c = new Config(); 2321 c.caption = "notification listener"; 2322 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE; 2323 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS; 2324 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE; 2325 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS; 2326 c.clientLabel = R.string.notification_listener_binding_label; 2327 return c; 2328 } 2329 2330 @Override 2331 protected IInterface asInterface(IBinder binder) { 2332 return INotificationListener.Stub.asInterface(binder); 2333 } 2334 2335 @Override 2336 public void onServiceAdded(ManagedServiceInfo info) { 2337 final INotificationListener listener = (INotificationListener) info.service; 2338 final String[] keys = getActiveNotificationKeysFromListener(listener); 2339 try { 2340 listener.onListenerConnected(keys); 2341 } catch (RemoteException e) { 2342 // we tried 2343 } 2344 } 2345 2346 /** 2347 * asynchronously notify all listeners about a new notification 2348 */ 2349 public void notifyPostedLocked(StatusBarNotification sbn) { 2350 // make a copy in case changes are made to the underlying Notification object 2351 final StatusBarNotification sbnClone = sbn.clone(); 2352 for (final ManagedServiceInfo info : mServices) { 2353 mHandler.post(new Runnable() { 2354 @Override 2355 public void run() { 2356 notifyPostedIfUserMatch(info, sbnClone); 2357 } 2358 }); 2359 } 2360 } 2361 2362 /** 2363 * asynchronously notify all listeners about a removed notification 2364 */ 2365 public void notifyRemovedLocked(StatusBarNotification sbn) { 2366 // make a copy in case changes are made to the underlying Notification object 2367 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the 2368 // notification 2369 final StatusBarNotification sbnLight = sbn.cloneLight(); 2370 for (ManagedServiceInfo serviceInfo : mServices) { 2371 final ManagedServiceInfo info = (ManagedServiceInfo) serviceInfo; 2372 mHandler.post(new Runnable() { 2373 @Override 2374 public void run() { 2375 notifyRemovedIfUserMatch(info, sbnLight); 2376 } 2377 }); 2378 } 2379 } 2380 2381 private void notifyPostedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn) { 2382 if (!info.enabledAndUserMatches(sbn.getUserId())) { 2383 return; 2384 } 2385 final INotificationListener listener = (INotificationListener)info.service; 2386 try { 2387 listener.onNotificationPosted(sbn); 2388 } catch (RemoteException ex) { 2389 Log.e(TAG, "unable to notify listener (posted): " + listener, ex); 2390 } 2391 } 2392 2393 private void notifyRemovedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn) { 2394 if (!info.enabledAndUserMatches(sbn.getUserId())) { 2395 return; 2396 } 2397 final INotificationListener listener = (INotificationListener)info.service; 2398 try { 2399 listener.onNotificationRemoved(sbn); 2400 } catch (RemoteException ex) { 2401 Log.e(TAG, "unable to notify listener (removed): " + listener, ex); 2402 } 2403 } 2404 } 2405} 2406