NotificationManagerService.java revision 3b98b3f1f85aff0c84ebef4dd497c146d1b4d248
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) Slog.v(TAG, "canInterrupt=" + canInterrupt + " intercept=" + intercept); 1616 synchronized (mNotificationList) { 1617 final StatusBarNotification n = new StatusBarNotification( 1618 pkg, opPkg, id, tag, callingUid, callingPid, score, notification, 1619 user); 1620 NotificationRecord r = new NotificationRecord(n); 1621 NotificationRecord old = null; 1622 1623 int index = indexOfNotificationLocked(pkg, tag, id, userId); 1624 if (index < 0) { 1625 mNotificationList.add(r); 1626 mUsageStats.registerPostedByApp(r); 1627 } else { 1628 old = mNotificationList.get(index); 1629 mNotificationList.set(index, r); 1630 mUsageStats.registerUpdatedByApp(r); 1631 // Make sure we don't lose the foreground service state. 1632 if (old != null) { 1633 notification.flags |= 1634 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE; 1635 } 1636 } 1637 if (old != null) { 1638 mNotificationsByKey.remove(old.sbn.getKey()); 1639 } 1640 mNotificationsByKey.put(n.getKey(), r); 1641 1642 // Ensure if this is a foreground service that the proper additional 1643 // flags are set. 1644 if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) { 1645 notification.flags |= Notification.FLAG_ONGOING_EVENT 1646 | Notification.FLAG_NO_CLEAR; 1647 } 1648 1649 final int currentUser; 1650 final long token = Binder.clearCallingIdentity(); 1651 try { 1652 currentUser = ActivityManager.getCurrentUser(); 1653 } finally { 1654 Binder.restoreCallingIdentity(token); 1655 } 1656 1657 if (notification.icon != 0) { 1658 if (old != null && old.statusBarKey != null) { 1659 r.statusBarKey = old.statusBarKey; 1660 final long identity = Binder.clearCallingIdentity(); 1661 try { 1662 mStatusBar.updateNotification(r.statusBarKey, n); 1663 } finally { 1664 Binder.restoreCallingIdentity(identity); 1665 } 1666 } else { 1667 final long identity = Binder.clearCallingIdentity(); 1668 try { 1669 r.statusBarKey = mStatusBar.addNotification(n); 1670 if ((n.getNotification().flags & Notification.FLAG_SHOW_LIGHTS) != 0 1671 && canInterrupt) { 1672 mAttentionLight.pulse(); 1673 } 1674 } finally { 1675 Binder.restoreCallingIdentity(identity); 1676 } 1677 } 1678 // Send accessibility events only for the current user. 1679 if (currentUser == userId) { 1680 sendAccessibilityEvent(notification, pkg); 1681 } 1682 1683 mListeners.notifyPostedLocked(r.sbn); 1684 } else { 1685 Slog.e(TAG, "Not posting notification with icon==0: " + notification); 1686 if (old != null && old.statusBarKey != null) { 1687 final long identity = Binder.clearCallingIdentity(); 1688 try { 1689 mStatusBar.removeNotification(old.statusBarKey); 1690 } finally { 1691 Binder.restoreCallingIdentity(identity); 1692 } 1693 1694 mListeners.notifyRemovedLocked(r.sbn); 1695 } 1696 // ATTENTION: in a future release we will bail out here 1697 // so that we do not play sounds, show lights, etc. for invalid 1698 // notifications 1699 Slog.e(TAG, "WARNING: In a future release this will crash the app: " 1700 + n.getPackageName()); 1701 } 1702 1703 // If we're not supposed to beep, vibrate, etc. then don't. 1704 if (!mDisableNotificationAlerts 1705 && (!(old != null 1706 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 )) 1707 && (r.getUserId() == UserHandle.USER_ALL || 1708 (r.getUserId() == userId && r.getUserId() == currentUser) || 1709 mUserProfiles.isCurrentProfile(r.getUserId())) 1710 && canInterrupt 1711 && mSystemReady 1712 && mAudioManager != null) { 1713 if (DBG) Slog.v(TAG, "Interrupting!"); 1714 // sound 1715 1716 // should we use the default notification sound? (indicated either by 1717 // DEFAULT_SOUND or because notification.sound is pointing at 1718 // Settings.System.NOTIFICATION_SOUND) 1719 final boolean useDefaultSound = 1720 (notification.defaults & Notification.DEFAULT_SOUND) != 0 || 1721 Settings.System.DEFAULT_NOTIFICATION_URI 1722 .equals(notification.sound); 1723 1724 Uri soundUri = null; 1725 boolean hasValidSound = false; 1726 1727 if (useDefaultSound) { 1728 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI; 1729 1730 // check to see if the default notification sound is silent 1731 ContentResolver resolver = getContext().getContentResolver(); 1732 hasValidSound = Settings.System.getString(resolver, 1733 Settings.System.NOTIFICATION_SOUND) != null; 1734 } else if (notification.sound != null) { 1735 soundUri = notification.sound; 1736 hasValidSound = (soundUri != null); 1737 } 1738 1739 if (hasValidSound) { 1740 boolean looping = 1741 (notification.flags & Notification.FLAG_INSISTENT) != 0; 1742 int audioStreamType; 1743 if (notification.audioStreamType >= 0) { 1744 audioStreamType = notification.audioStreamType; 1745 } else { 1746 audioStreamType = DEFAULT_STREAM_TYPE; 1747 } 1748 mSoundNotification = r; 1749 // do not play notifications if stream volume is 0 (typically because 1750 // ringer mode is silent) or if there is a user of exclusive audio focus 1751 if ((mAudioManager.getStreamVolume(audioStreamType) != 0) 1752 && !mAudioManager.isAudioFocusExclusive()) { 1753 final long identity = Binder.clearCallingIdentity(); 1754 try { 1755 final IRingtonePlayer player = 1756 mAudioManager.getRingtonePlayer(); 1757 if (player != null) { 1758 if (DBG) Slog.v(TAG, "Playing sound " + soundUri 1759 + " on stream " + audioStreamType); 1760 player.playAsync(soundUri, user, looping, audioStreamType); 1761 } 1762 } catch (RemoteException e) { 1763 } finally { 1764 Binder.restoreCallingIdentity(identity); 1765 } 1766 } 1767 } 1768 1769 // vibrate 1770 // Does the notification want to specify its own vibration? 1771 final boolean hasCustomVibrate = notification.vibrate != null; 1772 1773 // new in 4.2: if there was supposed to be a sound and we're in vibrate 1774 // mode, and no other vibration is specified, we fall back to vibration 1775 final boolean convertSoundToVibration = 1776 !hasCustomVibrate 1777 && hasValidSound 1778 && (mAudioManager.getRingerMode() 1779 == AudioManager.RINGER_MODE_VIBRATE); 1780 1781 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback. 1782 final boolean useDefaultVibrate = 1783 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; 1784 1785 if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate) 1786 && !(mAudioManager.getRingerMode() 1787 == AudioManager.RINGER_MODE_SILENT)) { 1788 mVibrateNotification = r; 1789 1790 if (useDefaultVibrate || convertSoundToVibration) { 1791 // Escalate privileges so we can use the vibrator even if the 1792 // notifying app does not have the VIBRATE permission. 1793 long identity = Binder.clearCallingIdentity(); 1794 try { 1795 mVibrator.vibrate(r.sbn.getUid(), r.sbn.getOpPkg(), 1796 useDefaultVibrate ? mDefaultVibrationPattern 1797 : mFallbackVibrationPattern, 1798 ((notification.flags & Notification.FLAG_INSISTENT) != 0) 1799 ? 0: -1, notification.audioStreamType); 1800 } finally { 1801 Binder.restoreCallingIdentity(identity); 1802 } 1803 } else if (notification.vibrate.length > 1) { 1804 // If you want your own vibration pattern, you need the VIBRATE 1805 // permission 1806 mVibrator.vibrate(r.sbn.getUid(), r.sbn.getOpPkg(), 1807 notification.vibrate, 1808 ((notification.flags & Notification.FLAG_INSISTENT) != 0) 1809 ? 0: -1, notification.audioStreamType); 1810 } 1811 } 1812 } 1813 1814 // light 1815 // the most recent thing gets the light 1816 mLights.remove(old); 1817 if (mLedNotification == old) { 1818 mLedNotification = null; 1819 } 1820 //Slog.i(TAG, "notification.lights=" 1821 // + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) 1822 // != 0)); 1823 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 1824 && canInterrupt) { 1825 mLights.add(r); 1826 updateLightsLocked(); 1827 } else { 1828 if (old != null 1829 && ((old.getFlags() & Notification.FLAG_SHOW_LIGHTS) != 0)) { 1830 updateLightsLocked(); 1831 } 1832 } 1833 } 1834 } 1835 }); 1836 1837 idOut[0] = id; 1838 } 1839 1840 void showNextToastLocked() { 1841 ToastRecord record = mToastQueue.get(0); 1842 while (record != null) { 1843 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback); 1844 try { 1845 record.callback.show(); 1846 scheduleTimeoutLocked(record); 1847 return; 1848 } catch (RemoteException e) { 1849 Slog.w(TAG, "Object died trying to show notification " + record.callback 1850 + " in package " + record.pkg); 1851 // remove it from the list and let the process die 1852 int index = mToastQueue.indexOf(record); 1853 if (index >= 0) { 1854 mToastQueue.remove(index); 1855 } 1856 keepProcessAliveLocked(record.pid); 1857 if (mToastQueue.size() > 0) { 1858 record = mToastQueue.get(0); 1859 } else { 1860 record = null; 1861 } 1862 } 1863 } 1864 } 1865 1866 void cancelToastLocked(int index) { 1867 ToastRecord record = mToastQueue.get(index); 1868 try { 1869 record.callback.hide(); 1870 } catch (RemoteException e) { 1871 Slog.w(TAG, "Object died trying to hide notification " + record.callback 1872 + " in package " + record.pkg); 1873 // don't worry about this, we're about to remove it from 1874 // the list anyway 1875 } 1876 mToastQueue.remove(index); 1877 keepProcessAliveLocked(record.pid); 1878 if (mToastQueue.size() > 0) { 1879 // Show the next one. If the callback fails, this will remove 1880 // it from the list, so don't assume that the list hasn't changed 1881 // after this point. 1882 showNextToastLocked(); 1883 } 1884 } 1885 1886 private void scheduleTimeoutLocked(ToastRecord r) 1887 { 1888 mHandler.removeCallbacksAndMessages(r); 1889 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r); 1890 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY; 1891 mHandler.sendMessageDelayed(m, delay); 1892 } 1893 1894 private void handleTimeout(ToastRecord record) 1895 { 1896 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback); 1897 synchronized (mToastQueue) { 1898 int index = indexOfToastLocked(record.pkg, record.callback); 1899 if (index >= 0) { 1900 cancelToastLocked(index); 1901 } 1902 } 1903 } 1904 1905 // lock on mToastQueue 1906 int indexOfToastLocked(String pkg, ITransientNotification callback) 1907 { 1908 IBinder cbak = callback.asBinder(); 1909 ArrayList<ToastRecord> list = mToastQueue; 1910 int len = list.size(); 1911 for (int i=0; i<len; i++) { 1912 ToastRecord r = list.get(i); 1913 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) { 1914 return i; 1915 } 1916 } 1917 return -1; 1918 } 1919 1920 // lock on mToastQueue 1921 void keepProcessAliveLocked(int pid) 1922 { 1923 int toastCount = 0; // toasts from this pid 1924 ArrayList<ToastRecord> list = mToastQueue; 1925 int N = list.size(); 1926 for (int i=0; i<N; i++) { 1927 ToastRecord r = list.get(i); 1928 if (r.pid == pid) { 1929 toastCount++; 1930 } 1931 } 1932 try { 1933 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0); 1934 } catch (RemoteException e) { 1935 // Shouldn't happen. 1936 } 1937 } 1938 1939 private final class WorkerHandler extends Handler 1940 { 1941 @Override 1942 public void handleMessage(Message msg) 1943 { 1944 switch (msg.what) 1945 { 1946 case MESSAGE_TIMEOUT: 1947 handleTimeout((ToastRecord)msg.obj); 1948 break; 1949 case MESSAGE_SAVE_POLICY_FILE: 1950 handleSavePolicyFile(); 1951 break; 1952 } 1953 } 1954 } 1955 1956 1957 // Notifications 1958 // ============================================================================ 1959 static int clamp(int x, int low, int high) { 1960 return (x < low) ? low : ((x > high) ? high : x); 1961 } 1962 1963 void sendAccessibilityEvent(Notification notification, CharSequence packageName) { 1964 AccessibilityManager manager = AccessibilityManager.getInstance(getContext()); 1965 if (!manager.isEnabled()) { 1966 return; 1967 } 1968 1969 AccessibilityEvent event = 1970 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); 1971 event.setPackageName(packageName); 1972 event.setClassName(Notification.class.getName()); 1973 event.setParcelableData(notification); 1974 CharSequence tickerText = notification.tickerText; 1975 if (!TextUtils.isEmpty(tickerText)) { 1976 event.getText().add(tickerText); 1977 } 1978 1979 manager.sendAccessibilityEvent(event); 1980 } 1981 1982 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) { 1983 // tell the app 1984 if (sendDelete) { 1985 if (r.getNotification().deleteIntent != null) { 1986 try { 1987 r.getNotification().deleteIntent.send(); 1988 } catch (PendingIntent.CanceledException ex) { 1989 // do nothing - there's no relevant way to recover, and 1990 // no reason to let this propagate 1991 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex); 1992 } 1993 } 1994 } 1995 1996 // status bar 1997 if (r.getNotification().icon != 0) { 1998 final long identity = Binder.clearCallingIdentity(); 1999 try { 2000 mStatusBar.removeNotification(r.statusBarKey); 2001 } finally { 2002 Binder.restoreCallingIdentity(identity); 2003 } 2004 r.statusBarKey = null; 2005 mListeners.notifyRemovedLocked(r.sbn); 2006 } 2007 2008 // sound 2009 if (mSoundNotification == r) { 2010 mSoundNotification = null; 2011 final long identity = Binder.clearCallingIdentity(); 2012 try { 2013 final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); 2014 if (player != null) { 2015 player.stopAsync(); 2016 } 2017 } catch (RemoteException e) { 2018 } finally { 2019 Binder.restoreCallingIdentity(identity); 2020 } 2021 } 2022 2023 // vibrate 2024 if (mVibrateNotification == r) { 2025 mVibrateNotification = null; 2026 long identity = Binder.clearCallingIdentity(); 2027 try { 2028 mVibrator.cancel(); 2029 } 2030 finally { 2031 Binder.restoreCallingIdentity(identity); 2032 } 2033 } 2034 2035 // light 2036 mLights.remove(r); 2037 if (mLedNotification == r) { 2038 mLedNotification = null; 2039 } 2040 2041 // Record usage stats 2042 switch (reason) { 2043 case REASON_DELEGATE_CANCEL: 2044 case REASON_DELEGATE_CANCEL_ALL: 2045 case REASON_LISTENER_CANCEL: 2046 case REASON_LISTENER_CANCEL_ALL: 2047 mUsageStats.registerDismissedByUser(r); 2048 break; 2049 case REASON_NOMAN_CANCEL: 2050 case REASON_NOMAN_CANCEL_ALL: 2051 mUsageStats.registerRemovedByApp(r); 2052 break; 2053 case REASON_DELEGATE_CLICK: 2054 mUsageStats.registerCancelDueToClick(r); 2055 break; 2056 default: 2057 mUsageStats.registerCancelUnknown(r); 2058 break; 2059 } 2060 2061 // Save it for users of getHistoricalNotifications() 2062 mArchive.record(r.sbn); 2063 } 2064 2065 /** 2066 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags} 2067 * and none of the {@code mustNotHaveFlags}. 2068 */ 2069 void cancelNotification(final int callingUid, final int callingPid, 2070 final String pkg, final String tag, final int id, 2071 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete, 2072 final int userId, final int reason, final ManagedServiceInfo listener) { 2073 // In enqueueNotificationInternal notifications are added by scheduling the 2074 // work on the worker handler. Hence, we also schedule the cancel on this 2075 // handler to avoid a scenario where an add notification call followed by a 2076 // remove notification call ends up in not removing the notification. 2077 mHandler.post(new Runnable() { 2078 @Override 2079 public void run() { 2080 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId, 2081 mustHaveFlags, mustNotHaveFlags, reason, 2082 listener == null ? null : listener.component.toShortString()); 2083 2084 synchronized (mNotificationList) { 2085 int index = indexOfNotificationLocked(pkg, tag, id, userId); 2086 if (index >= 0) { 2087 NotificationRecord r = mNotificationList.get(index); 2088 2089 // Ideally we'd do this in the caller of this method. However, that would 2090 // require the caller to also find the notification. 2091 if (reason == REASON_DELEGATE_CLICK) { 2092 mUsageStats.registerClickedByUser(r); 2093 } 2094 2095 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) { 2096 return; 2097 } 2098 if ((r.getNotification().flags & mustNotHaveFlags) != 0) { 2099 return; 2100 } 2101 2102 mNotificationList.remove(index); 2103 mNotificationsByKey.remove(r.sbn.getKey()); 2104 2105 cancelNotificationLocked(r, sendDelete, reason); 2106 updateLightsLocked(); 2107 } 2108 } 2109 } 2110 }); 2111 } 2112 2113 /** 2114 * Determine whether the userId applies to the notification in question, either because 2115 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard). 2116 */ 2117 private boolean notificationMatchesUserId(NotificationRecord r, int userId) { 2118 return 2119 // looking for USER_ALL notifications? match everything 2120 userId == UserHandle.USER_ALL 2121 // a notification sent to USER_ALL matches any query 2122 || r.getUserId() == UserHandle.USER_ALL 2123 // an exact user match 2124 || r.getUserId() == userId; 2125 } 2126 2127 /** 2128 * Determine whether the userId applies to the notification in question, either because 2129 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or 2130 * because it matches one of the users profiles. 2131 */ 2132 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) { 2133 return notificationMatchesUserId(r, userId) 2134 || mUserProfiles.isCurrentProfile(r.getUserId()); 2135 } 2136 2137 /** 2138 * Cancels all notifications from a given package that have all of the 2139 * {@code mustHaveFlags}. 2140 */ 2141 boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags, 2142 int mustNotHaveFlags, boolean doit, int userId, int reason, 2143 ManagedServiceInfo listener) { 2144 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2145 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason, 2146 listener == null ? null : listener.component.toShortString()); 2147 2148 synchronized (mNotificationList) { 2149 final int N = mNotificationList.size(); 2150 boolean canceledSomething = false; 2151 for (int i = N-1; i >= 0; --i) { 2152 NotificationRecord r = mNotificationList.get(i); 2153 if (!notificationMatchesUserId(r, userId)) { 2154 continue; 2155 } 2156 // Don't remove notifications to all, if there's no package name specified 2157 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) { 2158 continue; 2159 } 2160 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) { 2161 continue; 2162 } 2163 if ((r.getFlags() & mustNotHaveFlags) != 0) { 2164 continue; 2165 } 2166 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) { 2167 continue; 2168 } 2169 canceledSomething = true; 2170 if (!doit) { 2171 return true; 2172 } 2173 mNotificationList.remove(i); 2174 mNotificationsByKey.remove(r.sbn.getKey()); 2175 cancelNotificationLocked(r, false, reason); 2176 } 2177 if (canceledSomething) { 2178 updateLightsLocked(); 2179 } 2180 return canceledSomething; 2181 } 2182 } 2183 2184 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason, 2185 ManagedServiceInfo listener, boolean includeCurrentProfiles) { 2186 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2187 null, userId, 0, 0, reason, 2188 listener == null ? null : listener.component.toShortString()); 2189 2190 final int N = mNotificationList.size(); 2191 for (int i=N-1; i>=0; i--) { 2192 NotificationRecord r = mNotificationList.get(i); 2193 if (includeCurrentProfiles) { 2194 if (!notificationMatchesCurrentProfiles(r, userId)) { 2195 continue; 2196 } 2197 } else { 2198 if (!notificationMatchesUserId(r, userId)) { 2199 continue; 2200 } 2201 } 2202 2203 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT 2204 | Notification.FLAG_NO_CLEAR)) == 0) { 2205 mNotificationList.remove(i); 2206 mNotificationsByKey.remove(r.sbn.getKey()); 2207 cancelNotificationLocked(r, true, reason); 2208 } 2209 } 2210 updateLightsLocked(); 2211 } 2212 2213 // lock on mNotificationList 2214 void updateLightsLocked() 2215 { 2216 // handle notification lights 2217 if (mLedNotification == null) { 2218 // get next notification, if any 2219 int n = mLights.size(); 2220 if (n > 0) { 2221 mLedNotification = mLights.get(n-1); 2222 } 2223 } 2224 2225 // Don't flash while we are in a call or screen is on 2226 if (mLedNotification == null || mInCall || mScreenOn) { 2227 mNotificationLight.turnOff(); 2228 } else { 2229 final Notification ledno = mLedNotification.sbn.getNotification(); 2230 int ledARGB = ledno.ledARGB; 2231 int ledOnMS = ledno.ledOnMS; 2232 int ledOffMS = ledno.ledOffMS; 2233 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) { 2234 ledARGB = mDefaultNotificationColor; 2235 ledOnMS = mDefaultNotificationLedOn; 2236 ledOffMS = mDefaultNotificationLedOff; 2237 } 2238 if (mNotificationPulseEnabled) { 2239 // pulse repeatedly 2240 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED, 2241 ledOnMS, ledOffMS); 2242 } 2243 } 2244 } 2245 2246 // lock on mNotificationList 2247 int indexOfNotificationLocked(String pkg, String tag, int id, int userId) 2248 { 2249 ArrayList<NotificationRecord> list = mNotificationList; 2250 final int len = list.size(); 2251 for (int i=0; i<len; i++) { 2252 NotificationRecord r = list.get(i); 2253 if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) { 2254 continue; 2255 } 2256 if (tag == null) { 2257 if (r.sbn.getTag() != null) { 2258 continue; 2259 } 2260 } else { 2261 if (!tag.equals(r.sbn.getTag())) { 2262 continue; 2263 } 2264 } 2265 if (r.sbn.getPackageName().equals(pkg)) { 2266 return i; 2267 } 2268 } 2269 return -1; 2270 } 2271 2272 private void updateNotificationPulse() { 2273 synchronized (mNotificationList) { 2274 updateLightsLocked(); 2275 } 2276 } 2277 2278 private static boolean isUidSystem(int uid) { 2279 final int appid = UserHandle.getAppId(uid); 2280 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0); 2281 } 2282 2283 private static boolean isCallerSystem() { 2284 return isUidSystem(Binder.getCallingUid()); 2285 } 2286 2287 private static void checkCallerIsSystem() { 2288 if (isCallerSystem()) { 2289 return; 2290 } 2291 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid()); 2292 } 2293 2294 private static void checkCallerIsSystemOrSameApp(String pkg) { 2295 if (isCallerSystem()) { 2296 return; 2297 } 2298 final int uid = Binder.getCallingUid(); 2299 try { 2300 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo( 2301 pkg, 0, UserHandle.getCallingUserId()); 2302 if (!UserHandle.isSameApp(ai.uid, uid)) { 2303 throw new SecurityException("Calling uid " + uid + " gave package" 2304 + pkg + " which is owned by uid " + ai.uid); 2305 } 2306 } catch (RemoteException re) { 2307 throw new SecurityException("Unknown package " + pkg + "\n" + re); 2308 } 2309 } 2310 2311 public class NotificationListeners extends ManagedServices { 2312 2313 public NotificationListeners() { 2314 super(getContext(), mHandler, mNotificationList, mUserProfiles); 2315 } 2316 2317 @Override 2318 protected Config getConfig() { 2319 Config c = new Config(); 2320 c.caption = "notification listener"; 2321 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE; 2322 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS; 2323 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE; 2324 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS; 2325 c.clientLabel = R.string.notification_listener_binding_label; 2326 return c; 2327 } 2328 2329 @Override 2330 protected IInterface asInterface(IBinder binder) { 2331 return INotificationListener.Stub.asInterface(binder); 2332 } 2333 2334 @Override 2335 public void onServiceAdded(ManagedServiceInfo info) { 2336 final INotificationListener listener = (INotificationListener) info.service; 2337 final String[] keys = getActiveNotificationKeysFromListener(listener); 2338 try { 2339 listener.onListenerConnected(keys); 2340 } catch (RemoteException e) { 2341 // we tried 2342 } 2343 } 2344 2345 /** 2346 * asynchronously notify all listeners about a new notification 2347 */ 2348 public void notifyPostedLocked(StatusBarNotification sbn) { 2349 // make a copy in case changes are made to the underlying Notification object 2350 final StatusBarNotification sbnClone = sbn.clone(); 2351 for (final ManagedServiceInfo info : mServices) { 2352 mHandler.post(new Runnable() { 2353 @Override 2354 public void run() { 2355 notifyPostedIfUserMatch(info, sbnClone); 2356 } 2357 }); 2358 } 2359 } 2360 2361 /** 2362 * asynchronously notify all listeners about a removed notification 2363 */ 2364 public void notifyRemovedLocked(StatusBarNotification sbn) { 2365 // make a copy in case changes are made to the underlying Notification object 2366 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the 2367 // notification 2368 final StatusBarNotification sbnLight = sbn.cloneLight(); 2369 for (ManagedServiceInfo serviceInfo : mServices) { 2370 final ManagedServiceInfo info = (ManagedServiceInfo) serviceInfo; 2371 mHandler.post(new Runnable() { 2372 @Override 2373 public void run() { 2374 notifyRemovedIfUserMatch(info, sbnLight); 2375 } 2376 }); 2377 } 2378 } 2379 2380 private void notifyPostedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn) { 2381 if (!info.enabledAndUserMatches(sbn.getUserId())) { 2382 return; 2383 } 2384 final INotificationListener listener = (INotificationListener)info.service; 2385 try { 2386 listener.onNotificationPosted(sbn); 2387 } catch (RemoteException ex) { 2388 Log.e(TAG, "unable to notify listener (posted): " + listener, ex); 2389 } 2390 } 2391 2392 private void notifyRemovedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn) { 2393 if (!info.enabledAndUserMatches(sbn.getUserId())) { 2394 return; 2395 } 2396 final INotificationListener listener = (INotificationListener)info.service; 2397 try { 2398 listener.onNotificationRemoved(sbn); 2399 } catch (RemoteException ex) { 2400 Log.e(TAG, "unable to notify listener (removed): " + listener, ex); 2401 } 2402 } 2403 } 2404} 2405